Blog

CMake Before Coffee

This morning I changed one header and watched the old build machinery reconsider what appeared to be western civilisation. That was enough encouragement to try the same small library with CMake.

The useful bit was not clever syntax. It was stating the target directly:

set(parser_SRCS parser.cpp token.cpp)
kde4_add_library(parser ${parser_SRCS})
target_link_libraries(parser ${QT_QTCORE_LIBRARY})
install(TARGETS parser DESTINATION ${LIB_INSTALL_DIR})

I configured outside the source tree:

mkdir build
cd build
cmake ..
make VERBOSE=1

That last command matters while converting. The generated command line tells me which include path, definition, or library I forgot. Guessing from a linker error is possible, but so is repairing a watch with a spoon.

I prefer one explicit target with its own sources and libraries over directory-wide flags. It makes dependencies visible and avoids accidentally linking every library against everything else. The caveat is that our CMake helper macros are still moving, so a tidy file today may need adjustment next week.

My practical rule is to convert one buildable directory at a time, compile it, then continue. A giant mechanical conversion produces a giant pile of errors with no useful ordering. Small green steps are less heroic and considerably faster.

The Timer That Froze the Window

I found today’s problem by dragging a window. The repaint stopped, the title bar sulked, and a progress label remained at 12 percent while the application did several seconds of perfectly respectable work.

The work was running from a QTimer slot. I had assumed that using a timer made it asynchronous. It does not. A timer merely arranges for a callback to be delivered by the event loop; the callback still runs on that loop’s thread. If the slot computes for five seconds, paint events wait five seconds. So do mouse events and, more importantly, the user’s patience.

The quick diagnostic was wonderfully primitive:

void Scanner::scanNext()
{
    qDebug() << "enter scanNext";
    scanEverything();
    qDebug() << "leave scanNext";
}

The pause sat between those messages. Splitting the operation into bounded pieces fixed the immediate problem:

void Scanner::scanNext()
{
    for (int i = 0; i != 50 && hasMore(); ++i)
        scanOne();

    if (hasMore())
        QTimer::singleShot(0, this, SLOT(scanNext()));
}

A zero-duration timer does not promise instant execution. It puts more work back into event processing, allowing pending paints and input to run between batches. The batch size is deliberately boring: small enough to keep the interface alive, large enough not to drown in scheduling overhead.

I prefer this incremental approach when the job naturally divides into independent records. It keeps all GUI objects on their owning thread and makes cancellation straightforward. The caveat is that a single expensive record still blocks everything. In that case the computation belongs on a worker thread, with results sent back rather than widgets touched directly.

Calling QCoreApplication::processEvents() inside the loop is tempting, but it permits other callbacks to enter while the current operation is unfinished. That can expose partially updated state or let the user start the same action twice. Returning to the event loop between explicit batches gives me a cleaner boundary: each batch leaves the object consistent before yielding.

That boundary also gives cancellation a predictable checkpoint between records, rather than during an arbitrary mutation.

Qt 4’s event loop is not magic concurrency dust. This is disappointing only until one remembers that magic concurrency dust would probably deadlock before breakfast.

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.