Kde4

HAL Is a Source, Not a Policy

I plugged in a USB disk this afternoon and my test application offered to open it twice. Unplugging it removed only one entry. This is an efficient way to make hardware discovery look supernatural.

The bug was mundane: startup enumeration and hotplug notification both created an object, while neither path checked whether the same device identifier was already present. HAL supplies facts about devices and changes, but it does not decide how my application should represent them.

I now funnel both paths through one function:

void DeviceList::addDevice(const QString &udi)
{
    if (devices.contains(udi))
        return;

    Device device = backend->device(udi);
    if (!device.isValid())
        return;

    devices.insert(udi, device);
    emit deviceAdded(udi);
}

Removal uses the same identifier and emits only after the internal state is consistent. That ordering lets slots query the list without observing a ghost.

The larger KDE 4 question is where HAL ends and a hardware abstraction such as Solid begins. I do not want every application parsing HAL properties or knowing which backend discovered a battery. Applications should ask stable questions: is this a storage volume, can it be mounted, is this network interface active? Solid can translate those questions to the platform mechanism.

I prefer keeping the backend thin and putting application policy above Solid. A media player may automatically inspect an audio disc; a file manager may offer actions; a backup tool may ignore both and look only for a labelled volume. The same hardware event should not imply the same decision.

For testing, the backend now accepts a recorded sequence of add and remove events. Replaying “add, add, remove” verifies the duplicate guard without requiring me to develop exceptional timing with a USB plug. It also makes event ordering assumptions visible in a small test rather than in a user’s device list.

The caveat is that abstraction can hide information we have not yet learned to model. During early porting I am logging the raw UDI and selected properties beside the abstract device type. When a device is classified incorrectly, that evidence is valuable. I would remove such noise from a normal release, assuming we eventually have one that does not greet a single disk as twins.

Model/View Without the Fog

I triggered a crash today by deleting the third row of a list. The list had only two visible rows, which was the first clue that my idea of the data and the view’s idea had stopped speaking to each other.

The old code filled a widget item by item and kept pointers to those items in the document. Qt 4’s model/view classes ask for a cleaner bargain: the model owns the shape and meaning of the data, while views ask questions through QModelIndex. That index is a coordinate plus model identity, not the object itself.

For a flat list, the smallest useful model is not large:

class ContactModel : public QAbstractListModel
{
public:
    ContactModel(QObject *parent = 0)
        : QAbstractListModel(parent)
    {
    }

    int rowCount(const QModelIndex &parent = QModelIndex()) const
    {
        return parent.isValid() ? 0 : contacts.count();
    }

    QVariant data(const QModelIndex &index, int role) const
    {
        if (!index.isValid() || index.model() != this ||
            index.row() < 0 || index.row() >= contacts.count() ||
            index.column() != 0)
            return QVariant();

        if (role == Qt::DisplayRole)
            return contacts.at(index.row()).name;

        return QVariant();
    }

private:
    QList<Contact> contacts;
};

The parent check is important even for a list. A valid parent asks for children beneath an item; this model has none. Returning the full count there can make a view believe it has discovered a tree of infinite enthusiasm.

The more interesting part is changing data. The view cannot infer that a row appeared because a QList grew. The model must announce structural changes around the actual mutation:

void ContactModel::append(const Contact &contact)
{
    int row = contacts.count();
    beginInsertRows(QModelIndex(), row, row);
    contacts.append(contact);
    endInsertRows();
}

Those calls let attached views adjust selections, editors, and cached geometry coherently. For an ordinary value change, I modify the value and emit dataChanged(index, index) instead. Resetting the whole model after every edit appears easier, but it discards selection and makes modest updates expensive.

My crash came from removal code that changed the collection first and emitted signals afterwards. During that gap, a view requested an index using the old shape against the new data. Correct order is not decorative:

void ContactModel::remove(int row)
{
    if (row < 0 || row >= contacts.count())
        return;

    beginRemoveRows(QModelIndex(), row, row);
    contacts.removeAt(row);
    endRemoveRows();
}

