Skip to content

Tuesday, 30 June 2026

Ocean Icons in Penpot

I began the process of moving icons from Figma to Penpot. The icons are locate in the Icons page inside Penpot. This page contains some placeholder icon components created by our contributors. This was done so that we could use generic icons for UI as we complete more parts of Ocean design.

Including new icons from Figma into Penpot is not easy. There are a couple of drawbacks we see. Penpot does not have the same shape control that Figma does. Because of that, Penpot does not recognize shapes like circles, squares, lines, etc. Another issue is the importing erases the naming schemes that the icons have. This is a known bug at Penpot and they are working to resolve it.

Penpot also seems to do something pretty interesting on import. It creates a folder where the graphics are contained, it adds an invisible background image, and in some cases, a bitmap in the same structure. I believe these are just import issues that Penpot resolves in different ways. I hope their parsing on import gets better overtime.

However, with the new rendering engine in Penpot, the icon collection, though slow at times, is able to keep up with the rendering and I am able to load up thousands of svg icons in Penpot. Under the previous set up, this would not be possible without crashing.

Because of these two drawbacks, I had to flatten all the images before exporting. Otherwise, Penpot could run into rendering issues as svg code from Figma has its own quirks. Still, with these compromises, I was still able to copy all the monochrome icons built for Ocean into Penpot. Now, I have to take the time to rename all the icons to their proper names. There are also some icons that don’t render properly, or at all. For those icons, I will have to take them case by case and fix them.

After renaming all these icons and getting them to their proper organization, I will create components from them that can be used in UI throughout the file.

Ocean + Union June Meeting

Earlier this month, we also connected with the Union team to discuss the implementation support of reference tokens and semantic tokens from Penpot into Plasma. We also discussed the creation of Ocean assets.

Providing support in Plasma for Ocean design tokens is no small feat. It requires careful preparation. This is one of those elements that Ocean Design provides. The goal here is that Plasma and applications are able to support the tokens/variables provided by the Ocean design system.

In a distant future, our goal would be to give the Ocean system to anyone willing to create their own version. They would export their tokens and Plasma would support them without the need for additional code. This achieves a couple of great things, one is that designers work in an environment that is ready for editing with all the graphical sources needed, no need for coding skills. On the other hand, this should push interested users into designing and testing their designs first, without the need to push code first, which requires the mobilization of our teams to review, adapt and provide feedback. It’s a “cheaper” effort to work with graphics first and see how they would look after export.

There are more benefits to this approach, but these are two that I always go back to.

From this meeting, our determination is that I should provide a series of spec files containing all the technical details from the foundational components in Penpot. These components are the smallest bit of interaction in graphical systems. Buttons, shadows, typography, etc. These are akin to the bricks on a wall.

With this idea in mind, I was able to create and provide the specs to the Union team.

New Components in Penpot

Continuing with the process of creating sample components in Penpot, we now feature:

  • Application Navigation: This page contains sidebars for desktop and mobile.
  • Section Footers: This page contains footers used in application footers or even websites, for desktop and mobile sizes.
  • Inline CTAs (Calls to action): These CTAs help users make quick decisions on a piece of information without the need for taking over the screen using Modals, for example.
  • Section Headers (Received an expansion): Section headers used to be just one version of the component. This was expanded to add more versions.
Please note that these components are "not" Plasma components and is not indicative of Plasma's future state. These are here for inspiration for the future, and for users looking to test their design edits and results. Eventually, these could serve as starting points for layout design, component creation, etc.

In addition to these component samples, I am now working on building a calendar view component. I am taking inspiration from Merkuro Calendar and also including some ideas from Ocean. The idea is that a calendar view could be available for others to use and test designs against. More to come on this.

Fixes in Ocean Design System

  • Fixed layout and design for dropdown items. They were previously just text in a layout. They are supposed to behave differently inside dropdown menus. They need to have inner margins and separation from other items in a list. The shortcut component also received updates and looks more legit than before. This should make it easier to use dropdown components in Penpot.
  • Created a dot component that is used in Calendars to show activity or reminders inside monthly views.
  • Inputs components received updates so they behave better when manipulating sizing. There were some issues with containers and components inside containers so I tried fixing those. There is an existing Penpot bug that when components are updated at the source, the component copies tend to go back to use fixed sizing instead of dynamic sizing. This makes me cycle through fixing not just sources but copies of the components. It seems the issue is in nested components where the parent takes over some of the sizing properties. Penpot is working on this.
  • Created the checkmark inside a circle component. This component is meant to be used when running through selections that need to indicate success in their interaction. For example, completing a survey, or a multi-step installation procedure. A checkbox is not appropriate there but a circle with a checkmark inside. So now that’s available. It’s not using a monochrome Ocean icon checkmark yet, but that can be changed in the future.
  • Avatars are now using an image with color all inside. Previously I had an avatar image that was white and it made it difficult to see the white border around the avatar image.

Next in Component Design

  • Calendar view
  • Slideout menus
  • Progress steps

