Skip to content

Thursday, 10 September 2026

Over 180 individual programs plus dozens of programmer libraries and feature plugins are released simultaneously as part of KDE Gear.

Today they all get new bugfix source releases with updated translations, including:

  • kdeconnect: Fix orientation of an arrow within the plasmoid (Commit, fixes bug #524889)
  • kongress: Fix opening the room map from a talk (Commit)
  • okular: Fix a crash when saving documents (Commit, fixes bugs #477153 and #505130)

Distro and app store packagers should update their application packages.

Wednesday, 9 September 2026

Wednesday, 9 September 2026

KDE today announces the release of KDE Frameworks 6.30.0.

This release is part of a series of planned monthly releases making improvements available to developers in a quick and predictable manner.

New in this version

Baloo
  • Apply clang-format on fileexcludefilters.cpp. Commit.
  • Exclude .snapshots folders from indexing. Commit.
  • [app] Skip content indexing in the extractor if onlyBasicIndexing is set. Commit. See bug #500665
Bluez Qt
  • Port away from ecm_add_qml_module's deprecated CLASSNAME arg. Commit.
Breeze Icons
  • Set missing fill="currentColor" for update-busy. Commit.
  • Add "tv" symlink to "video-television". Commit.
  • Add symlinks for text-x-shellscript. Commit.
  • Add symbolic mastodon icon. Commit.
Extra CMake Modules
  • List ECM sources explicitly instead of using GLOBS. Commit.
  • ECMInstalledLibraryCheck: add more separating linebreaks to generated files. Commit.
  • ExecuteKDEModules: add newer KDE modules to test. Commit.
  • ExecuteKDEModules: split off support for apple platform, document it. Commit.
  • ECMInstalledLibraryCheck: use $cond:string over $IF:cond,string,. Commit.
  • Add ECMInstalledLibraryCheck. Commit.
  • Ecm_generate_export_header: make VERSION optional, default to PROJECT_VERSION. Commit.
  • ECMSetupVersion: support version args in . and forms. Commit.
  • ECMSetupVersion: use final hex number in version header, not calculation. Commit.
KCalendarCore
  • Use QLocale for concatenating recurrence days. Commit.
  • Improve translation contexts for some of the recurrence descriptions. Commit.
  • Src/vcalformat.cpp - fix spelling typo in a comment. Commit.
  • Adapt recurrenceDescription unit tests to English translations. Commit.
  • Add Incidence::recurrenceDescription. Commit.
  • Fix Incidence::statusName property name. Commit.
  • Add translated enum names for Incidence::secrecy and Incidence::status. Commit.
  • Change parseScheduleMessage to take a QByteArray as input. Commit.
  • Add translated names for attendee status and role enums. Commit.
  • Fix tr() message context. Commit.
  • Expose Calendar and ScheduleMessage to python bindings. Commit.
  • Add translated error messages. Commit.
KCMUtils
  • Documentation fixes. Commit.
  • Fix documentation for SettingsStateBinding. Commit.
  • Fix docs for KQuickManagedConfigModule. Commit.
  • Kcmutils_generate_module_data: add missing var init, for safer usage. Commit.
  • Add missing EXCLUDE_DEPRECATED_BEFORE_AND_AT default variable declaration. Commit.
  • No longer explicitly include CMakeParseArguments. Commit.
  • Fix types in QML documentation. Commit.
KCodecs
  • [KEncodingProber] Fix signedness issues for mBestGuess index. Commit.
  • [KEncodingProber] Return 0.0 confidence if encoding signals wrong syntax. Commit.
  • [KEncodingProber] Clean up float constants and C-style static casts. Commit.
  • [KEncodingProber] Drop charlen tables from state machine models. Commit.
  • [KEncodingProber] Remove need for charlen table from MBCS probers. Commit.
  • [KEncodingProber] Remove some erroneously copied comment. Commit.
  • [KEncodingProber] Remove no longer used method. Commit.
  • [KEncodingProber] SBCS: Replace model pointer with reference. Commit.
  • [KEncodingProber] SBCS: Replace unbounded array pointer with span. Commit.
  • [KEncodingProber] Move sequence counter sum out of loop. Commit.
  • [KEncodingProber] Test for Cyrillic encodings (and Unicode reencodings). Commit.
  • [KEncodingProber] Test for ASCII only UTF-16 encoded texts. Commit.
KColorScheme
  • Kcolorschemetest: explicitly cast enum values to int for arithmetic ops. Commit.
KConfig
  • Add some missing CMake var initialization, for safer usage. Commit.
  • Kconfigcore: minor cleanups. Commit.
  • Fix tabNext() and tabPrev() default documentation. Commit.
KContacts
KCoreAddons
  • KFormat: Get current time in UTC. Commit.
  • Kfilesystemtype: identify a fuse mount by its name when the table cannot. Commit.
  • Kfilesystemtype: tell a fuse mount with a disk behind it from one without. Commit.
KDav
  • Fix DavItemMoveJob doc's \since specifier from 6.29 to 6.30. Commit.
  • Add dav MOVE job. Commit.
KFileMetaData
  • Reenable Qt 6.12 CI. Commit.
  • [PostscriptDscExtractor] Fix time offset parsing for Qt 6.12. Commit.
  • [PostscriptDscExtractor] Modernize QString usage. Commit.
  • [SimpleExtractionResult] Fix copy constructor. Commit.
  • [EmbeddedImageData] Remove some dead, unreachable code and data. Commit.
  • Fix a few nodiscard warnings for QFile::open. Commit.
  • [ExtractorPlugin] Allow alias names when matching extractor mimetypes. Commit. Fixes bug #522678
  • [kfilemetadata_dump] Provide information about cover images etc. Commit. See bug #500113
  • [FFmpegExtractor] Extract cover image from Matroska streams. Commit. See bug #500113
  • [FFmpegExtractorTest] Move test class declaration to implementation file. Commit.
KGlobalAccel
  • Parse QKeySequence from QDBusArgument more carefully. Commit. Fixes bug #524700
KGuiAddons
  • Add geo: URI handler for Cartes. Commit.
  • Add support to pass along the unmodified geo: URI in an URL template. Commit.
  • Fix geo: URI query string encoding. Commit.
  • Reuse explicitly provided image data. Commit. Fixes bug #519651
  • Waylandclipboard: Check isInterruptionRequested in prepare read loop. Commit. Fixes bug #517743
KHolidays
  • Fix dates for 2083 (2026/27 A.D.). Commit.
  • Update Norwegian holidays based on official sources. Commit.
  • Update event date for Urs Aala-Hazrat. Commit.
KI18n
  • Add another test case for 0. Commit.
KIdletime
  • Include CMakeFindFrameworks only with APPLE. Commit.
  • Drop no longer used CMake variable. Commit.
KImageformats
  • Jp2: Fix new[] vs delete by not doing new[]. Commit. Fixes bug #525120
  • QRoundOrZero_T: fix possible assert in qRound. Commit. Fixes bug #524678
  • Autotests: Add non-standard HEIF image. Commit.
  • Heif: re-enable decoding of non-standard images. Commit. Fixes bug #495686
  • Add HEIC test files with crop transformation. Commit.
  • Heif: check crop values. Commit.
  • Jxl: Do not rewind after reading final frame. Commit.
  • Rgb: reject RLE start offsets that underflow the raster data. Commit.
  • Avif: Do not rewind in jumpToNextImage(). Commit.
  • Ossfuzz: call functions used in animations. Commit.
KIO
  • Bring KSycoca::setupTestMenu code here. Commit.
  • Copyjob: name the copy lists for what they hold. Commit.
  • KUrlNavigator: Set the frameShape to StyledPanel. Commit.
  • KDirModel: Pass UTC time to KFormat::formatRelativeDateTime(). Commit.
  • Drop duplicated explicit install() call on kio_help plugin. Commit.
  • KDirSortFilterProxyModel: Avoid creating QDateTime instance. Commit.
  • [ftp] Send commands in upper case consistently. Commit. Fixes bug #523927
  • Autotests: Run the http worker tests as part of the suite. Commit.
  • Http: Keep one network manager for the life of the worker. Commit.
  • KOpenWithDialog: fix pattern matching in filterAcceptsRow(). Commit. Fixes bug #524085
  • Previewjob: drop the synchronous cached-thumbnail lookups. Commit.
  • Desktopexecparsertest: fix typo in installation var. Commit.
  • Desktopexecparsertest: add missing quotes for a string definition argument. Commit.
  • Previewjob: make the previews in the order they were asked for. Commit.
  • File worker: reserve an entry for the fields it always carries. Commit.
  • Kcoredirlister: let the list of items grow instead of sizing it per batch. Commit.
  • Http: Keep a large request body out of memory. Commit.
  • Kmountpoint: Write the Latin-1 strings as literals. Commit.
  • Kmountpoint: add SupportsFileCloning for COW filesystems. Commit.
  • PreviewJob: Read cached thumbnails in bounded batches before generating. Commit.
  • Gui: Read and write the thumbnail cache in one place. Commit.
  • PreviewJob: add cachedPreview() for a synchronous cache-only lookup. Commit.
  • Kfileitemactions: test service menu submenu. Commit.
  • Kfileitemactions: fix submenu lifetime using main menu as the parent. Commit. Fixes bug #524239
  • Http: Report how far an upload has got, not what the answer weighs. Commit. Fixes bug #518511
  • Avoid MIME content-sniffing on slow filesystems. Commit.
  • Systemdprocessrunner: Handle when unit returns failure. Commit.
  • Don't show percent-encoded strings for items of desktop:/ IO worker. Commit. Fixes bug #522470
  • KIO: hand what a message carries over as it is within a process. Commit.
  • Properties: show how much room a folder takes up, not only its data. Commit. Fixes bug #457363
  • Jobtest: set the language the expected text is written in. Commit.
  • Filepreviewjob: Check remote-skip conditions before checking mimetype. Commit.
  • File: ask statx for the permission bits that are read from its answer. Commit.
  • File: let a folder with the setgid bit give a copied file its group. Commit. Fixes bug #399270
  • File worker: ask for the mount of the destination while looking at it. Commit.
  • File worker: create copy destinations relative to a pinned directory fd. Commit.
  • OpenURLJob: Stop reading BrowserApplication from kdeglobals. Commit.
  • KMountPoint: cache mount lookups by unique mount id. Commit.
  • Autotests: verify KFileCopyToMenu prunes missing recent destinations. Commit.
  • Autotests: test for non_existing directories removed. Commit.
  • Kfilecopytomenu: Limits max entries and removes unavailable links from the list. Commit.
  • Git-blame-ignore-revs: add code reorganization commits. Commit.
  • Kfileitemactions: reorder functions for better readability. Commit.
  • Openurljobtest: wait for the launched processes before ending a test. Commit.
  • Autotests: ask the mime database which type a test means. Commit.
  • Openurljob: open a shell script rather than refuse it as a program. Commit. Fixes bug #522948
  • Knewfilemenu: reorder execute functions to match the order they are called in slotActionTriggered. Commit.
  • Knewfilemenu: allow overriding system templates with local templates. Commit. Fixes bug #473991
  • Knewfilemenu: set application link title to not be misleading. Commit. Fixes bug #520949
  • Knewfilemenu: fix relative symlinks. Commit. Fixes bug #508444
  • RenameFileDialog: offer the rename operation of the last rename. Commit. Fixes bug #523932
  • RenameFileDialog: free what the dialog owns. Commit.
  • SlaveBase: do not take a missing total for a finished transfer. Commit.
  • DeleteJob: pass on the bytes its rmdir subjob reports. Commit.
  • Kio_file: delete a tree natively on unix, and report the bytes deleted. Commit.
Kirigami
  • Fix typo in documentation. Commit.
  • Remove wrong API documentation marker. Commit.
  • Pull TODO out of API documentation. Commit.
  • Fix \since syntax. Commit.
  • Fix documentation syntax in ScenePosition. Commit.
  • Fix documentation syntax in PagePool. Commit.
  • Fix documented QML import name. Commit.
  • Add missing \inherits to C++ types. Commit.
  • Mark SafeArea as internal. Commit.
  • Fix parameter name in documentation. Commit.
  • Add missing parameter marker in documentation. Commit.
  • Add missing full stops in documentation. Commit.
  • Remove non-functional link. Commit.
  • Remove * from QML API documentation comment. Commit.
  • Qml-format InlineViewHeader. Commit.
  • Qml-format LinkButton. Commit.
  • Fix type name in documentation. Commit.
  • Add explicit \inherits where needed. Commit.
  • ColumnView: better layout for pinned items. Commit.
  • ListItemDragHandle: stop the dropAnimation if one is still running. Commit. Fixes bug #517233
  • InlineMessage: Use smallSpacing for the label also on its right. Commit.
  • Typo--. Commit.
  • SwipeListItem: add a deprecated notice. Commit.
  • SwipeListItem: Don't make the content item overlap the icons. Commit. Fixes bug #518436
  • Forms/flat: Fix padding calculation for subtitle. Commit.
  • Flat/FormGroup: remove unused import. Commit.
  • Flat/FormGroup: make spacing around headings more like FormLayout. Commit.
  • Flat/FormGroup: simplify layout. Commit.
  • Flat/FormGroup: make header more accurate to FormLayout. Commit.
  • OverlaySheet: make the scrim darker. Commit. Fixes bug #447965
  • Fix Overlaysheet touchscreen behavior. Commit.
  • Drop unneeded AUTOMOC_MOC_OPTIONS. Commit.
  • FormGroup(cards): fix resize loop. Commit.
  • Drop explicit CMake settings duplicatd from KDECMakeSettings. Commit.
  • Include KDECMakeSettings/KDEFrameworkCompilerSettings early. Commit.
  • Remove duplicated KDEInstallDirs module include. Commit.
  • Forms: Explicitly specify individual paddings when setting padding. Commit.
  • Units::eventFilter: fix to react just to font change of app instance. Commit.
  • FormEntry: make it possible to get the inner spacing. Commit.
  • FormEntry: make spacing more consistent. Commit.
  • Finish making GlobalDrawer.isMenu behave the same everywhere. Commit.
  • ImageColors: de-flake test_extractColors. Commit.
  • Kirigami app template: use PROJECT arg with ecm_setup_version(). Commit.
  • FormEntry(flat): make sure the trailing area is actually fillHeight. Commit.
  • Add another safety check before automatically popping hidden pages. Commit.
  • FormEntry(flat) some minor fixes. Commit.
  • FormEntry(flat) some minor fixes. Commit.
  • FormEntry(all): rename forceExpanedContents with fullWidth. Commit.
  • Form: center contents only horizontally. Commit.
  • FormEntry (both): completely propagate Layout properties. Commit.
  • FormEntry(cards): fix the flipping of switches. Commit.
  • FormEntry (flat): reorganize the internal layout. Commit.
  • FormEntry: make links inthe subtitle clickable. Commit.
  • FormEntry: introduce forceExpandedContents. Commit.
  • Docs: Explicitly ignore basictheme_p.h in Platform documentation. Commit.
  • Docs: Suppress "can't link to index.html" warnings. Commit.
  • Docs: Don't generate documentation for private types. Commit.
  • Forms: Fix documentation of FormAlignmentGroup members. Commit.
  • Forms: Fix documentation of FormGroup type. Commit.
  • Docs: Ensure the Forms module is properly included in indices. Commit.
  • Icon: Don't try to load empty fallback icons. Commit.
  • Set implicitwidth at 3d gridunits only for cards layout. Commit.
  • Keep the ToolBarPageFooter menu inside the SafeArea. Commit.
  • Make the Page globalFooter page property a QQC2.Page type. Commit.
  • Keep ToolBarPageFooter buttons inside the SafeArea. Commit.
KJobWidgets
  • Kjobcreator: Fix tab order. Commit.
KNotifications
  • Notificationtester: Add URL text field. Commit.
KParts
  • Remove some unused includes. Commit.
KQuickCharts
  • Add_qml_tests: initialize _extra_args variable in all cases. Commit.
  • Fix usage of misnamed KDE_INSTALL_DEFAULT_ARGUMENTS. Commit.
  • Controls/LegendDelegate: use tabular numerals for value. Commit.
KRunner
  • Remove left-overs of dropped ecm_setup_qtplugin_macro_names usage. Commit.
KService
KSVG
  • Add missing EXCLUDE_DEPRECATED_BEFORE_AND_AT default variable declaration. Commit.
  • SvgItem: hold one texture for one picture, like FrameSvgItem does. Commit.
KTextEditor
  • Only show bracket match preview if the view is visible. Commit.
  • Vi-mode: Fix append to block for tabs. Commit.
  • Vi-mode: Fix cursor column swap with tabs. Commit.
  • Vi-mode: Fix operations in selection ranges with tabs. Commit.
  • Vi-mode: Fix success message for non-successful save commands. Commit. Fixes bug #473077
  • Vi-mode: Fix "save all" command for unnamed files. Commit.
  • Fix resolution for KateVi::Range debug operator. Commit.
  • Vi-mode: Implement filename registers. Commit.
  • Vi-mode: Fix yank highlight for blocks with tabs. Commit.
  • Vi-mode: Simplify method to detect waiting for characters. Commit.
  • Vi-mode: Refactor the method for leaving insert mode. Commit.
  • Vi-mode: Fix checks for setting user marks. Commit. Fixes bug #520734
KTextTemplate
  • No longer explicitly include CMakeParseArguments. Commit.
  • Make date/time filter locale-aware. Commit.
  • Make Context accessible to filters. Commit.
KUnitConversion
  • Fix inconsistencies in exponentiated units. Commit.
  • Check if toUnit is in this category before trying to convert. Commit.
KUserFeedback
  • User-feedback-manual.qdocconf template: fix outdated version variable use. Commit.
  • User-feedback-manual.qdocconf template: fix outdated URL. Commit.
KWallet
  • Properly handle when the portal fails to open the wallet. Commit.
  • Remove unused variable. Commit.
  • Remove some default function args. Commit.
  • Ksecretd: Remove unused method parameters. Commit.
  • Remove leftovers from KSecretsService. Commit.
  • Ksecretd: Remove unused sessionTimeout parameter from pamOpen. Commit.
  • Ksecretd: Remove wrong comment. Commit.
  • Remove unused WId parameter from sync(). Commit.
  • Drop KSecretD::sync. Commit.
  • Ksecretd: Fix Completed argument when dismissing collection creation. Commit.
  • Ksecretd: Drop appId. Commit.
  • Drop defunct Leave Open option from kcfg. Commit.
  • Ksecretd: Remove unused force parameter from internalClose(). Commit.
  • Ksecretd: Drop CloseCancelled handling. Commit.
  • Remove unneeded KSecretD::networkWallet. Commit.
  • Drop unneeded KSecretD::doCloseSignals. Commit.
  • Ksecretd: Drop unused walletOpened signal. Commit.
  • Ksecretd: Make sure collection is reported as unlocked after PAM unlock. Commit. See bug #459287
  • Ksecretd: Make parameter names consistent. Commit.
  • Ksecretd: Drop unneeded intermediate function. Commit.
  • Drop long-time commented out code. Commit.
  • Add missing KWALLET_BUILD_DEPRECATED_SINCE in cpp file. Commit.
  • Add desktop file for kwalletd. Commit.
  • Run clang-format. Commit.
  • Drop dead CMake code for tests. Commit.
  • Deprecate Wallet::requestChangePassword(). Commit.
  • Deprecate Wallet::lockWallet(). Commit.
  • Deprecate Wallet::sync(). Commit.
  • Ksecretd: Remove unused force parameter from close(). Commit.
  • Ksecretd: Drop KWalletSessionStore. Commit.
  • Drop unused handleSession from ksecretd. Commit.
  • Drop Close When Idle handling from kwalletd. Commit. Fixes bug #524500
  • Propagate locked status from backend to kwalletd. Commit.
  • Add formatting commit to ignore list. Commit.
  • Drop isPath handling. Commit.
  • Drop unused function. Commit.
  • Fix logic errors in internalOpen. Commit. Fixes bug #524373. Fixes bug #524592
  • Autotests: fix dependency version var name. Commit.
  • Fix memory leaks in kwalletd. Commit.
  • Add Leave Manager Open to kcfg file. Commit.
  • Export kcfg file in CMake config. Commit.
  • Drop pseudo access control for wallet. Commit.
KWidgetsAddons
  • KColorMimeData: guard against a null QMimeData pointer. Commit.
  • KMessageDialog: clean-up left-over KF5 times show event handling. Commit.
KWindowSystem
  • Remove outdated & unused CMakeFindFrameworks include. Commit.
KXMLGUI
  • Aboutkde: use better shadow for Konqi. Commit.
  • Fix broken toolbars after second edit w/ multiple KXMLGUIClients. Commit. Fixes bug #442179
  • Mark more pimpl parent pointers as const, consistently be first member. Commit.
Modem Manager Qt
  • Do not use CMAKE_MODULE_PATH variable before it is created. Commit.
  • Remove usage of no longer existing QT_DEFINITIONS CMake variable. Commit.
  • Remove Qt4-times unused subdir CMakeLists.txt file for D-Bus support. Commit.
Network Manager Qt
  • Drop Q_FLAGS/Q_ENUMS on non-QObject/gadget types. Commit.
  • Port away from deprecated Q_FLAGS. Commit.
Oxygen Icons
  • Fixes. Commit.
  • More fixes. Commit.
  • Bug fixing. Commit.
  • Missing icon sizes. Commit.
  • Minor bug fix. Commit.
  • Add go-parent-folder icons for Bug 524878. Commit.
  • Minor fix i hope to a rendering bug. Commit.
  • Another derivative icon. Commit.
  • More bug fixing. Commit.
  • Bug fixing. Commit.
  • New size and clean up, the symbolic needs to point to point to a correct version of the icons for how its used in plasma, renaming on the applet to the correct linked version would be more optimal IMO. Commit.
  • Miss commit fix. Commit.
  • New icon for KDebugSettings. Commit.
  • Small improvements. Commit.
  • More sizes. Commit.
  • Just for testing. Commit.
  • Aparently forgot to submit the source svg. Commit.
  • 32X32 AND 22X22 OF KTIMER ICON VERSIONS. Commit.
  • Improvement in visibility. Commit.
  • New icon. Commit.
  • Renaming. Commit.
  • Missed an icon raster. Commit.
  • Fixing a bug and removing the faoux backlight. Commit.
  • New ktimer icon still missing the small sizes. Commit.
Purpose
  • Remove no longer used cmake variable. Commit.
  • Removal of pastebin pulgin. Commit.
QQC2 Desktop Style
  • SwipeDelegate: remove workarounds for inset. Commit.
  • TableHeaderViews: account for the margins of the root control. Commit. See bug #524099
Solid
  • Upower: Return -1 for unknown battery percentages. Commit. See bug #518513
  • Autotests: drop usage of not existing LIBS variable. Commit.
Sonnet
Syntax Highlighting
  • Add DotEnv syntax highlighting. Commit.
  • Repository.h: Make Repository::eventFilter() protected. Commit.
  • Syntax/yaml.xml: Have BuildStream files use the yaml highlighter. Commit.
  • Associate .envrc files with Bash. Commit.
  • Add KDL syntax highlighting. Commit.
  • Add Just syntax highlighting. Commit.
  • Initialize more cmake variables explicitly. Commit.

Tuesday, 8 September 2026

I tagged v0.0.11 last night. Or this morning. I’ve stopped keeping track of which side of midnight things happen on.

Most of what’s in this tag I already went on about on Monday, in the weekly update: the harp swapping, the Guitar Pro files, the pitch detectors that turn out not to hear chords. Go read that one if you want the long version, and it is a long version, I checked. This is only about the four things that landed after I wrote it.

Four things. Well. Three things, because one of them was me deleting a line out of a TODO file, and I’m not going to make you read a paragraph about that.

The harmonica I made up

So there was this line in my TODO list, and it said my chromatic harmonica was modelled as reaching less of its range than a diatonic, which is backwards. Chromatics are the ones that get all the notes. That’s the deal with a chromatic. You press the little button on the side and you get the notes in between, that is the entire pitch of the instrument, that’s why it costs four times as much as the other one.

So I went in to fix it, expecting to add a missing technique somewhere, and what I found instead was that the layout in my code was not a chromatic harmonica. It was not a harmonica at all. It was a C major scale going up one note per hole. C D E F G A B C D E F G, straight up, like somebody had typed a scale into an array and gone to lunch, which, in fairness, is exactly what happened, and the somebody was me.

A real 12-hole chromatic is solo tuned. It goes C E G C, then D F A B, and that four hole group repeats up the instrument for three octaves. That repeating is the whole point of it. That’s what makes one fingering work in every octave, that’s the thing you’re buying. Mine repeated nothing. Mine also stopped at A5 instead of C7, so fourteen of the thirty seven semitones between C4 and C7 weren’t reachable at all, on an instrument whose one job is reaching all of them.

Fourteen out of thirty seven. On the chromatic. The one that gets all the notes.

Nobody noticed, and here’s the bad part

Now you’d think somebody would have noticed, and here is why nobody did, and it’s the part that actually bothers me.

Both of the chromatic charts I ship had the made up layout copied into them. Both of them. So they agreed with the code perfectly, and the code agreed with them, and everybody in there was consistent and confident and describing an instrument nobody owns.

One of those two charts, and I want you to appreciate this with me, one of those two charts is the lesson that teaches you the chromatic slide.

So if you had gone out and bought a real chromatic harmonica, an actual physical one, made of metal, by a company, and you’d sat down to learn the slide from my game, the hole numbers would have been wrong. Every one of them. And nothing anywhere would have told you why, and you would have assumed the problem was you, because when you’re learning an instrument you always assume the problem is you, and about nine times in ten you’re right, and this was the tenth time.

Both charts are re-fingered onto the real instrument now. The music didn’t change, and it couldn’t, because every event already recorded the pitch it sounds and those pitches were fine. It was only ever the hole numbers that were imaginary. So I re-resolved each event from its note instead of its hole: thirty three of them in Für Elise, sixteen in the lesson. Not one note moved.

The slide lesson does now teach ten slides instead of fourteen, because on a real solo tuned harmonica more of those notes just sit on a plain reed and don’t need the button. Which is fine. Ten slides is plenty of slides.

And the test that would have caught this doesn’t compare the charts to themselves, because as established they were extremely happy with themselves. It compares them against a tuning this codebase can actually build.

The staff was lying too, only quietly

Two smaller ones, both in the notation strip along the top.

Accidentals were drawn per note instead of per bar. So if a tune leans on one altered note, and blues tunes do, a second position blues leans on its F# all day long, you got the same sharp redrawn in front of every single note. Which looks silly, but it’s worse than silly, because each one is about eleven pixels wide and an eighth note only has seventeen pixels to give in the first place.

They hold for the rest of the bar now, like they do on paper. And every note that cancels one gets a real natural sign, because just skipping the repeats would have left a later plain F in that bar reading as an F#, and I’d have gone from drawing something redundant to drawing something false, which is a worse trade than it sounds.

The staff itself was a fixed thirty four pixels per beat, and a notehead plus the accidental in front of it wants about twenty one, and a sixteenth note was getting eight and a half. They overlapped. Of course they overlapped. It works out the spacing from each song’s own density now, with a cap on it, because otherwise one busy bar shrinks how far ahead you can see down to about a bar and a half, and then you can’t read the thing at all.

That’s v0.0.11. Still 0.0.x, still early, still on GitHub, still, as of this week, no longer shipping an instrument that does not exist.

Today KDE releases a bugfix update to KDE Plasma 6, versioned 6.7.5.

Plasma 6.7 was released in June 2026 with many feature refinements and new modules to complete the desktop experience.

This release adds a month’s worth of new translations and fixes from KDE’s contributors. The bugfixes are typically small but important and include:

View full changelog

Its been a minute since my last posts when I went over plasma-keyboard and its new diacritics feature, and the mega-sprint in Graz.

Overview

I've been busy with a whole lot of things, but here's a brief highlight:

  • Tons of bugfixes for plasma-keyboard and the diacritics feature after some distros set plasma-keyboard to be on by default. This unexpected new batch of testers found a host of bugs that warranted a bunch of frenetic bugfixing after the launch of Plasma 6.7; thankfully these were successful and the flow of bug reports slowed
  • Improved the plasma-keyboard documentation for new contributors
  • Added a bunch of unit tests for plasma-keyboard; a few months ago we had 0% test coverage and now we have 57% of C++ code covered by tests (would prefer it higher, but progress!)
  • Added apidox to some difficult kwin code
  • plasma-setup maintenance (we've had new contributors 🎉, bugs, proposals, etc)
  • In relation to the STF grant to KDE, I've been doing technical review on behalf of the e.V. for all the changes being done (spoiler alert: a massive amount of amazing work has been/is being done!)
  • A whole bunch of gardening, reviews, bug triage/fixing, etc — not so much of the fun stuff I would like such as the new plasma-keyboard features I have planned, but important work that has been needful
  • Fixed the OSK (on-screen keyboard) button on the lock screen, and added a matching one to plasma-login-manager
  • Hopefully being merged in time for 6.8: redesigned/fixed up system tray applet for plasma-keyboard

ci-healthcheck

I created a utility to check the health of KDE CI along with a web dashboard to visualize the results, and began making weekly updates on the state of all Plasma's CI health on the mailing list.

screenshot of the ci-healthcheck dashboard

In the beginning we regularly had a dozen or more repos with CI failing on master, dozens of repos whose CI hadn't been run in months or in a few cases years, and 14% of repos configured to report failing tests in the MR view.

Now we usually never have more than 1 or 2 repo with failing CI in master any given week and even had a few weeks in a row with no failing CI, every Plasma repo runs its CI against master at least once a week to catch issues, and fully 94% of Plasma repos are configured to report failing tests in the MR view.

I consider this a massive success! 🎉🎉🎉

Still more that can be done to improve the reports and CI health, but kudos to everyone for helping trim the fat and keep our software stack healthy and reliable! 🍪

plasma-morekeys

There have been requests for plasma-keyboard to support full-sized keyboard layouts, however there has been debate about if that is appropriate to have in plasma-keyboard; such a feature seems like it would be rather niche, while adding a fair amount of complexity and maintenance burden.

Full-sized layouts wouldn't add any benefit for the vast majority of users who want a way to type in a search, a text message, etc — it seems like a much rarer user who would want to perform desktop keyboard shortcuts, use vim in a terminal, change tty from their OSK instead of a real keyboard, etc.

The super talented Aleix created plasma-morekeys as a solution for those who want a full keyboard layout that simply emulates a real keyboard and can do all of the above mentioned things like keyboard shortcuts, and more.

screenshot showing plasma-morekeys working with a terminal

This is intended as a test; it was put together quickly so those who need this feature can try it and provide feedback. If it proves successful then we can transition it to an official project, perhaps provide integration with plasma-keyboard to make choosing/using it seamless.

If you are one of the people with interest in a full-size keyboard layout for your OSK, please give it a try:

  • Install the plasma-morekeys flatpak (consider this alpha software!):
curl -L -o /tmp/plasma-morekeys.flatpak "https://nextcloud.merritt.codes/s/x4gqrs596NPYarF/download" && flatpak install --user --or-update --bundle /tmp/plasma-morekeys.flatpak
  • Test out how it works for your usecases
  • If you encounter bugs or find it isn't quite working how you require, report it and tell us what's wrong and what your usecase is so we can try and address it

Akademy

Its just over a week until Akademy!! I'm looking forward to seeing a whole bunch of my KDE family again in Graz, and the chance to get some important work done together.

I have a bit of face blindness, so please don't be offended if I can't recognize everyone on sight! (I often rely on other cues like mannerisms, hair style, voice, etc)

I will try my best to be outgoing, but if I come across as a very anxious wallflower please know that I am friendly and super happy to see you all, and I appreciate the social butterflies pulling me into the mix! 😆

See you soon! 👋👋👋

Monday, 7 September 2026

I’m getting old (though, not as old as the image below), and I miss the good old days when using Linux was more difficult, but when every system in a Linux desktop session was simple.

Excalibur (1981)
Excalibur (1981)

This was before polkit, systemd, … Even before DBus and PulseAudio. All of these systems are cool and all, but I sometimes miss the simplicity of “everything can be a shell script”.

I’ve been playing around with something that made me investigate how the Plasma session is started, and if I can replicate it without significant issues with a simple shell script or something else.

It turns out it is not that difficult of a process, just a bit convoluted.

Your chosen display manager starts startplasma. A DBus session is created and startplasmacompositor (and kwin_wayland) is started. Then plasma_session starts. And it starts kded6 and ksmserver. There are a few other things that are started as well (like kactivitymanagerd), but they are started automagically by DBus when another component tries to use their DBus API.

Moving to a shell script

In the old days, there was just a simple shell script called startkde which set the environment variables and started all the needed processes for a working KDE (and later Plasma) session. It wasn’t pretty, it wasn’t efficient, but it did what it needed to.

Se I decided to try to reimplement a rudimentary version of that script for the new Plasma.

These are the basic environment variables you’d want set before running a Plasma session (or other UI applications):

# XDG
export XDG_SESSION_TYPE=wayland
export XDG_CURRENT_DESKTOP=KDE
export XDG_SESSION_DESKTOP=KDE

# Qt
export QT_QPA_PLATFORM=wayland
export QT_WAYLAND_SHELL_INTEGRATION=xdg-shell
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
export QT_AUTO_SCREEN_SCALE_FACTOR=0
export QT_WAYLAND_RECONNECT=1
export QSG_RHI_BACKEND=vulkan

# KDE
export DESKTOP_SESSION=plasma
export KDE_FULL_SESSION=true
export KDE_SESSION_VERSION=6

I placed this into a seaprate script called env.sh as it is useful for testing to be able to initialize the same environment in a shell outside of startkde.

Apart from these, if you have some custom installation paths for Qt or KDE things, you should also set PATH, XDG_DATA_DIRS, XDG_CONFIG_DIRS, QT_PLUGIN_PATH, QML2_IMPORT_PATH and QT_QUICK_CONTROLS_STYLE_PATH.

If you have any custom environment variables defined in ~/.config/plasma-workspace/env, you should add them here as well (or source all the files from that directory).

For the main script, just source env.sh, run dbus-update-activation-environment --all and start all the session components mentioned above:

source env.sh
dbus-update-activation-environment --all
kwin_wayland --drm &
KWIN_PID=$!
sleep 1

export WAYLAND_DISPLAY=wayland-0
kactivitymanagerd &
ksmserver &
kded6 &
plasmashell &
krunner &
# other things you want started along with Plasma

wait $KWIN_PID

The sleep 1 is evil and there are better ways to check if wayland-0 became available, but this is a quick and dirty script after all. And the original startkde script also had some sleeps in it.

The only thing remaining is to create a session file that your display manager can use, and you’re good to go (/usr/share/wayland-session/shellscript-based-startplasma.desktop)):

...
Exec=dbus-run-session -- /path/to/the/script.sh
...

This seems to work without important issues, it just skips the splash screen which I can live without (wow, ksplashqml is 15 years old…).

I didn’t test it for too long, as I quickly replaced this shell script with a dinit-based setup, but more on that in the next post.

I'm going to Akademy 2026

Gwenview has been KDE’s flagship image viewer for over 25 years. It works, it’s feature-packed, and it’s been modernized repeatedly. Gwenview has served generations of KDE users faithfully and well.

So… why build a replacement?

Well, over the years Gwenview has fallen behind in some areas that can’t easily be retrofitted without rewriting half of it anyway — like better performance with large image libraries, GPU acceleration, mobile-friendliness, and the kind of enjoyably hackable QtQuick UI that would make it easier to modernize and engage a new generation of contributors.

Either by way of illustration or as a result of the above, community contributions of features, bug fixes, and UI improvements to Gwenview have dried up in recent years. It would appear that Gwenview’s technical architecture has reached the limit of what people are willing to do with it.

Something new

Meet Photos (originally named “Koko”):

Actually I lied: Photos is not a new app. Its first release was five years ago, after extended development dating at least back to 2017. But it was always somewhat niche, because it began as a mobile app and couldn’t match Gwenview in raw functionality and desktop-friendliness.

So over the past year, Techpaladin Software (full disclosure: I’m the CEO) has sponsored a bunch of development with the explicit goal of bringing Photos to parity with Gwenview as a first-class desktop app. Oliver’s recent blog post contains lots of interesting details about the process.

Techpaladin folks will continue to contribute maintenance for a while, but our hope is that we’ve managed to bootstrap the app to the point where the KDE community can generally take over for further feature and UI development on the desktop side. Therefore…

Give it a try!

I’d like to invite users to try using Photos, and invite distros to consider replacing Gwenview with it — either soon, or in the future.

I’m sure it won’t be perfect. Gwenview has had an extra 15 or so years in the metaphorical oven. But at this point Photos has pretty much all of the relevant features, plus some added goodies:

  • Much better performance, especially with large photo libraries and high DPI screens
  • Mobile-friendly UI that’s also capable of being used as a very small window
  • Shares Spectacle’s amazing annotation system
  • You can mark images as favorites
  • Timeline, location, and “just show me all my images” views

So give it a try! The current 26.08 release is pretty good, and the upcoming 26.12 release will add a few more improvements.

If you find any bugs, sources of UI awkwardness, or missing features compared to Gwenview that you actually need and care about, feel free to report them on the Bugzilla page. And changes can be contributed via merge request on Photos’ invent.kde.org page.

Let us know what you think! And if you like it, consider helping out with development or bug triaging. It takes a village to raise a child maintain an app. 🙂

This is a weekly update, which means I did a week of things and now I’m going to tell you about them, and I should say up front that most of the week went on two problems that sound small when you write them down. You had to own the exact harmonica a chart was written for. And the chart had to be in my file format, the one I invented, the one nobody else has ever written anything in.

The one harmonica you own

Here’s the thing about harmonicas. A diatonic is built in one key. Not tuned to one key, built in it, the reeds are physically cut for it, and if the chart says C and the harp in your pocket says G then you can’t play that chart. Not badly, not with effort. You sit there and nothing you do produces the note.

And people learning have one harmonica. One. Whichever one turned up. So the situation where you open my game, pick a song, and discover it wasn’t written for the only instrument you own isn’t an edge case, it’s Tuesday.

There are two honest answers to this and they go in opposite directions. You keep the tab and let the music move: same holes, same breath, different key. Or you keep the music and let the tab move: same tune, different holes. Both are defensible, I couldn’t pick, so I built both and put them on a little screen that turns up after you choose a song.

That screen also tells you the damage. Something like “5 need a bend · 2 need an overblow · 9 can’t be played”. The nine is the number I built the screen for. If swapping your harp in quietly eats a fifth of the melody, I’d rather say it out loud and let you decide than hand you a chart with holes in it and let you assume you’re the problem.

The test that went looking for one thing

The remapping code has a property test on it. It walks twelve source keys against twelve target keys, both harmonica families, every hole, both breath directions, in both of those modes I just described, and for every single combination it checks that anything the code calls playable is a pitch that harmonica can physically produce.

That check is the entire reason the test exists. If it fails, my game is sitting there listening for a note you cannot make, and then marking you down for not making it, and you’d have no way of knowing which of you was broken.

It told me I was wrong three times, and each time the code was right, which is why I wrote a test instead of trusting myself. A G harp is pitched below C, not above, so G4 sits on hole 4 and not hole 1. A#4 isn’t on an F harp at all, and I had to go find a Bb before I could test the case I actually wanted. And transposing a chart onto its own harmonica isn’t the no-op I’d assumed, because hole 3 blow and hole 2 draw are both G4 on a C harp, so resolving by pitch alone was quietly rewriting one into the other and telling me it had done nothing.

But that’s not the interesting part.

The interesting part is that the same test, which I wrote to check transposition, sat down and told me about a bug that has nothing to do with transposition at all. The set of notes my scorer will accept from your microphone was built out of blow reeds, draw reeds and bends. Not overblows. Not overdraws. On a C harp, seven of the eight over-pitches simply were not in that set, which means any chart asking for an overblow could never be scored. Ever. Not once. And nothing on screen would have told you why.

Nobody hit it because none of the charts I ship use one. It was going to sit there until somebody wrote something advanced, and then it was going to look like a microphone problem, and they’d have spent an evening buying a better microphone.

Later in the week I found the same kind of thing pointing the other way. My table of how far each hole bends had every hole capped at a semitone and a half, which is just wrong. A bend pulls a reed toward the other reed in the same hole, so how far you can go is however much room is between them. Three full semitones on hole 3, which is the note you buy a harmonica in order to play. Nothing at all on holes 5 and 7. The table says that now, and there’s a test comparing it against the physical layout, because two descriptions of one physical fact will come apart eventually and I’d rather find out from CI.

Somebody else’s files

Harmonicon reads Guitar Pro now, gp3 through gp7, and MuseScore, and MusicXML, and MIDI. Drop one in a song folder and it gets found and converted and played.

Parsing them was the easy half. The hard half is that a tab file is a whole band. Bass, comp, melody, usually drums, and I have to work out which of those you meant. So every track gets converted on its own and fitted to its own harmonica, because the right harp for a melody is almost never the right harp for a bass line, and then you get a list telling you what you’d actually be able to play. “Track 1: 6 notes, 0% playable. Track 2: 100%.”

If a track is called Harmonica it wins automatically, and I learned two exceptions to that by breaking things. A bass line called Harmonica handed me a chart with no playable notes in it at all, so being named right no longer beats being playable. And drum tracks now report zero notes on purpose, because their frets are drum kit indices, and if you read those as pitches you get a part that looks entirely reasonable, wins the picker, and then plays nothing whatsoever resembling the song.

The word “harp” on its own is deliberately not in my list of names to look for. It matches an orchestral harp. A harp part converted to harmonica is precisely the unplayable soup the list exists to prevent, and the arithmetic isn’t close: missing a track called Harp costs you one click, matching a real one costs you a chart nobody alive can play.

All seven of them sit behind one trait in a new crate, so the next format is a module and a match arm. It cost more than I wanted. That crate went from 5 dependencies to 94, almost all of it zip and everything zip drags along behind it. I wrote the tradeoff down and slept on it before deciding, which I mention because I nearly didn’t, and the thing that decided it was that zip is what MuseScore and the newer Guitar Pro containers genuinely need, so it buys three formats rather than propping up one.

Which of my detectors can hear a chord

Chord scoring has been in there for ages. It wants every pitch of the chord present at the same time, which is a reasonable thing to want. What I had never once checked is whether the pitch detectors can actually deliver it.

So I measured, on a real chord out of a real chart. FFT, which is the default, hears both notes. NMF hears both notes. YIN, pYIN and MPM hear neither.

And the three that fail don’t fail quietly. pYIN and MPM both report an F4, confidently, a note which is not in the signal, which simply happens to sit between the two notes that are. So it isn’t a chord going unscored. It’s a wrong note strolling into the scorer with its hands in its pockets.

None of which was visible from inside the game. Choosing pYIN in the options made every chord in every chart unhittable and told you nothing. The picker says “pYIN, single notes only” now, and if the song you loaded has chords your detector can’t hear, a banner says so.

While I was in there: unplugging your microphone in the middle of a song used to leave the game convinced it was still connected. Your notes just stopped scoring, forever, silently. Which is the exact failure I’d built a warning overlay for the week before, reachable by the more likely route the entire time, and I’d like it on the record that I found this myself before anybody else did.

Three hundred gigabytes

Now this next part isn’t about the game at all and you can skip it, except that it ate two days, so.

My build directory had reached 300 gigabytes.

Two separate reasons, both boring, both mine. A debug binary was 2.41 GB, of which about ninety percent was debug information for dependencies I have never once stepped into. And cargo doesn’t delete the old ones. So there were ninety-nine files of over a gigabyte each, sitting there, going back to August, every one of them a complete binary from some build I’d long since stopped caring about.

A few profile settings and a sweep later a debug binary is 287 MB, and a clean build plus the entire test suite leaves the whole tree at 7.1 GB.

While I was in the mood I also found the version number written in four different places that disagreed with each other, which meant the version the game showed you in Help / About was one that no release has ever actually carried. One script bumps all four now, and CI fails in about three seconds if the tag and the manifest disagree, rather than after four platform builds have finished uploading.

So: you can play a chart on the harmonica you own, or on the one it was written for, and either way it’ll tell you what that’s going to cost you before you start. And you can hand it a Guitar Pro file, and it’ll work out which part of the band you meant.

1306 tests passing. Still 0.0.x, still early, still on GitHub.

Sunday, 6 September 2026

The past few weeks have been about turning my initial exploration of KDE NetworkManager into actual code.

I've been working on refactoring the VPN and other virtual connections, digging through the existing Plasma NM, NetworkManager, Qt, and its code to understand how everything fits together.

Why this needs doing

Plasma-NM is the part of Plasma that talks to NetworkManager. Most users encounter it through the network applet in the system tray, but the part I have been working on lives in the Connection Editor in System Settings—the place where you can add a Wi-Fi network, configure a static IP address, or set up a VPN.

The Connection Editor is built using Qt Widgets and .ui files, which follows a traditional Qt architecture. The .ui file describes the structure of the interface and is compiled by Qt's uic tool into C++ code. The resulting View owns and lays out the widgets that make up the interface, while the widgets handle user interaction and communicate with the underlying Model through methods and Qt's signal/slot mechanism.

Here is the flow chart for this

EniokaBlogs-qt-app-anatomy-qtwidgets

Image credit: Kevin Ottens

QML follows a somewhat different approach. Instead of the View directly owning and managing every UI widget, QML commonly uses a Model–View–Delegate architecture.

The Model provides the data that needs to be displayed. The View is responsible for displaying a collection of that data, while the Delegate defines how each individual item from the model should look and behave.

modelview-overview

Now moving to the technical details of my implementation.

First and foremost, the folder structure. The current work lives in two places. A library holding the settings models and the QML views, and a KCM that ties them together:

  libs/editorqml/
  ├── settings/                    C++ models, one per settings group
  │   ├── generalSettings/
  │   ├── wificonnectionsettings/
  │   ├── wifisecurities/          Wi-Fi security and the shared 802.1x model
  │   ├── wiredconnectionsettings/
  │   ├── ipv4settings/
  │   ├── ipv6settings/
  │   └── vpn/
  │       ├── openvpn/  openconnect/  l2tp/  pptp/  sstp/  ssh/
  │       ├── vpnc/  strongswan/  libreswan/  iodine/  fortisslvpn/
  │       └── wireguard/
  └── qml/                         the views, mirroring settings/
      ├── components/              one page per connection type
      ├── generalSettings/
      ├── wificonnectionsettings/
      ├── wifisecurity/            plus authentication/ and its EAP methods
      ├── wiredconnectionsettings/
      ├── ipv4/  ipv6/
      └── vpn/<type>/

  kcms/kcm_connections_qml/
  ├── kcm.cpp / kcm.h              owns one instance of every settings model
  └── ui/main.qml                  the connection list and the editor panel

Both editors live in the same source tree, separated by a build flag.

Everything new is behind -DBUILD_EDITORQML=ON flag, which is off by default. The flag gates exactly two directories, libs/editorqml and kcms/kcm_connections_qml.

Here is the detailed refactoring for the WifiConnectionType.

Wi-Fi is the largest of the connection types, having six tabs: Status, General, Wi-Fi, Wi-Fi Security, IPv4, and IPv6.

The straightforward half

WifiSetting is the model behind the Wi-Fi tab: a QObject exposing the fields the form edits (ssid, mode, bssid, band, channel, macAddress, clonedMacAddress, mtu, hidden), with three methods doing all the talking to NetworkManager. loadConfig() reads a WirelessSetting into those properties, setting() writes them back as a QVariantMap, and isValid() says whether they could be saved.

The QML then never mentions NetworkManager at all:

QQC2.TextField {
    text: root.setting.ssid
    onTextEdited: root.setting.ssid = text
}

WifiSetting does more than just store data. It also exposes availableSsids, availableBssids, macAddresses, and channels, which are not part of the connection at all. They come from the wireless hardware and fill the drop-downs, so you can pick a network rather than typing an SSID. The channel list even changes with the selected band. Putting them on the same object means the QML gets a value and its choices from one place.

The half that is actually interesting

Wi-Fi Security is not one form. It is ten, and a single enum decides which one you see:

enum SecurityType {
    None, WepHex, WepPassphrase, Leap, DynamicWep,
    WpaPsk, WpaEap, SAE, Wpa3SuiteB192, OWE
};

In the widget version, this was a stack swapped by index. In QML, it is a combo box and sub-forms whose visibility is bound to the selection:

QQC2.ComboBox {
    currentIndex: root.wifiSetting.securityType
    onActivated: root.wifiSetting.securityType = currentIndex
}

WpaPersonal {
    visible: root.wifiSetting.securityType === WifiSecuritySetting.WpaPsk
          || root.wifiSetting.securityType === WifiSecuritySetting.SAE
}

Three of the ten (DynamicWep, WpaEap, Wpa3SuiteB192) have no fields of their own and hand the job to 802.1x, which is its own tree of seven EAP methods. This is where splitting the models paid off in a way I did not plan for.

802.1x is not a Wi-Fi thing; wired uses it too. Because Security8021xSetting is a plain QObject rather than a widget owned by a Wi-Fi tab, the wired editor binds to the same model and reuses the same seven QML files.

Validity forwards the same way, so the Save button ends up gated on an EAP form several levels down without anything in between knowing:

case WpaPsk:
    return NetworkManager::wpaPskIsValid(m_psk) || m_pskOption == AlwaysAsk;
case WpaEap:
    return m_8021xSetting->isValid();

Note the check comes from NetworkManagerQt rather than a regex written here, a rule I tried to keep throughout.

Wiring it together

The KCM is the piece that connects the two halves. kcm.cpp constructs one instance of every settings model and exposes each as a property:

Q_PROPERTY(WifiSetting *wifiSetting READ wifiSetting CONSTANT)
Q_PROPERTY(IPv4Settings *ipv4Settings READ ipv4Settings CONSTANT)

CONSTANT because the pointers never change. Only their contents do. That is what lets any QML file reach a model without being handed one:

PlasmaNMQ.WifiConnectionSettings {
    setting: kcm.wifiSetting
}

Loading a connection walks the models in one direction. Selecting a row calls loadConnectionSettings(), which hands the NetworkManager settings to each model's loadConfig() in turn, then fires requestSecrets() over D-Bus.

Secrets come back later and asynchronously, so a small hash maps a setting name to the model that wants it:

m_secretsHandlers = {
    {"802-11-wireless-security", [this](auto setting) {
        m_wifiSecurity->loadSecrets(...);
    }},
    {"vpn", ...},
};

Each model's setting() returns a QVariantMap, applyTypeSettings() assembles the ones the current connection type needs into an NMVariantMapMap, and that single map goes to the Handler, which is the only place D-Bus writes happen. The editor never talks to NetworkManager itself.

Two more signals are present. Every model emits validChanged, which the KCM aggregates into one connectionValid property that the Save button binds to, so a malformed key three levels down inside an EAP form greys out the button. The same signals mark the KCM dirty, which is what makes the Apply button light up.

Which page appears is decided by one property. kcm.connectionType comes straight from the connection, and a Loader switches on it:

switch (kcm.connectionType) {
case PlasmaNM.Enums.Wireless:  return wireless;
case PlasmaNM.Enums.Wired:     return wired;
case PlasmaNM.Enums.Vpn:       return vpn;
case PlasmaNM.Enums.WireGuard: return wireguard;
}

For VPNs, there is a second switch inside that one, based on the service type, which picks the right page from qml/vpn/. This nested lookup replaces the VpnUiPlugin mechanism, since the old contract returned a QWidget, which is incompatible with the QML-based approach.

What is left

The editor now covers Wi-Fi, wired, IPv4 and IPv6, all eleven VPN types, WireGuard, and importing and exporting VPN profiles. That is most of what a person meets day to day, but it is not everything the widget editor does.

Connection types still on widgets

Mobile broadband is the big one, and it is not just a settings page. Adding a GSM or CDMA connection runs a whole wizard that asks for the country, provider, and plan, backed by a provider database.

Then there are the virtual types: bond, bridge, VLAN, and team. These are the ones that let you build an interface out of other interfaces. DSL, Hotspot and the shared PPP settings are also in the list.

Flows around the editor

Creating a connection is in progress: the type chooser and per-type defaults work, and the Save button is now gated on real validation rather than a non-empty name, but it still needs finishing.

Export works for OpenVPN and vpnc, which is not a limitation of the port but of the plugins, since the other nine never implemented it. WireGuard import still needs its own path because it does not go through a VPN plugin like the others.

The dependency that is outside of the KCM

This is the part I had not expected. Finishing the QML editor does not mean the widget library can be deleted, because the widget editor is reachable from more than just the KCM.

libs/CMakeLists.txt has the core library linking to the editor library, not the other way around, which means Handler can open a ConnectionEditorDialog itself. That is how the applet raises the full editor for a WPA-Enterprise network it cannot join with a password alone, and kded links the same library for the secret-agent prompt. The pindialog, password dialog and secret agent from Kded also need refactoring.

So the port has a second half that has nothing to do with System Settings: those call sites need a QML equivalent before anything can be removed.

The finish line is deleting libs/editor, removing the widget KCM, and dropping the build flag, so that there is one editor again rather than two. Everything above is what stands between here and there.

Thanks, see you soon.

Saturday, 5 September 2026

Last week I attended this year’s State of the Map (SotM) in Paris, France, OpenStreetMap’s annual conference. This was actually my first time making it to SotM since I started working with OSM data for KDE Itinerary eight years or so ago.

Group photo of 500+ SotM 2026 conference attendees.
SotM 2026 group photo (source)

All links to talks below go the their abstracts, video recordings are still being added to the OSM FR Peertube instance at the time of writing.

Public Transport

On Saturday I had the chance to present Transitous, right after Felix’s and Robin’s talk about MOTIS, the routing engine we use in Transitous.

Transitous also got mentioned in a couple of other talks, including Cartes announcing integration with Transitous and CoMaps announcing work towards that as well. Overall we got very positive feedback for Transitous.

SotM being in Paris also meant there were a number of participants and presentations from SNCF and wider French public transport ecosystem. This also once again demonstrated why we need things like the Open Transport Community Conference, unsurprisingly a lot of challenges in this field exist in all countries and more international exchange could avoid some duplicated efforts.

Outside of the conference program there were also a few discussions on how to move specific topics in Transitous forward, such as integration with Citybikes or improving GTFS data deduplication and measuring GTFS feed overlap.

Indoor Mapping

SotM allowed me to meet a few people in real life that I had only encountered online in the quarterly online OSM indoor meetup so far. The work Antoine is doing on train stations or Gwenaëlle is doing on accessibility and universities isn’t “just” mapping, it’s also pushing modelling and tagging schemes in OSM forward.

I also got a chance to talk to the team from Sozialhelden about their ongoing work on elevator status data (realtime disruptions and planned maintenance times), and how we could move forward with matching that to OSM data. That’s a prerequisite for integrating elevator status data into Transitous, an eagerly awaited feature there given the huge impact this has on wheelchair routing.

A rather different but also important topic was QA tooling for indoor mapping. For OSM in general there’s not only a number tools but also quite some research on e.g. reachability or completeness metrics. For indoor spaces this is still largely uncovered, so it was good to see lightning talks in the scientific track on that.

Yet another topic that came up was the documentation, training and onboarding story for indoor mapping. Even some very experienced OSM mappers see indoor mapping apparently as hard and unapproachable, something easy to lose sight of when stuck deep in modelling and tagging issues.

Dynamic OSM

Since the discussions at the FOSSGIS conference and the discoveries presented at Grazer Linuxtage earlier this year the momentum around dynamic (traffic) data keeps building, there was a packed BoF session on this at SotM as well.

There’s a very broad interest in this, besides the usual suspects like the FOSS routing engines and map/navigation apps there were a number of national agencies present and indicating their support for an open solution, as well as firefighters unhappy with Google routing them around accidents while actually needing to go towards them.

There’s currently three approaches being developed:

  • OSM Road Closures by SOSM, focusing on (crowd-sourced) road closure tracking and being modeled around OpenLR.
  • “OSM-RT” by the MOTIS team, inspired by GTFS-RT and OSM changesets, provding a dynamic layer on top of OSM using the existing OSM data models, identifiers and tagging schemes (not unlike what we do for Kongress as well).
  • OpenEventDatabase, modeled around GeoJSON geometry and a generic tagging scheme.

Those aren’t necessarily competing, but complement each other. OpenEventDatabase shows that there’s usecases for this way beyond traffic data, OSM-RT looks like an elegant and efficient way to distribute and combine dynamic data feeds from different sources into applications, and both crowd-sourcing and integrating existing official data probably needs a more domain-specific view as adopted by the OSM Road Closure database.

One concrete outcome of the BoF session is that there’s no a Matrix channel for coordinating work around dynamic OSM data, as this probably only has a chance of gaining critical mass and going anywhere if we manage to align all this. That remains hugely ambitous of course, but seeing how eager people and organizations seem to be to break free of the monopolies in this area, this might actually work out.

KDE Integration

There’s also a few outcomes that immediately benefit users of various KDE apps:

  • The SotM venue had indoor mapping usable by Kongress, resulting in a few fixes for this. Kongress can now also add talks or sessions directly to your system calendar (on Linux and Android). Unfortunately no doors were mapped in the venue, which meant we couldn’t test the built-in routing in Kongress.
  • Itinerary and KTrip now filter out service alerts for (planned) events way outside of your time of travel, such as future construction work. This makes results in Paris a bit more readable, where there’s service alerts for works up to half a year into the future.
  • We now have a geo: URI handler for Cartes built in, so you can select that in System Settings as the default map (MR 233, part of KF 6.30). I had previously failed to figure out the details for this, after talking to the Cartes team it was just a matter of a few minutes.

And more

All of this is barely scratching the surface of SotM, the topics and the community are far broader than this. Seeing talks on how software and data from the OSM community is essential for emergency services, or helped during the response to the recent earthquakes in Venezuela show how much real-world impact what we do can have.

Hearing the head of the French National Geographic Institute (IGN) very explicitly state in his closing remarks that (digital) sovereignty can only be achieved by openness and collaboration rather than by closing down and adding borders was also encouraging, many public agencies have yet to understand that.

SotM was my start into the conference season, two other major events for me follow in quick succession now, KDE Akademy in Graz, Austria in just two weeks, and the Open Transport Community Conference early October in Bern, Switzerland.

I'm going to KDE Akademy 2026 banner