Selections add another wrinkle. A normal QModelIndex is intended for immediate use. If I must retain an index while rows can move, I use QPersistentModelIndex, and even then I ask whether storing a domain identifier would be clearer. Persistent indexes survive model notifications, not arbitrary lies told by the model.

Sorting and filtering belong in a proxy when possible. QSortFilterProxyModel sits between source and view, translating indexes in both directions. That means code receiving a selected proxy index must map it before addressing source data:

QModelIndex sourceIndex = proxy->mapToSource(view->currentIndex());
Contact contact = model->contactAt(sourceIndex.row());

Forgetting this works beautifully until sorting changes the order. These are the bugs that wait politely through a demonstration and introduce themselves to the first user.

I prefer models that expose domain data through roles and operations, with widgets knowing as little as possible about storage. One model can then drive a list, a combo box, and perhaps a custom view without three synchronisation routines. The caveat is that model/view has real ceremony. A fixed list of five labels does not need a heroic subclass; QStringListModel may be enough.

Editable models need one more honest contract. flags() must advertise editable indexes, and setData() must validate the role and value before changing storage. Returning true means the edit happened, not merely that the method received a pleasant visit:

bool ContactModel::setData(const QModelIndex &index,
                           const QVariant &value, int role)
{
    if (role != Qt::EditRole || !index.isValid() ||
        index.model() != this || index.row() < 0 ||
        index.row() >= contacts.count() || index.column() != 0)
        return false;

    contacts[index.row()].name = value.toString();
    emit dataChanged(index, index);
    return true;
}

The model and bounds checks matter here too. An index from another model is not permission to address this model’s storage, even when its row happens to fit. The delegate asks the model to commit; it does not gain ownership of the underlying record.

There is also a testing advantage. I can exercise rowCount, data, insertion, and removal with QCoreApplication, no screen required. My current test records the emitted row ranges and checks the resulting names. It is not glamorous, but neither is debugging a stale selection at midnight.

Qt 4’s mechanism got less foggy when I stopped treating the model as a collection adapter. The view trusts its signals and invariants. Once I stopped lying to it, the disappearing third row quit crashing, though it remains philosophically ambitious.

Porting the Seam, Not the Screen

I started porting a small KDE utility today and made the predictable mistake: I opened the main window source first. Ten minutes later I had changed several class names and learned almost nothing about whether the program still worked.

The useful work began when I backed up and found the seams. This program has a parser, a document object, a job that reads files, and a window that displays results. Only the last part truly cares about widgets. I built the parser and document as a small Qt 4 library before touching the screen.

The immediate payoff was a command-line test:

int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);

    if (argc != 2) {
        qCritical() << "usage:" << argv[0] << "file";
        return 1;
    }

    Document document;
    QString error;
    if (!document.load(QString::fromLocal8Bit(argv[1]), &error)) {
        qCritical() << error;
        return 1;
    }

    qDebug() << document.entries().count();
    return 0;
}

That exposed two assumptions hidden by the old interface. One parser path depended on a widget-created codec, and file loading reported errors by opening a message box. Neither belongs in the core. Loading now returns a status and an error string; the caller decides how to present it.

This matters because KDE 4 is architecture and porting work at the moment, not a desktop I would hand to an unsuspecting relative. APIs and libraries are being separated so applications can use services without importing a sack of unrelated GUI machinery. A compiling window is pleasant evidence, but it is not proof of a sound port.

Porting from the non-GUI core outward gives me executable checkpoints and makes ownership clearer. Old applications sometimes mix policy, storage, and widgets so thoroughly that extracting a core costs more than a direct compile fix. For a tiny or soon-to-be-replaced tool, the ugly route may be honest.

One more practical trick: make every compatibility warning visible during the port. I am using verbose builds and keeping a short list of intentionally deferred warnings. If the list grows without explanation, it is not a list; it is a compost heap.

I also run the command-line test from a clean build tree, not only from the source directory. That catches accidental dependence on local files before the GUI can hide it.

Tomorrow I will return to the main window. It should now have less to do, which is my favourite feature in a window class.

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.