Thanks

  • Thanks to Beryesa for creating the Penpot Design Notes page. This is super helpful when users need to understand what they are looking at and how it is used in the system. 🙏🙏🙏
  • Thanks to iclone for his work on commenting around UX issues in the VDG channel.
  • Thanks to the Union team for all their support for Ocean.
  • Thanks to Lorenz for helping me troubleshoot some crashes in Penpot.

Penpot Issues and Features

https://github.com/penpot/penpot/issues

Monday, 29 June 2026

This is a weekly update from my Google Summer of Code 2026 project with KDE, improving effect widgets in Kdenlive, a free and open source video editor.

MR !887 merged

The Curves widget MR got merged to master this week and will ship in the Kdenlive 26.08 release. Final change before merge was replacing blockSignals(true/false) pairs on the point spinboxes with QSignalBlocker objects, which auto-unblock when they go out of scope cleaner and safer than manual pairs.

Gradient widget skeleton

Started work on the Gradient widget, targeting the gradientmap MLT filter. The filter already supports up to 32 color stops via stop.N parameters (e.g. stop.1="#ff000000 0.0"), but Kdenlive's existing XML only exposed 2 stops with the wrong parameter type.

Built a new GradientEditWidget in src/assets/view/widgets/ with:

  • Draggable color stop handles on a live QLinearGradient preview bar
  • Left-click on empty area adds a stop with an interpolated color
  • Right-click on a handle removes it (minimum 2 stops enforced, max 32)
  • Serializes to/from the MLT stop.N format via a new ParamType::GradientEditor wired through AssetParameterModel
  • Project save/reload round-trips correctly

Added gradienteditwidgettest.cpp with 8 assertions covering serialization round-trips, add/remove stops, min/max enforcement, and position snapping. All passing.

What's next

Julius Künzel suggested the widget be designed for potential upstreaming to KDE Frameworks, and pointed to Qt-Color-Widgets as a reference; it has a GradientEditor class with a polished UX. Investigating whether to wrap that instead of maintaining a custom implementation. Will update once there's more clarity on the direction.

Both the Gradient widget MR and the Qt-Color-Widgets investigation are in progress; more next week.

Read on for a look at development news and the Krita-Artists forum's featured artwork from last month.

Development Report

Krita 5.3.2.1/6.0.2.1 Released

A 5.3.2.1/6.0.2.1 hotfix release was made to fix two serious regressions.

First, a layer-related problem causing issues like the wrong layer being selected or crashes was fixed. This reverts the behavior of the layer being changed on keyframe selection. (bug 1, bug 2, bug 3; change)

Second, a crash when inserting or removing hold frames was fixed. (bug; change)

Developments in Krita Plus

Android Donations Overhaul

On Krita downloaded from the Android Google Play store, it was possible to purchase a supporter badge as a way of donating to the project.

For the next release, Carsten has overhauled this into the ability to purchase supporter packs (change). These supporter packs will let you download resource bundles with brushes, templates, and more directly inside Krita, as a convenience compared to manually importing them. There is also a subscription, managed through Google Play, which will give access to all of the offered resource bundles.

As with all official store versions of Krita, the money will go towards supporting Krita's development, and none of Krita's features will be locked behind a paywall. Krita on the Google Play store remains free, and offers the same APKs as available on the official website.

Other Features and Fixes

On Android, a prompt to adjust the interface scale will now be shown on first startup, and it can be changed later in Settings → Change Interface Scale. (change by Carsten Hartenfels)

Multiple layers selected with Ctrl+click are no longer deselected when using the Transform Tool (bug; change by Luna Lovecraft). An issue where no layer was selected when deleting the top layer of a document and opening a new view on it was fixed (bug; change by Gregg Jansen van Vuren).

An issue where the buttons of the Selection Actions Bar and Assistant Tool panel could render entirely black, and an issue where Reference Images could cause slowdown, were fixed. (change by Wolthera van Hövell)

Invert Selection no longer selects a larger area than it should. (bug; change by Ricky Ringler)

Crashes went undoing creating a text and then making a new text shape, and when undoing multiple shapes, were fixed. (bug 1, bug 2; change 1, change 2 by Elena Sagalaeva)

A keyboard shortcut assigned to the Rotate or Zoom Canvas canvas inputs no longer crashes when used. (bug; change by Agata Cacko)

Developments in Krita Next

Exporting Recorder Timelapses on Android

Previously, Krita on Android could not render video, as it's not possible to run an external FFmpeg as is done on other platforms.

However, Carsten has found a way to instead use Android's built-in MediaEncoder to render the Recorder's timelapses (change). This is also planned to be implemented for rendering animation in the future.

Other Features and Fixes

Various other features and fixes have made their way into the Unstable builds.

The Wide Gamut Color Selector now has the saved color history feature where the color history is remembered on restart and can be saved into the document. This is part of the effort to make it a replacement for the Advanced Color Selector. (change by Wolthera van Hövell)

The Histogram Docker now scales the histogram based on the maxiumum value, and has a button to show it in logarthimic scale. (change by Wolthera van Hövell)

In the Transform Tool's Free transform mode, holding Ctrl for perspective and then letting go of Ctrl now activates a camera height adjustment mode. (change by Ralek Kolemios)

Future Developments

