Skip to content

Friday, 27 March 2026

Eight weeks, a deep dive into KDE's architecture, and one completely fried motherboard later, my Season of KDE (SoK) journey has officially come to an end!

For the past couple of months, I've been working on KDE's computer-aided translation tool, Lokalize, under the mentorship of Finley Watson. What started as a simple UI ticket ended up teaching me a ton about open-source development, community collaboration, and user experience.

Fixing the "Jumping" Menubar

If you use Lokalize, you’ve probably run into this bug: the menubar reshuffles every time you switch tabs. Go from the Editor to the Project Overview, and suddenly the Edit, Go, and Sync menus disappear or swap places. It totally breaks muscle memory. As part of my Season of KDE project, my task was to fix it.

Instead of just hacking together a quick fix, I restructured how Lokalize handles its menus globally. I created a "Global Skeleton" layout that reserves a permanent spot for every menu, regardless of which tab is open. I then wrote logic that hooks into the application's tab-switching events. Now, if a menu isn't needed for your current tab, it simply greys out instead of disappearing completely. The result is a much more predictable UI!

Ghost Actions & The Bookmark Manager

While exploring the codebase, I discovered a graveyard of "ghost actions"—UI buttons that existed on the screen but had zero backend code making them work. After surveying the KDE translators' mailing list to understand their daily workflows, I got to work building them:

  • Core Editing & Batch Actions: I built the backend logic to make Cut, Copy, Paste, and Alternate Translations work seamlessly. I also implemented batch actions (Save All, Close All, Revert All), complete with a safe shutdown sequence that prompts users for unsaved changes so they never lose their work.
  • The Bookmark Manager (In Review): Translators lacked a good way to review multiple flagged lines in large files. I designed a brand-new, interactive Bookmark Manager dialog that lists all bookmarked entries with text previews and checkboxes. Currently, this feature is under active discussion with the KDE Visual Design Group (VDG) to ensure the UX aligns perfectly with KDE's design guidelines.

What I Learned

Writing code was really only half the experience. My biggest takeaways from SoK were:

  • Navigating Legacy Code: I learned how to read, trace, and respect a massive, established C++ codebase rather than trying to rewrite everything from scratch.
  • Community-Driven Development: Talking to the translators and getting feedback from the design team taught me that building what users actually need is just as important as writing the code to make it work.

List of Contributions

Merged:Pending Review / Ongoing:

What's Next?

While Season of KDE is over, my time with Lokalize isn't! I will keep contributing to get the Bookmark Manager completed, and I am currently drafting my proposal for Google Summer of Code (GSoC).

Special Thanks

A very special thank you to my mentor, Finley Watson. None of this would have been possible without your guidance. Thank you for the incredibly thorough code reviews, your endless patience, and for being so supportive when my hardware literally went up in smoke. Thank you for making this such a great first experience with KDE.

Finally, a huge thank you to the translators on the mailing list who helped shape these features, and the entire KDE community for being so welcoming.

See you in the KDE Git logs!

Wednesday, 25 March 2026

Greetings!

As we're nearing the end of SoK 2026, I am writing to share my experience and progress since my previous blog.

The second half of my SoK project has been a real learning experience for me, about how contributions to KDE and open-source communities in general work. I also learnt the importance of thorough testing, bug fixing, and polishing that is required before shipping any software.

Technical update and challenges faced

Till the halfway mark, I was able to complete a rough working version of the source mode editor. But after feedback from my mentor, I realized that a lot of work was still left to be done.

Firstly, there were a lot of repeated lines of code in my solution, as I had essentially split the existing editor into two - the rich editor and the raw editor. This meant that a lot of the functionality was common between the two editors, and I had re-used existing code for that. This was true for both the QML pages and the C++ document handlers. As I now realize, that would have been a nightmare for maintainability, as any single fix or change would have to be made in two places.

The fix for the C++ part seemed to be obvious: to use inheritance. So I decided to make the RichDocumentHandler and RawDocumentHandler inherit from a common parent, the DocumentHandler. DocumentHandler now contained all the common methods, significantly reducing repeated lines of code.

Similarly on the QML side, I made a parent component, EditPage, and made the Rich and Raw edit pages inherit from it. This caused some issues (mostly related to property sharing between components) that were eventually fixed.

A major issue I faced was caused by the fact that my MR essentially removed the existing EditPage.qml and documenthandler.cpp (although files with the same name are still present, they serve different purposes). Their contents were divided into two files - the rich and raw versions respectively. But while I was working on my feature, other contributors were still modifying the old files. Git can't handle this automatically, because the old file is in a way completely changed. This meant that I had to manually go through each commit to the old files, and move the changes accordingly to the new files. This proved to be a major headache, and caused me (and even Carl) to spend considerable time manually rebasing. So when finally the feature was merged, it was a sigh of relief, as we wouldn't need to manually maintain it anymore!

