Linux

What the Desktop Taught Me This Year

I spent much of this year fixing things that I first tried to solve at the wrong layer. A missing button event sent me into desktop settings. A wandering disk name produced a worse shell script. A driver race invited more logging. My instincts have been very energetic, if not consistently useful.

The better method is to follow the mechanism. Kernel devices appear through the device model and sysfs; udev applies naming policy. Input travels from the kernel through X.Org to applications. Qt objects have explicit parent ownership. Deferred kernel work has context and lifetime rules. Git history is a graph of objects, not a remote folder with ceremony.

KDE 3.5 ties the user-facing side together with unusual steadiness. Its maturity is not one clever feature but the accumulated reliability of applications, libraries, and conventions.

My practical conclusion for the year is to identify the boundary before editing the configuration or code. Ask what layer owns the decision, what evidence crosses that boundary, and what lifetime the data has. Then make one change and test it.

This sounds obvious when written in December. In February it apparently required an oops. Education remains committed to memorable examples.

KDE 3.5 and the Value of Finish

KDE 3.5 has been released, and I upgraded the main workstation after using development builds on another machine. The practical question was not whether the version number justified celebration. It was whether I could move my working session without spending the evening negotiating with it.

My naive plan was to keep every cached file and preference because testing had gone well elsewhere. Experience intervened. I backed up the configuration, upgraded the packages, and started with the existing account, but kept a clean account ready as a control. That made it possible to distinguish release problems from my personal museum of settings.

The existing account worked with only a small amount of cleanup. More importantly, the desktop preserved habits established across the 3.x series. Konsole, Konqueror, the panel, and common dialogs did not demand a new operating philosophy. Applications still felt related through shared shortcuts, appearance, and services.

This is the benefit of finishing a line of software rather than constantly replacing its foundation. KDE 3.5 contains visible improvements, but its strongest feature is accumulated correction. Years of reports have covered odd displays, unusual input setups, stale sessions, broken files, and people who click buttons in an order no designer predicted.

Maturity does not mean absence of bugs. It means the common paths are dependable, failure is more localized, and the model has become familiar enough that diagnosis starts from evidence rather than folklore. When an application misbehaves, I can usually decide whether to inspect its configuration, KDE output, X.Org, or the kernel instead of rebooting the entire stack ceremonially.

For C++ desktop development, the platform also feels coherent. Qt 3.3 and KDE libraries offer established patterns for ownership, events, actions, and user interface conventions. Consistency reduces application code and user surprise at the same time, which is a rare two-for-one bargain.

There is still excess. Some menus and control panels would benefit from deciding which choices matter most. Flexibility is admirable, but every checkbox is also a tiny maintenance promise.

My verdict is that KDE 3.5 demonstrates the value of patient integration. It does not need to astonish me each morning. It needs to restore my terminals, open my files, and stay predictable while I break something much more interesting in kernel code. On that measure, this release is in very good shape.

Testing KDE 3.5 With Real Work

I’ve been testing KDE 3.5’s KHTML changes ahead of the final release. Passing a standards test is good news, but I wanted to know whether the same engine still handled the untidy pages I use for work.

I started with a small directory of local pages: nested floats, a wide preformatted block, a form inside a table, transparent PNGs, and one page with deliberately broken markup. Each isolates a behavior well enough that a rendering change isn’t just “this site looks odd.”

Then I used Konqueror on documentation and project sites with style sheets enabled, disabled, and replaced by a tiny user sheet. Keyboard focus still had to move through links and form controls in a sensible order. Better CSS support isn’t much help if a search field becomes unreachable without a mouse.

One layout did regress. Instead of filing the full page, I removed blocks until only a float, a cleared heading, and six lines of CSS remained. That reduction changed the report from a screenshot of somebody else’s site into a local file that showed the bug every time.

I kept the exact revision and compared the reduced page with the previous build. “Konqueror acted funny” is emotionally accurate and technically ornamental; a twelve-line attachment with a before-and-after result is useful.

KHTML’s shared role raises the stakes. A rendering fix reaches Konqueror and any application embedding the part, while a regression travels just as efficiently. Small local pages make that shared behavior testable without depending on a live site changing underneath the report.

The standards progress in 3.5 is visible, especially on layouts that used to need browser-specific nudges. The useful release behavior isn’t the badge, though; it’s rendering those pages more correctly without breaking forms, focus, and rough old HTML.

My little page collection now lives beside the build scripts. It’s less glamorous than a browser shootout, but much better at explaining the next crooked heading.