Testing Needed: Updated Library Dependencies

The team has been working on updating the various libraries Krita depends on for things such as file format support to newer versions. These updated dependencies need testing before they can be merged to the main builds, so check out the library updates forum topic for test packages if you want to help!

Community Report

May 2026 Monthly Art Challenge Results

The winner of the "Animals and Patterns" challenge is…

Join This Month's Art Challenge!

For June's theme, last month's winner has chosen "Something Unexpected", with the optional challenge of using filters.

This month's featured forum artwork, as voted in the Best of Krita-Artists - April/May 2026:

Participate in next month's nominations and voting to voice your opinion on the Best of Krita-Artists - May/June 2026.

Krita is Free - But You Can Contribute!

Krita is free to use and modify, but it can only exist with the contributions of the community. A small sponsored team alongside volunteer programmers, artists, writers, testers, translators, and more from across the world keep development going.

If this software has value to you, consider donating to the Krita Development Fund. Or Get Involved and put your skills to use making Krita and its community better!

Krita's mascot Kiki putting money in a piggy bank

Additional Changes

Krita Plus (Stable, 5.3.3/6.0.3-prealpha):

  • File Formats: Fix loading XMP Exif metadata where a rational number field has spaces. When attempting to save XMP Exif with invalid values, omit the invalid values and log a warning instead of failing to save. (bug 1, bug 2; change by Agata Cacko)
  • General: Add icons for the Getting Started, User Community, Krita Website, Source Code, and Switch Application Language actions (change 1, change 2 by Arkady Flury)
  • HDR: Animation: Use non-constant luminance coefficients so FFmpeg >=7 will render HDR animations. (bug; change by Wolthera van Hövell)
  • Android: Support stylus barrel rotation. (change by Carsten Hartenfels)

Krita Next (Unstable, 5.4.0/6.1.0-prealpha):

  • Comic Panel Editing Tool: Close all shapes cut with the tool. (change by Agata Cacko)
  • Selections: Fix moving a vector selection to not convert it to a pixel selection. (change by Luna Lovecraft)
  • Filters: Fix Wavelet Noise Reducer only applying to the first 512 rows of the image. (change by Sergey Kleimenychev)
  • General: Fix Image Split with guides being off by one pixel. (bug; change by Aditya Rout)
  • General: Fix the backup file suffix setting in General->File Handling to be editable. (bug; change by Carsten Hartenfels)
  • Selection Tools: The option to Move Selected Content was reverted due to bugs, such as making a selection showing the move cursor. (bug; change)

Nightly Builds

These pre-release versions of Krita are built every day.

Note that there are currently no Qt6 builds for Android.

Get the latest bugfixes in Stable Krita Plus (5.3.3/6.0.3 prealpha): Linux Qt6 Qt5 — Windows Qt6 Qt5 — macOS Qt6 Qt5 — Android arm64 Qt5 – Android arm32 Qt5 – Android x86_64 Qt5

Or test out the latest Experimental features in Krita Next (5.4.0/6.1.0-prealpha). Feedback and bug reports are appreciated!: Linux Qt6 Qt5 — Windows Qt6 Qt5 — macOS Qt6 Qt5 — Android arm64 Qt5 – Android arm32 Qt5 – Android x86_64 Qt5

Sunday, 28 June 2026

GSoC 2026 • digiKam • Post 1: Design and Progress

I’ve been hanging around KDE apps since I was a teenager :), so getting to spend another summer inside one feels a bit like coming home. This time it’s digiKam!

Here’s what I want digiKam to do: Let me type “photos of mountains from last summer that I rated highly” into a search box, and have it just… find them. No complex filters, no guessing where to click, just plain English, the way you’d ask a friend who’d been on the trip with you.

That, in one sentence, is my GSoC project: interfacing digiKam’s search engine with an AI-based LLM so you can search your photo collection in natural language. For many users, digiKam’s Advanced Search is a hidden gem, powerful but a little intimidating. By adding natural language support, we’re making it accessible to everyone, from beginners to experts who want to save time. And as someone who’s used KDE apps for years, I loved the idea of bridging the gap between digiKam’s powerful features and the simplicity of just asking for what you want.

I’d recently been deep-diving into transformers, and this project stood out to me as a near-perfect blend: real software development and having to actually understand LLMs, which architectures fit where, when you want an encoder versus a decoder, and how a model behaves once it’s wired into an actual application.

This first post is about the overall design and the progress so far. The more interesting bits about the actual language model: which one, how fast, how accurate, are coming in a second post, so consider this the scene-setting.

(Mild Technical Content Ahead, but I promise to keep the scary parts optional. ;) )

The one idea the whole project rests on

DigiKam already has a powerful Advanced Search feature: a dialog packed with dropdowns for tags, dates, ratings, albums, color labels, and more. It can handle complex queries, but it requires users to know how to navigate it.

So the LLM in my project does NOT search your photos. Let me say that again, because it’s the most important design decision: the model never touches your database, never decides what matches, never invents results. All it does is translate your sentence into the exact same structured query you could have built by clicking the dialog yourself. The model produces an “intent”; digiKam’s existing, trusted search engine does the actual searching.