Apart from this, I also added a spell-checking capability to the editor, using Sonnet.

Demo

Learning and experience

I was grateful to see my work get published in Marknote 1.5 - and also felt a responsibility at the same time. Source mode is an asset for the app, but at the same time any bugs in my work are also a liability!

I learnt how a professional application is different from a hobby project, and how the quality and rigour of work should reflect that.

It was a really fun experience working with the KDE community and I'd like to thank my mentor Carl Schwan for always being there to help!

SoK has been a great first stepping stone to introduce me to the community, and I'm looking forward to contributing to Marknote and other apps even after the program!

Tuesday, 24 March 2026

Global objects are one of the core abstractions in Wayland. They are used to announce supported protocols or announce semi-transient objects, for example available outputs. The compositor can add and remove global objects on the fly. For example, an output global object can be created when a new monitor becomes available, and be removed when the corresponding monitor gets disconnected.

While there are no issues with announcing new global objects, the global removal is a racy process and if things go bad, your application will crash. Application crashes due to output removal were and still are fairly common, unfortunately.

The race

Let’s start from the start. If the compositor detects a new output, it may announce the corresponding wl_output global object. A client interested in outputs will then eventually bind it and receive the information about the output, e.g. the name, geometry, and so on:

A new wl_output announcement.

Things get interesting when it is time to remove the wl_output global. Ideally, after telling clients that the given global has been removed, nobody should use or bind it. If a client has bound the wl_output global object, it should only perform cleanup.

The preferred way to handle wl_output global removal.

However, it can happen that a wl_output global is removed shortly after it has been announced. If a client attempts to bind that wl_output at the same time, there is a big problem now. When the bind request finally arrives at the compositor side, the global won’t exist anymore and the only option left will be to post a protocol error and effectively crash the client.

A wl_output global removal race.

Attempt #1

Unfortunately, there is not a lot that can be done about the in-flight bind requests, they have to be handled. If we could only tell the clients that a given global has been removed but not destroy it yet, that would help with the race, we would still be able to process bind requests. Once the compositor knows that nobody will bind the global anymore, only then it can destroy it for good. This is the idea behind the first attempt to fix the race.

The hardest part is figuring out when a global can be actually destroyed. The best option would be if the clients acknowledged the global removal. After the compositor receives acks from all clients, it can finally destroy the global. But here’s the problem, the wl_registry is frozen, no new requests or events can be added to it. So, as a way around, practically all compositors chose to destroy removed global objects on a timer.

That did help with the race, we saw a reduction in the number of crashes, but they didn’t go away completely… On Linux, the monotonic clock can advance while the computer sleeps. After the computer wakes up from sleep, the global destruction timer will likely time out and the global will be destroyed. This is a huge issue. There can still be clients that saw the global and want to bind it but they were not able to flush their requests on time because the computer went to sleep, etc. So we are kind of back to square one.

Attempt #2

The general idea behind the first attempt was sound, the compositor only needs to know the right time when it is okay to finally destroy the global. It’s absolutely crucial that the Wayland clients can acknowledge the global object removal. But what about the wl_registry interface being frozen? Well, it’s still true, however we’ve got a new way around — the wl_fixes interface. The wl_fixes interface was added to work around the fact that the wl_registry interface is frozen.

With the updated plan, the only slight change in the protocol is that the client needs to send an ack request to the compositor after receiving a wl_registry.global_remove event

After the compositor receives acks from all clients, it can finally safely destroy the global.

Note that according to this, a client must acknowledge all wl_registry.global_remove events even if it didn’t bind the corresponding global objects. Unfortunately, it also means that all clients must be well-behaving so the compositor can clean up the global data without accidentally getting any client disconnected. If a single client doesn’t ack global remove events, the compositor can start accumulating zombie globals.

Client changes

The client side changes should be fairly trivial. The only thing that a client must do is call the wl_fixes.ack_global_remove request when it receives a wl_registry.global_remove event, that’s it.

Here are some patches to add support for wl_fixes.ack_global_remove in various clients and toolkits:

Compositor changes

libwayland-server gained a few helpers to assist with the removal of global objects. The compositor will need to implement the wl_fixes.ack_global_remove request by calling the wl_fixes_handle_ack_global_remove() function (it is provided by libwayland-server).