Debugging the Desktop From evdev to X.Org

An extra mouse button worked in one application but not another, which sent me directly to the KDE control center. I changed mappings until ordinary buttons became surprising too. This was useful only as a demonstration that random adjustments scale badly.

I started over at the bottom of the stack. The kernel input layer exposes event devices and reports key, button, and relative motion events. X.Org opens an input device through its configured driver, translates those events into the X input model, and sends them to clients. KDE and its applications then apply shortcuts or application behavior.

That sequence gives each test a specific question. Kernel event diagnostics answer whether hardware input reaches the kernel. The X.Org log answers which device and driver the server opened. xev answers which X events a client receives. An application test answers what that client does with them.

The button appeared at the kernel layer but was mapped unexpectedly by X.Org. I had copied a configuration stanza from a different mouse, including a button mapping whose numbers reflected that device. Removing the borrowed assumption and mapping the observed buttons deliberately fixed the problem.

The exercise also exposed a trap in logs. A warning near the end of Xorg.0.log looked relevant because it mentioned input, but the earlier device initialization lines showed that it belonged to another configured device. Timestamps, identifiers, and full initialization sections matter more than a conveniently alarming word.

I saved a small checklist with the configuration:

1. Does the kernel report the event?
2. Which device does X.Org open?
3. What does xev receive?
4. How does the application interpret it?

This is not sophisticated debugging, and that is why it works. Each layer has a narrow contract. The first failed contract determines where to investigate; layers above it cannot repair missing information.

Desktop Linux can feel complicated because these boundaries are visible. I prefer visible complexity to one giant mystery, but only when I respect the boundaries. Starting at the application and changing every layer on the way down is not diagnosis. It is redecorating the staircase while looking for a leak.

Replacing the Directory Poll With inotify

Now that Linux 2.6.13 includes inotify, I replaced a timer in a small file-watching utility. The timer scanned a directory every second, which was simple, portable, and wasteful. It also introduced the charming choice between delayed updates and more frequent useless work.

My first inotify version assumed each read() returned one complete event. It worked during gentle testing and became nonsense when several files changed quickly. The interface returns a byte stream containing one or more variable-length struct inotify_event records. Correct code walks the buffer by each fixed header plus its len field.

union {
        struct inotify_event event;
        char bytes[4096];
} buffer;
ssize_t count;
size_t offset = 0;

do {
        count = read(fd, buffer.bytes, sizeof(buffer.bytes));
} while (count < 0 && errno == EINTR);

if (count < 0) {
        perror("read");
        exit(EXIT_FAILURE);
}
if (count == 0) {
        fprintf(stderr, "inotify descriptor reached EOF\n");
        exit(EXIT_FAILURE);
}

while (offset < (size_t)count) {
        struct inotify_event *event;
        size_t event_size;

        if ((size_t)count - offset < sizeof(*event)) {
                fprintf(stderr, "short inotify event header\n");
                exit(EXIT_FAILURE);
        }
        event = (struct inotify_event *)(buffer.bytes + offset);
        event_size = sizeof(*event) + event->len;
        if (event_size > (size_t)count - offset) {
                fprintf(stderr, "short inotify event name\n");
                exit(EXIT_FAILURE);
        }
        handle_event(event);
        offset += event_size;
}

The union gives the byte storage suitable alignment for struct inotify_event; a plain character array doesn’t promise that. The code also deals with interruption, read failure, EOF, and truncated records before treating bytes as an event. A watch identifies a pathname to the kernel, and events report changes associated with a watch descriptor.

The largest conceptual correction was learning that notifications are hints about state changes, not a private journal guaranteed to preserve my worldview. Events can arrive in bursts. A queue can overflow. A watched object can move or disappear. On overflow, the safe response is to rescan and rebuild known state rather than guess what was missed.

Renames deserve similar care. Related move events carry a cookie that can help pair them, but the application still needs sensible behavior when only one side is visible. Watching a directory also does not automatically mean recursively watching every directory below it.

I kept one function that performs a complete scan and made event handling update the same internal model. That gives the program a recovery path and keeps startup behavior consistent with overflow recovery. The event loop is an optimization over known state, not the only source of truth.

Compared with polling, inotify is immediate and avoids repeated directory walks when nothing changes. It also exposes more edge cases because the kernel reports actual operations rather than letting a periodic snapshot blur them together.

I prefer the new mechanism, with one reservation: event-driven doesn’t mean infallible. Read complete buffers, parse every record, expect overflow, and keep a rescan path. The kernel says something happened; deciding what the application should now believe is still our job.