I like this framing because it keeps the AI firmly in its lane. The LLM is a translator sitting in front of a door that already exists: it’s not a new door, and it definitely isn’t allowed to wander off and make things up. Anything it produces is something a human could have produced by clicking. That’s the safety guarantee, and everything in the pipeline is built to enforce it.

The Pipeline in Action

Here’s the journey your query takes:

Your sentence
     |
     v
[ Prompt Builder ]  : wraps it with format instructions
     |
     v
[ Language Backend ]  : runs the model, returns raw output
     |
     v
[ Intent Parser ]  : validates strictly; rejects anything malformed
     |
     v
[ Capability Dictionary ]  : maps human words to digiKam's real fields
     |
     v
[ Intent Resolver ]  : builds concrete search criteria
     |
     v
Advanced Search widgets populated —> digiKam runs the search

Walking through it:

  1. You type a sentence.
  2. A prompt builder wraps it in instructions that tell the model exactly what format to answer in.
  3. A language backend runs the model and gets back its answer.
  4. An intent parser reads that answer and crucially validates it strictly. The model’s output is never trusted directly. If it isn’t well-formed and doesn’t match the known fields and operators, it’s rejected outright. No partial guessing.
  5. A capability dictionary maps fuzzy human words to digiKam’s real fields: “best” might mean a high rating or an “Accepted” pick label, “colour label” maps to the actual colour-label field, and so on.
  6. An intent resolver turns the validated, mapped intent into concrete search criteria.
  7. Those criteria populate the Advanced Search widgets, and digiKam runs the search exactly as if you’d filled them in by hand.

The nice consequence of splitting it up this way is that the model can be as small and dumb as we like, every layer after it is busy double-checking its homework. If the model says something nonsensical, the parser catches it. If it names a field that doesn’t exist, the dictionary won’t map it. By the time anything reaches your database, it’s been laundered through several layers of “is this actually a thing digiKam can do?”

Why Local Models?

A quick but important detour. The model runs locally on your own machine, not in some company’s cloud. Running the model locally isn’t just about performance - it’s about privacy.

And privacy matters more here than it might first appear. Think about what’s actually in a photo library: where you live, who your family and friends are, where you travelled and when, the inside of your home, your children, the events that matter to you. A photo collection is one of the most personal things a person keeps on a computer. And the searches you run over it are revealing in their own right, the words you’d type to find a photo say something about what you’re looking for and why.

If any of that were sent off to a remote server to be processed, you’d be trusting a third party with exactly the information most people would least want to hand over. Running everything on-device sidesteps that entirely: your photos never leave your machine, your queries never leave your machine, and there’s no account, no API key, and no internet connection required. The feature works the same on a plane as it does at home.

The catch is that a local model is a big file you have to get onto people’s computers somehow which brings me to the part I spent most of this period actually building.

Plugging into digiKam’s Infrastructure

C++ has been my favourite language since my teens, and one of the quiet pleasures of this project has been getting to brush up on it properly. Qt, though, is a different story and a familiar one if you’ve read my Krita posts. Every term I spend with Qt I learn a little more, and every term I’m reminded that it’s one of the harder frameworks to get comfortable in, precisely because it leans so heavily on design patterns. You don’t really “learn Qt” so much as slowly stop being surprised by it.

When I first thought about “the model needs to be downloaded onto the user’s computer,” my instinct was to just write something that downloads a file. Simple enough. But that instinct was wrong. digiKam already knows how to download large model files. It’s been doing it for years, for face recognition, object detection, auto-rotation, aesthetics scoring. There’s a whole system for it: a central DNNModelManager that reads a config file describing each model, and a FilesDownloader that fetches the files from KDE’s servers and verifies them. My mentor’s guidance was clear and correct: don’t build a parallel download mechanism, plug into this one.

The wrinkle is that this entire system was built for OpenCV vision models - models that look at images. My model is a language model run by a completely different library (llama.cpp). It’s a different kind of beast that doesn’t fit the OpenCV machinery at all.

The solution turned out to be pleasingly modular. By creating a lightweight DNNModelNaturalLanguage class, I was able to plug into digiKam’s existing model download system without modifying its core logic. This means the GGUF file is downloaded, verified, and managed just like digiKam’s other AI models (e.g., for face recognition). There’s an existing model type in digiKam (DNNModelConfig) that registers and verifies a file but does no OpenCV loading and that was almost exactly the shape I needed, so my wrapper mirrors it. The actual loading and running of the model happens separately, in a SearchLlamaBackend that talks to llama.cpp.

So the division of labour is clean: digiKam’s existing system handles getting the file onto your computer (download, checksum, the works), and my llama.cpp backend handles running it. No duplication, and my model gets to ride the same well-tested rails as every other model in digiKam.

Why Qwen2.5-1.5B-Instruct