In order to remove a global, the compositor will need to set a “withdrawn” callback and then call the wl_global_remove() function. libwayland-server will take care of all other heavy-lifting; it will call the withdrawn callback to notify the compositor when it is safe to call the wl_global_destroy() function.

Conclusion

In hindsight, perhaps available outputs could have been announced differently, not as global objects. That being said, the wl_output did expose some flaws in the core Wayland protocol, which unfortunately, were not trivial and led to various client crashes. With the work described in this post, I hope that the Wayland session will become even more reliable and fewer applications will unexpectedly crash.

Many thanks to Julian Orth and Pekka Paalanen for suggesting ideas and code review!

SWHID is an open ISO standard with transparent governance and free access to its specification. It includes a reference implementation and several tools that developers can use or extend. The community is open, and anyone can join the discussion to help shape the next version of the standard. Read more in this new blog post

Today is the simultaneous release of Krita 5.3.0 and Krita 6.0.0!

Depending which version of Qt and KDE Frameworks you build, the same source will result in one of the other. Both versions are almost functionally identical, with 6.0.0 having more Wayland functionality.

But note that since Krita 6 is still considered rather experimental, since it's our first release based on Qt 6, and there were many complicated changes between 5 and 6.

For real work, please use 5.3.0! We expect 6.0 to become the main version of Krita before the end of the year, though.

What's New

Video courtesy of David Revoy.

Krita 5.3/6.0 is the result of many years of work by the Krita developers. Some features have been rewritten from the ground up, others make their first appearance.

Enjoy the completely new text feature: on canvas editing, full opentype support, text flowing into shapes. It is now easier than ever to create vector-based panels for comic pages. Tools got extended: for instance, the fill tool now can close gaps. The liquify mode of the transform tool is much faster. There are new filters: a propagate colors filter and a reset transparent filter. Support for HDR painting has been improved. The recorder docker can now work in real time. There is improved support for file formats, like support for text objects in PSD files. And much, much, much more!

For a complete overview of everything, check out our extensive release notes!

5.3.0 Download

Windows

If you're using the portable zip files, just open the zip file in Explorer and drag the folder somewhere convenient, then double-click on the Krita icon in the folder. This will not impact an installed version of Krita, though it will share your settings and custom resources with your regular installed version of Krita. For reporting crashes, also get the debug symbols folder.

[!NOTE] We are no longer making 32-bit Windows builds.

Linux

Note: starting with recent releases, the minimum supported distro versions may change.

[!WARNING] Starting with recent AppImage runtime updates, some AppImageLauncher versions may be incompatible. See AppImage runtime docs for troubleshooting.

MacOS

Note: minimum supported MacOS may change between releases.

Android

Krita on Android is still beta; tablets only.

Source code

For source archives, please download the 6.0.0 archives and build with Qt5.

md5sum

For all downloads, visit https://download.kde.org/stable/krita/5.3.0/ and click on "Details" to get the hashes.

Key

The Linux AppImage and is signed. You can retrieve the public key here. The signatures are here (filenames ending in .sig).

6.0.0 Download

Windows

If you're using the portable zip files, just open the zip file in Explorer and drag the folder somewhere convenient, then double-click on the Krita icon in the folder. This will not impact an installed version of Krita, though it will share your settings and custom resources with your regular installed version of Krita. For reporting crashes, also get the debug symbols folder.

[!NOTE] We are no longer making 32-bit Windows builds.

Linux

Note: starting with recent releases, the minimum supported distro versions may change.

[!WARNING] Starting with recent AppImage runtime updates, some AppImageLauncher versions may be incompatible. See AppImage runtime docs for troubleshooting.

MacOS

Note: minimum supported MacOS may change between releases.

Android

Krita 6.0.0 is not yet functional on Android, so we are not making APK's available for sideloading.

Source code

md5sum

For all downloads, visit https://download.kde.org/stable/krita/6.0.0/ and click on "Details" to get the hashes.

Key

The Linux AppImage and the source tarballs are signed. You can retrieve the public key here. The signatures are here (filenames ending in .sig).

Hello again! Welcome to another bugfix release. This update is highly recommended, as it tackles a few critical issues to keep the app running smoothly.

  • Resolved a crash that occurred when creating your first notebook.
  • Fixed an issue that prevented notebooks from being removed.
  • Corrected a glitch allowing notes to be drag-and-dropped through quick sketches.
  • Note sorting is functioning as intended now.
  • Restored the undo and redo buttons when a single note is open.
  • Added the "Clear" button to allow removing content from the quick sketches.
  • Added several minor background improvements for better overall stability.

Get Marknote