A word on the model itself (much more in post two). I went with Qwen2.5-1.5B-Instruct, in a quantized GGUF form, for a few reasons:

  • Size vs. capability. At ~1.12 GB (Q4_K_M quantization) it’s small enough to download and run on a normal laptop, but the 1.5B-Instruct variant is genuinely good at following instructions and producing structured JSON, which is exactly what I need it to do.
  • It’s good at the specific job. This project lives or dies on the model reliably emitting clean, parseable, schema-shaped output. Qwen2.5 is notably solid at structured outputs.
  • The licence. It’s Apache 2.0, so it’s freely redistributable, which means KDE can actually host the file on its own infrastructure. Why a decoder-only model (and not BERT)

Before picking a specific model, I had to pick a kind of model and this is one of the places my transformers reading actually paid off, so indulge me for a paragraph.

The obvious-seeming choice for “understand a sentence and classify it” is an encoder model like BERT. Encoders are efficient, deterministic, and great at fixed-label tasks. But they assume a finite, predefined output space and that’s exactly what search queries don’t have. A query can express any number of constraints (“landscape photos with red labels near Paris last summer” is four constraints at once), and I’ll keep adding new searchable dimensions over time. To force that into an encoder, I’d need a pile of auxiliary pieces: intent classifiers, entity extractors, rule-based combiners and that scaffolding gets more brittle every time digiKam gains a new search field.

A decoder-only model sidesteps all of that. It generates a sequence, so it can emit a variable number of structured constraints directly as JSON, following a schema I define in the prompt. New search field? Update the schema and no retraining. And it handles ambiguity gracefully: instead of being forced to pick one interpretation of “best photos,” it can emit a clarification request inside the same constrained output. (Encoder-decoder models like T5 could generate structured output too, but they carry extra architectural and latency overhead that’s wasteful for a local, on-demand desktop feature.)

So the short version: a small decoder-only model gives me compositional, schema-shaped generation with built-in ambiguity handling, at a size that runs on a laptop. That’s the whole wishlist.

All assuming 4-bit (Q4) quantization in GGUF form, which is the standard for CPU inference with llama.cpp:

ModelParamsSize (Q4)LicenceNotes
Qwen2.5-1.5B-Instruct1.5B~0.9–1.2 GBApache 2.0Balanced quality/efficiency, good structured output, long context — my primary
TinyLlama 1.1B1.1B~0.7–0.9 GBApache 2.0Lightest, fast CPU inference — fallback for low-RAM machines
Gemma 2B Instruct2B~1.3–1.6 GB(Gemma terms)Strong general language understanding
Phi-22.7B~1.5–1.8 GBMITBetter reasoning than 1B models, heavier CPU load
Qwen2.5-3B-Instruct3B~1.8–2.2 GBApache 2.0More capable than 1.5B, but higher RAM
Phi-3 Mini3.8B~2.2–2.6 GBMITBest comprehension here, but slowest and largest

The pattern is a straightforward size-vs-capability trade-off. The bigger models (Phi-3 Mini, Qwen2.5-3B) reason better but want more RAM and run slower on a CPU-only laptop and since this feature has to stay usable on ordinary hardware without a dedicated GPU, “runs comfortably in ~1 GB of RAM” is a hard constraint, not a preference. That rules the heavier models out for the default.

Among the lightweight options, Qwen2.5-1.5B-Instruct hits the sweet spot: small enough for a CPU laptop, but notably reliable at the one thing this project actually needs: emitting clean, schema-shaped structured output. TinyLlama 1.1B stays in the picture as a fallback for lower-end machines where even 1.5B is too much. Both are Apache 2.0, which (as above) is what makes KDE hosting possible, a point that quietly eliminated some otherwise-tempting models with restrictive licences.

Natural language search demo

Where things stand

The pipeline already works end-to-end using a mock backend (standing in for the real model). So, prompt > parse > resolve > populate-the-search-and-run is all functioning and unit-tested. And the download integration I described above is in place: the model is registered with digiKam’s central manager, and I’ve verified it gets picked up correctly and its file path resolves to the shared model directory.

What’s Next?

  • Host the GGUF model on KDE’s infrastructure (in progress).
  • Wire up llama.cpp for local inference.
  • Benchmark Qwen2.5 vs. TinyLlama on digiKam-specific queries (e.g., “5-star photos”, “pick label accepted”).
  • Dive into model performance, fine-tuning, and caching!

Found an excuse to start drawing again :)

Hand drawn art

Saturday, 27 June 2026

In my last post on Android platform integration I had suggested increasing the focus on Linux on mobile phones, due to Google’s ongoing attempts to close down Android for us. I have to follow that myself then of course, starting with looking at the situation around location/positioning.

What we have

The positioning stack looks roughly as follows as far as KDE applications are concerned:

  • Geoclue collects positioning data from various sources (GNSS receiver, cell modem, online service, etc) and provides a D-Bus API for it.
  • XDG Desktop Portal has a Location API for exposing this to sandboxed applications, including permission handling. XDG Desktop Portal itself uses Geoclue as its source.
  • Qt Positioning provides the application-facing API for retrieving position data, and QLocationPermission provides the API for requesting permissions when running sandboxed.

There’s two main gaps here though:

  • Qt Positioning only has a Geoclue backend, not one for the XDG Desktop Portal Location API. This means we wont have access to positioning information in a sandbox.
  • Qt’s entire permission API has only a dummy implementation on Linux, meaning it will always claim all permissions have been granted, which isn’t true in a sandbox.

What I couldn’t look into is whether Geoclue is able to actually retrieve GNSS data on “real” hardware, lacking access to devices to test this on.

Developer Tooling

Testing GNSS code with real hardware is rather inconvenient anyway, you have to move around for this, and quite a bit even when you also want to test various edge cases. Much better would be a way to inject arbitrary GNSS data low enough in the stack.

Many years ago I had built something like this for GammaRay, but that works on the level of Qt Positioning, which is above the parts we want to test here. Fortunately Geoclue offers us a way to do this as well. It’s looking for _nmea-0183._tcp mDNS services that provide a NMEA 0183 feed, and will use that as a source for GNSS data.

NMEA 0183 is a decades-old serial port protocol for GNSS equipment, easy enough to implement. As there are more usecases for this below, there’s now a small library setting up such a services, announcing it via mDNS and sending NMEA 0183 messages. NMEA 0183 defines two dozen or so different message types, but since Geoclue only looks at GGA and RMC ones those are all we need.

Screenshot of a map view showing a GPX track replay on the left and simulated and received position data in text form on the right.
XDG portal location spoofing tool.

Then all we need is a little GUI on top of this to select inputs on a map and have that fed into Geoclue. As a bonus we also have GPX track replay, so you can get a continuous feed of position updates automatically. The code is here.

XDG Portal plugin for Qt Positioning

Being able to “move” around the world from the convenience of my desk/couch made it then easy to implement the main missing piece here, connecting Qt Positioning to the XDG Desktop Portal Location API. The code is here, it’s just a couple of D-Bus calls and a bit of boilerplate needed for positioning plugins.

It’s registered with a higher priority than the Geoclue plugin and will be skipped if the portal API is not available.

QPermission API

That still leaves the permission handling. Qt’s API for that lives in Qt Core, so we cannot just implement support for XDG protal permissions there, as that needs dependencies from higher up in the stack, such as D-Bus and window ids.

There’s an easy way out though, by generalizing an already existing permission plugin system that so far is only provided on Apple platforms. A patch to Qt enabling this on Unix systems as well is in review currently. With that applied implementing support for requesting location permissions is then very similar to what the positioning plugin already does, you’ll find the code here.

A very convenient side-effect of having permission plugins is that we can now also build a “simulator” that allows testing permission flows in applications while running in an unrestricted development environment and without having to mess with system permission settings each time.

So I did that here, usage is pretty simple:

$ qpermission-simulator [--ask|--grant|--deny] <application-to-test>

Note that this also needs the above mentioned Qt patch to actually do anything, and it currently requires the tested application to be a QApplication in order to show its UI. It does work for all permission types supported by Qt though, not just the location one.

Other positioning sources

One unique feature that microG offers on Google-free Android is using onboard APIs of planes/trains/buses as a location source. That’s useful as the GNSS antennas of those vehicles tend to give you much better results than the one on your phone inside the metal casing of the vehicle.

Of course we have to have that as well, and it’s easy enough to build that since we have an existing library for dealing with onboard APIs already as part of KPublicTransport.

Putting that together with the NMEA 0183 server we get a small KDED module that watches for changes to the Wi-Fi network you are connected to, and if it’s one of a known onboard API it’ll offer its service to Geoclue. As Geoclue will only connect to it on demand (ie. when an application actually asks for a location) it will only poll the onboard API when necessary, so we also get practically no overhead in the idle state here.

Other sources are possible in a similar way as well, e.g. having KDE Connect provide location data from your phone to your desktop computer.

Feedback

This is mostly the result of about two weeks of prototyping, and I’d very much appreciate review and feedback on this. Does the general approach make sense? Is there something else missing around the the location/position topic? Where should the various components live and be distributed eventually (some of this only really relevant inside a Flatpak sandbox fox example)?

Welcome to a new issue of This Week in Plasma!

This week members of the core Plasma team spent almost all of their time in bug-fixing mode! As usual, people unleashed their real-world setups on the new Plasma release and found some issues we missed during the development process, and that nobody reported during the two beta releases. So we made it a priority to fix those issues!

Plasma 6.7.1 was released earlier this week with the first round of fixes, and 6.7.2 is scheduled for early next week with more.

A few new features and UI improvements started landing, too.

Check it out here:

Notable new features

Plasma 6.8