We encourage everyone to update as soon as possible. You can grab the latest version via Flatpak, or your favorite package manager.

Packager Section

You can find the package on download.kde.org and it has been signed with Carl Schwan's GPG key.

Monday, 23 March 2026

Heading into the final weeks of SoK, things got a bit chaotic. Week 7 was right in the middle of my university exams, so i had to take a planned break from coding. I survived the exams and was geared up for a solid Week 8 sprint to wrap things up, but then my laptop chose violence again. A few weeks back it had already died on me once (the screen went completely dark). I replaced the panel, it came back to life, and i thought we were good. This time it was worse: no screen flicker, no fan spin, nothing. The repair shop confirmed a motherboard failure and told me, “We can look at it… eventually… no promises on timeline.”

Waiting around simply wasn't an option. So i did the only logical thing in full panic mode: I went straight to a store and bought a new laptop. RAM prices hit me hard too 💀, but I think it'll be worth it at this time. Huge thanks to my mentor, Finley, for being super understanding and providing an extension so i could set up my new dev environment and finish strong!

Once the new setup was ready, i jumped right back into Lokalize. Both of my final MRs ended up being fixes for edge cases in the major features i built earlier in the program.

1. Fixing an Oversight from Week 3: Menu Availability

Back in Week 3, i wrote the updateMenuAvailability() logic to dynamically disable top-level menus whenever a user switched tabs. However, i missed a crucial edge case: what happens when you close the very last tab? Because there were no tabs left to trigger my update function, the menu state would stay stale when the application fell back to the "Welcome" screen.

To fix my own bug, i patched the showWelcome() function to directly call updateMenuAvailability() and made sure that the application startup sequence initialized through showWelcome(). This way, the refresh path runs consistently every time the Welcome screen appears, ensuring the menus are greyed out correctly when no tab is active.

Merge Request: Lokalize: Refresh top-level menu availability on Welcome screen

2. Fixing a DRY Failure from Weeks 4 & 5: Tab Focus

The next task came out of the code review for the batch file actions (Save All, Close All, etc.) I built back in Weeks 4 and 5. While reviewing my MR that implemented the "Revert All" feature, Finley pointed out an existing DRY (Don't Repeat Yourself) failure in the codebase.

When "Revert All" is triggered, it checks if any open files have unsaved changes. If they do, it activates a warning prompt via the existing fileOpen() function. The problem was that fileOpen() was triggering a warningTwoActionsCancel dialog ("The document contains unsaved changes...") without focusing on the related tab first. If a translator had many files open, they wouldn't know which specific document the warning was actually about. Because this UX issue affected any call to fileOpen(), he asked me to tackle it in a separate, dedicated MR.

I fixed this by adding a new signal signalActivateThisTabRequested(QWidget *tabWidget) to the EditorTab class. Right before the prompt is called in editortab.cpp, I emit this signal passing this as the argument. I then wired this up in lokalizemainwindow.cpp to connect to activateTabByPageWidget. Now, the second the warning appears, the UI automatically flips to the correct document, making the action much safer and more intuitive.

Merge Request: editor: focus relevant tab before unsaved changes prompt in fileOpen()

While testing this new tab focus MR, i actually found another bug in my old "Revert All" code, it doesn't revert all the open files every time. I suspect this might be due to some interference from autosave, so I'll be looking into fixing that in the future.

This officially wraps up my Season of KDE! It’s been an incredible experience diving into C++, Qt, and KDE's XMLGUI architecture. Huge thanks to Finley and the KDE community for the mentorship and support throughout the journey.

Sunday, 22 March 2026

Animations… Everybody loves animations. They make your desktop look more eye candy, and they also help with guiding or drawing the user’s attention towards certain elements on the screen, for example a new window or a popup or a button, etc.

An animation is a just quick series of images to simulate the movement of an object on the screen. For example, consider a basic animation that only lists digits

A digits animation.
Animation frames.

As you can see, it’s just a sequence of individual digit images that are presented quick enough.

The animation frames have to presented at a steady pace to maintain the smooth feeling. If one or two frames are late, a frame drop can occur, which the user will likely notice. There are various reasons why a frame may not be presented on time. One reason might be that the app has been busy doing something else, for example loading a big file from the disk. However, there can also be reasons that are not exactly under the direct influence of the app, for example the operating system scheduler may prioritize scheduling other tasks or perhaps the memory scheduler has decided that it needs to do some of its things, etc. An animation with a frame drop may look as follows

Frames 4, 5, and 6 have been dropped.

If a frame cannot be painted on time, we are in a sticky situation no matter how you look at it. The animation won’t look as smooth as it could.

That being said, it is also worth mentioning how some apps (and compositors) drive animations. A lot of them advance animations simply by the amount of time that has passed since the last frame. In pseudo-code, it looks as follows:

const milliseconds target = next_presentation_timestamp();
const milliseconds delta = target - previous_repaint_time;
previous_repaint_time = target;

advance_animations_by(delta); 

The biggest drawback of this approach is that it can introduce discontinuities to the motion of an object. For example

Frame drop analysis.

If the app becomes busy for about 48 milliseconds after painting the frame 3, the delta interval will be 48 the next time a frame will be painted, which will effectively advance the animation to frame 7. Technically, this is not the wrong behavior. It does make sense if you look purely at the math. From the human eyes point of view though, the fact that there is a discontinuity in the animation now is far from ideal.

Luckily, there is a simple workaround. If we know the refresh rate of the monitor, we could estimate the maximum allowed delta interval for a single frame (for example, for a 60Hz monitor, it will be 16.6ms) and limit the animation delta time by it. It won’t make animations butter smooth but it will reduce discontinuous jumps in the animation.

Animation with smoothed delta intervals.

In comparison to the aforementioned described method to advance animations, with the proposed method, animations will advance as follows

Animation frames with smoothed/capped delta intervals.

As you can see, even though there was a frame jitter, the frames 4, 5, and 6 have not been dropped. Obviously, this is not a silver bullet solution. The final motion may still look “wonky” depending on the duration of the frame jitter but even with that being said, the advantages are still worth it. In pseudo-code, it will look as follows

const milliseconds target = next_presentation_timestamp();
const milliseconds max_delta = 1000 / output->refresh_rate;
const milliseconds delta = min(max_delta, target - previous_repaint_time);
previous_repaint_time = target;

advance_animations_by(delta);

Plasma 6.7

The animation delta interval throttling will be implemented in the next release of Plasma. Note that this will only affect compositor-side animations, for example the ones that are played when a window is opened or closed. It will not affect animations that are played inside applications, the compositor has little actual influence on those.

In my testing, both on my desktop and a less powerful laptop, I noticed slight improvements when opening new windows, animations feel a little bit more smoother especially on the laptop. That being said, please don’t take it as me saying that animations will be perfectly smooth. No, if a frame jitter occurs, we’re in a pretty ugly situation and these changes are mostly about hardening kwin so the animations look less choppy.

Eight weeks ago, I wrote an excited post about joining Season of KDE 2026. Now, as the program wraps up, it's time to look back at what we built. My project had two goals: make the Mankala AI smarter using parallelism, and give the game a visual refresh. Both are done. What Got Built Digital Assets…

Saturday, 21 March 2026

During FOSDEM this year, I met a group of very cool people that put together events about Open Source Design. They invited me to participate this year.

The topic was similar to my talk during FOSDEM, but with a twist on the soft skills needed to complete the creation of a new design system for Plasma.

The conference is only one day. Something really cool they did was to have something like BoF (Birds of a Feather) sessions during the afternoon slot before my talk. We had the opportunity to discuss important aspects proposed by the attendees around design and making your way into the field.

Germany, Berlin. FOSS Backstage Design 2026 – Community, Management and Compliance. 18.03.2026 Photo Jan Michalko

Since I was representing KDE in this project, I brought my newly-acquired tshirt from FOSDEM!

The experience was great. Everyone was awesome and even met up with new contributors to the VDG. Comments from after my talk were very positive and I look forward to participating in even more events of this nature.

Design System Update

At this time, the design system keeps evolving. I have gone back to rework some application icons. After reviewing the current designs and consulting with team members, the consensus is that they should be simpler, more approachable. I have made some concepts that I will keep exploring going forward.

Additional to that, new tokens have been created. We now have shadow tokens, and I have applied them to all the components that I can think of. They have replaced manually-created shadows from the previous time.

Radius and spacing tokens now need to also make their way into the components. Unfortunately, Penpot is struggling today on creating new tokens. I will wait for an update. However, the work involves going through out component buttons and applying foundational spacing tokens to the sides and inner spaces for the components.

While this might be long, it is easy. The spacing numbers are already in the component. My part is to create all the spacing components, locate the margin or padding number I see today in buttons, find the base component, and apply the spacing tokens.

I am also closely following up on Penpot’s new beta, hopefully coming out soon, with their new rendering engine.

I have also connected the Penpot team with our LAS (Linux App Summit) event and hopefully they can attend.

More to come!

If you’re interested in participating in this effort and have some awesome design and integration skills, join us!

https://matrix.to/#/#visualdesigngroup:kde.org