You can now set up wallpaper slideshows that don’t change automatically. Instead you can manually switch the wallpaper via the item in the desktop context menu or its global keyboard shortcut. (Fushan Wen, KDE Bugzilla #518669)

Added an Esperanto keyboard layout to Plasma’s virtual keyboard. (Daniel O’Neill, plasma-keyboard MR #148)

Notable UI improvements

Plasma 6.7.2

Improved text alignment in System Monitor’s Processes page while using the tree view. (Arjen Hiemstra, KDE Bugzilla #442095)

Plasma 6.8

Reduced the number of visual frames in the Menu Editor app. (Levi Leal, kmenuedit MR #59)

Notable bug fixes

Plasma 6.6.6

Fixed an issue that could make KWin crash when some apps opened dialogs and popups in non-standard ways. (Vlad Zahorodnii, kwin MR #9444)

Fixed a regression introduced by Qt 6.11 that made the word “Undefined” appear next to Places entries in the Kickoff Application Launcher widget. (Christoph Wolk, KDE Bugzilla #521799)

Fixed a visual glitch affecting users of Deskflow that could make a clone of the pointer inappropriately remain visible on the client machine. (David Redondo, KDE Bugzilla #521486)

Plasma 6.7.1

Fixed a somewhat common case where KWin could crash on the lock screen when using an NVIDIA GPU. (Xaver Hugl, KDE Bugzilla #520842)

Fixed a recent regression that made KWin crash when using a DisplayLink monitor. (Xaver Hugl, KDE Bugzilla #520361)

Fixed an issue that could make screens plugged into a laptop with both an NVIDIA and an AMD GPU freeze. (Xaver Hugl and SungHwan Jung, KDE Bugzilla #521727)

Fixed a recent regression that broke certain shader-based KWin effects, including the popular “Burn My Windows” effects. (Xaver Hugl, KDE Bugzilla #521774)

Fixed a recent regression that broke color-related KWin effects like color blindness correction and screen color inversion. (Xaver Hugl, KDE Bugzilla #521737)

Plasma 6.7.2

Fixed the currently most common KWin crash, related to variable refresh rates with multi-monitor setups. (Vlad Zahorodnii, KDE Bugzilla #521909)

Fixed a case where Info Center could crash when trying to display information about certain NVIDIA GPUs. (Harald Sitter, KDE Bugzilla #521295)

Fixed a recent regression that broke the RDP server when run using systemd. Sorry about that! (David Edmundson, KDE Bugzilla #521776)

Fixed a recent regression that could sometimes make Chromium-based apps freeze if another window was forced into “Keep Above Others” mode. (Xaver Hugl, KDE Bugzilla #521687)

Plasma 6.8

Fixed a case where Plasma could crash after you ejected an audio CD from Dolphin or Audex. (Kai Uwe Broulik, KDE Bugzilla #522051)

Flatpak 1.18.1

Fixed an issue that made a background service for Flatpak crash if you used the KDE desktop portal dialog to allow an app to update itself while the app was still running. (Sebastian Wick, flatpak issue #6686)

GTK 4.23.2

Made the process of selecting text to later paste it by middle-clicking more reliable in GTK 4 apps. (Vlad Zahorodnii, gtk MR #10006)

Notable in performance & technical

Plasma 6.7.2

Improved full-screen video playback performance in Chromium-based apps. (Xaver Hugl, KDE Bugzilla #521960)

Plasma 6.8

Turned on triple buffering by default for NVIDIA GPUs, because the bugs blocking this from working properly in the past have since been fixed. (Xaver Hugl, kwin MR #9472)

How you can help

KDE has become important in the world, and your time and contributions have helped us get there. As we grow, we need your support to keep KDE sustainable.

Would you like to help put together this weekly report? Introduce yourself in the Matrix room and join the team!

Beyond that, you can help KDE by directly getting involved in any other projects. Donating time is actually more impactful than donating money. Each contributor makes a huge difference in KDE — you are not a number or a cog in a machine! You don’t have to be a programmer, either; many other opportunities exist.

You can also help out by making a donation! This helps cover operational costs, salaries, travel expenses for contributors, and in general just keeps KDE bringing Free Software to the world.

Friday, 26 June 2026

Time to put on my Techpaladin Software had again: we’re hiring! David Edmundson has already posted it, so I’m here to boost the signal: we need you! Especially if you’ve got experience with Qt/KDE development.

We’re looking for someone with that plus an eye for UX & design, and another person with that plus deep tech skills, particularly around I/O and other “system plumbing” topics.

Check it out: https://techpaladinsoftware.com/joinus.html

KDE has now started its fifth round of “goal-setting” — a process that began in 2017.

These goals are big, high-level goals. Think “focus on large strategic topic X” more so than “fix my pet bugs A, B, and C”.

And it’s up to the KDE community to choose the goals! Up to you, up to me, up to all of us. They will then be voted on, with the top three informing KDE’s overall direction for the next two years.

KDE needs you! Your fresh ideas, your contributions, your budding leadership talent. But not your imposter-syndrome-driven fear of coming out of your shell. 🙂 Overcome that!

I’ve now been the champion for two goals: “Usability & Productivity” and “Automate & Systematize Internal Processes“. I’d count the first as pretty successful; the second, less so. So at this point I feel like I have a pretty good idea of what works and what doesn’t, and I’d like to use that knowledge to help others submit their own high-quality actionable goals.

Here’s what works, in my experience:

  • Articulate an immediately obvious large problem or aspiration. If you’ve spent any time in KDE or using KDE software, you’ll have encountered stuff that could be better. Big stuff. That’s the topic to write a goal around! Not “Discover is too slow to refresh when I launch it” but rather “First-class software delivery UX” (you can steal that).
  • Have a technical lead and a communications lead. They can be different people, but you need both. Only have a tech lead? Not enough communication, people forget about it, don’t join, and the tech lead burns out. Only have a comms lead? Not enough work happens and the goal limps embarrassingly towards the finish line.
  • If you propose a goal, be the tech lead. You can be the comms lead too. But you should be prepared to be the tech lead. It’s much easier to recruit people to do other things than it is to recruit a tech lead to do a ton of work on your behalf that you can’t to yourself.
  • Don’t bite off more than you can chew. If you can’t be the tech lead on your idea, scale it back or change its scope so that you can be the tech lead. This doesn’t mean you have to know how to do everything. It just means you can do a big chunk of the technical work, and at least understand the concepts of the things you can’t personally do.

It’s really fun, and you end up having a huge impact on how awesome KDE is.

I’ve submitted a goal proposal about improving KDE’s documentation. Credible choices make voting useful, but right now we have as many submissions as goal slots, and that’s no fun. So get yours in too!

If you've been around KDE, you've probably heard of Techpaladin Software

We're a consultancy that works almost exclusively on KDE, improving KDE upstream while delivering the features and functionality our clients need.

We're growing and looking for more developers to join the team. We're after experienced developers who are already familiar with KDE, C++, and Qt.

We currently have two positions available:

  • Someone with a keen eye for design to work on high-level features and help shape the user experience.
  • Someone with a more technical background to work on lower-level system integration and platform work.

In return, you'll get to work with some of the most talented and welcoming developers in this space... and also me!

If that sounds interesting, you can find more information and apply here:
https://techpaladinsoftware.com/joinus.html

KDE Eco organized a two-day sprint to work on KEcoLab — the energy measurement lab I maintain. A few more people came and joined on day two, which made the discussions richer. The sprint was mostly about figuring out the next steps for KEcoLab, fixing some open issues with the measurement pipeline, and trying to get a second system set up in the lab. You can find the full sprint notes here.

Thanks to KDE e.V. for sponsoring the sprint, KDAB for hosting us at their Berlin office, and Volker for the guidance and support throughout.

Headless Remote Access

The first thing we wanted to sort out was making the lab PC fully remotely accessible, which means we could use it without having someone physically in the room to debug our issues with the lab PC. SSH was already working fine, but RDP was not very reliable (it used to break sometimes). Nowadays, We had to go to the lab PC in person once to allow remote connections, which is exactly the kind of thing you do not want to do repeatedly. During the sprint, we applied a fix using flatpak permission-set to persist screen capture permissions across reboots, based on a recently merged patch (plasma/xdg-desktop-portal-kde!326).

However, the issue is not fully closed. A couple of days after the sprint, the problem came back and it was not clear what had changed. We had a follow-up meeting to discuss this, notes here. Joseph and Volker are currently testing different approaches to figure out how to properly persist the session across reboots. We will update this once we have a reliable fix.

Setting Up a Second Machine

Cornelius Schumacher donated a Fujitsu Esprimo 510 to the lab for us to add as a second System Under Test. It did not go well. The machine threw six beeps on boot, which pointed to a CPU or motherboard problem, most likely a BIOS flash failure. We tried reseating the RAM, resetting the CMOS battery, testing each RAM stick one by one. Nothing changed. The machine is not usable, but we are keeping the hardware for spare parts.

Debugging the Okular Pipeline

We ran the full Okular measurement pipeline on Wayland and hit a few problems. On day two, we saw an R error about non-numeric data. When we looked into it, the collectl files had data from pipeline runs we had stopped earlier, stopping a pipeline mid-run was causing the next run to append data to existing files, which broke the statistical analysis. We now have issues open to fix this properly:

  • Kill pipeline: #64
  • Failsafe for pipeline failure: #18
  • Cancel SUS script: #16

A cleanup stage has since been added to the pipeline to handle this — see !152.

The reports also had wrong durations and missing averages. That turned out to be missing stopActions in the ydotool scripts. Once we fixed those, the numbers looked reasonable. Joseph is continuing to work on this and will follow up on the remaining open questions.

There is also a broader question about the data itself. Our power draw results look very different from measurements taken at Umwelt Campus Birkenfeld back in 2022. Their data showed distinct power draw phases across tasks, while ours is mostly flat apart from the spike when Okular opens. It could be Wayland versus X11, Flatpak versus a repo package, or just Fedora versus Ubuntu. Probably a mix of all of them. Something to investigate.

Future Planning

We went through the open issues and talked about what comes next, integrating KEcoLab into a broader KDE Plasma testing workflow, planning for the Akademy BoF, and ideas around extending ydotool/kdotool to make measurement scripts more flexible. We did not get through everything but the direction is clearer now.

Outside the Lab

After the first day, Carl, Aakarsh, Joseph, and I headed to Tempelhofer Feld. It was a warm evening and the field was full of people cycling, skating, and just sitting around. We walked around for a while and talked about everything except KEcoLab, which was exactly what we needed. A big thank you to Carl and Joseph for showing us around, Berlin has a way of making you feel like you have all the time in the world, even when you have a pipeline to fix in the morning.


Not everything worked, but we came away knowing exactly what needs to be fixed and why. That is usually what a good sprint looks like.

Tempelhofer Feld

Tempelhofer Feld