Skip to content

Wednesday, 25 June 2025

Dear fans of music & open source music players,
in preparation of the upcoming Amarok 3.3 release, a second beta release (3.2.82) has been prepared.

This time, the most important change is the new GStreamer-based audio backend. This enables a number of features that weren't available on Qt6 Phonon backends, and likely also provides a more reliable audio experience in general. In addition to audio system work, e.g. more safeguards have been set up around collection scanning code to prevent some potential database issues. More details on changes are listed in the ChangeLog.

These come in addition to the previous beta 1 changes (Qt6 only, database update). Please note that due to the database update in beta 1, downgrading from 3.3 betas is not directly possible, and returning to pre-3.3 versions requires the database (at ~/.local/share/amarok/mysqle/) to be manually backed up beforehand.

Additionally, there isn't an official release with Qt6 support of liblastfm available yet (needed for last.fm support). To enable last.fm functionalities, one needs to use a library version built from e.g. sources at https://github.com/Mazhoon/liblastfm/tree/81e8f9dc16a0a18fe9a066d51c2071086326670b

The Amarok 3.3 beta 2 source tarball is available on download.kde.org and it has been signed with Tuomas Nurmi's GPG key. In addition to the source code, it is likely that some distributions will provide beta packages. The various nightly git builds provided by various splendid packagers should also provide a way of using the beta changes and participating in the testing.

Happy listening!

Tuesday, 24 June 2025

Plasma's upcoming first-run experience is coming along nicely.

After a bunch of research and discussions we settled on continuing / fixing up the KISS project (KDE Initial System Setup) for the new First Run Experience (FRE) / Out-Of-Box Experience (OOBE). It was in a sort of half finished state.

Since then has been a bunch of work on it such as:

  • Getting it to compile and run
  • Whole bunch of fixes, cleanup, and polish to the UI, UX, and code/developer experience
  • Implemented the ability to actually create the new user (you could enter a name and password, but it was all basically placeholder GUI previously)
  • Added language selection front/back ends
  • Added basic build / run instructions to the README
  • Added ECM logging
  • Cleaned up the debug output which made changes more difficult
  • Added basic CI (thanks Nico!)
  • Added keyboard layout selection front/back ends

That last one was more difficult than expected.. turns out keyboard layouts can be quite complex!

First came some refactoring of the keyboard layouts KCM from plasma-desktop so we could reuse some of the existing, complex functionality. Then adapting a UI/UX appropriate for the FRE. Investigating things like keyboard models, detecting defaults, mapping language to keyboard layout, etc..

Then taking the results of the user choice and figuring out how to apply that both to the system as a default (systemd-localed dbus call) as well as to the running Plasma session (setting the value manually in the kxkbrc keyboard settings file).

As is often the case with software development, that succinct summary belies the massive amount of work it took to get there! 😅 💪

With that work completed, we have most of what is needed for a minimum viable product!

Next up:

  • Granting authentication without a user prompt
    • Plan: special user with sysusers.d and a polkit rule
  • Running automatically on first boot & in live sessions
    • Plan: systemd unit seems promising, but more research is needed
  • Improve documentation
    • Especially related to building & running without kde-builder
  • Think about the name
    • I am not thrilled with the KISS acronym personally 🤷‍♀️

There is also obviously a lot of improvements and polish that can be made, but for now here is a preview of the FRE:

These past few week’s my focus was on exploring input device detection and event handling mechanisms in Linux, with a particular emphasis on game controllers and their potential integration into KWin. I also spent time reading through KWin’s input-related source code to understand how it currently manages devices, and began reviewing documentation for various Linux input subsystems—including evdev, HID, and /dev/input/jsX in order to evaluate which layer would provide the most reliable and straight forward support for integrating controller recognition. The time was mostly spent learning how to use different libraries, tools and creating virtual controller prototype.

Tools, Libraries, and Concepts Used

libevdev

libevdev is a library for handling evdev devices. It provides a higher-level interface over /dev/input/event* and abstracts much of the complexity of input event parsing.

evdev is the generic input event interface. This is the preferred interface for userspace to consume user input, and all clients are encouraged to use it.

-The kernel development community.

libevdev can be used to:

  • Detect physical game controllers.
  • Read input events (e.g., joystick, buttons).
  • Create virtual input device and write/forward events to it from physical game controller.

Useful functions:

  • libevdev_new(), libevdev_set_fd(int fd, struct libevdev **dev): for opening physical devices.
  • libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev): for polling events.
  • libevdev_get_id_*(const struct libevdev *dev): to query device meta data.

uinput (User Input Subsystem)

I used the Linux uinput subsystem to create a virtual input device that mirrors a physical controller input. uinput is what allows us to make a virtual controller out of any evdev device by:

  • Opening a file discriptor for the input device that will be emulate (i.e. have it input event forwarded).
  • Forwarding the inputs from a evdev interface device to /dev/uinput (or /dev/input/uinput).
  • uinput then creates a new node to expose the virtual device as a evdev interface device in /dev/input/event*

From here the idea is that KWin or any other system component can treat the virtual controller as if it were an ordinary HID device.

uinput is a kernel module that makes it possible to emulate input devices from userspace. By writing to /dev/uinput (or /dev/input/uinput) device, a process can create a virtual input device with specific capabilities. Once this virtual device is created, the process can send events through it, that will be delivered to userspace and in-kernel consumers.

-The kernel development community.

Useful functions:

  • libevdev_uinput_create_from_device(const struct libevdev *dev, int uinput_fd, struct libevdev_uinput **uinput_dev):
    For creating a uinput device based on the given libevdev device.
  • libevdev_uinput_get_devnode (struct libevdev_uinput *uinput_dev):
    Return the device node representing this uinput device.
  • libevdev_uinput_write_event (const struct libevdev_uinput *uinput_dev, unsigned int type, unsigned int code, int value): Post an event through the uinput device.

Tools used:

  • libevdev-uinput.h for management of uinput devices via libevdev.
  • /dev/uinput opened with correct permissions.
    • Ensuring the current user is in the input group.
    • Verifying that the uinput kernel module is loaded (using modprobe uinput). Some distros (Ubuntu/Kubuntu) have it built in, not loaded as module, thus modprobe uinput command won't log anything.
    • Opening /dev/uinput with O_WRONLY | O_NONBLOCK flags using open(), and ensuring no EPERM or EACCES errors were returned.
    • Optional: Run program as sudo user.

force feedback detection/support

Using ioctl(fd, EVIOCGBIT(EV_FF, ...)) and tools like fftest, I examined:

  • How to query a device’s force feedback (FF) capabilities to figure out which effects are supported (e.g., rumble, sine wave).
  • How to upload ff effects to physical game controller and test rumble motors.
    • This was key to understanding haptic capability support on physical devices.

To enable force feedback, you have to:

have your kernel configured with evdev and a driver that supports your device.

make sure evdev module is loaded and /dev/input/event* device files are created.

Testing & Validation

  • Used evtest and fftestto test evdev devices and understand their capabilities - sudo evtest /dev/input/eventX.
  • Used those same tools to test virtual devices creating using uinput -
    sudo fftest dev/input/eventX. uinput creates a node device in dev/input/eventX for the virtual input.
  • Prototype logs validate that a virtual device can be created and events can properly be written to a that virtual device using libevdev.

Takeaways

  • Using libevdev and libevdev-uinput we can access physical controllers, create virtual controller and read/write low-level input events.
  • Understanding of the permission requirements to open /dev/input/* and /dev/uinput (use udev rules or run as root).
  • Tools to test:
    • evtest and fftest (from input-utils)
    • udevadm info --name=/dev/input/eventX --attribute-walk
      • Shows the device hierarchy - how the device is connected to PC and any parent device it connects to.
  • Built a minimal proof-of-concept C++ program that routes an evdev devices input 1:1 to a virtual controller (via uinput).
  • Not all controllers support all force feedback types; some failed with EINVAL during upload.
  • libevdev does not handle FF upload directly — this remains kernel-level and typically involves ioctl().

References and Documentation

Monday, 23 June 2025

You might have noticed that Plasma keyboard shortcuts have been changing recently with the aim to have everything KWin/Plasma be Meta+Something.

Now, I tend to redefine most of the default shortcuts, so this didn’t affect my workspace directly, but I liked the idea to have different modifiers used depending on the category of /thing/ for which I’m creating a shortcut.

An additional aim I had is to have a common shortcut ‘body’ for equivalent actions in different categories, in order to more easily build muscle memory with my new shortcuts.

Categories that I have are:

  1. system (Plasma and such)
  2. terminal application (Konsole, Kitty)
  3. terminal multiplexer (TMux)
  4. specific application (Firefox, Vim, …)

And these are the modifiers I’m trying out:

  1. system: Meta+Anything and Alt+Special keys
  2. terminal application: Ctrl+Shift+Anything
  3. terminal multiplexer: nothing special yet, just the Ctrl+d as the leader
  4. specific application:
    • working with tabs: Ctrl+Anything
    • other shortcuts: Alt+Normal keys

So, for example, the ; and ' as the shared shortcut /bodies/ mean the following in different categories:

  1. Meta+; and Meta+' – switch to next and previous window (I’m using Krohnkite for tiling, so this is not like Alt+Tab, but moving through the visible tiled windows);
  2. Ctrl+Shift+; and Ctrl+Shift+' – would mean switch to next and previous panes in the terminal application (I’m not using this yet, as I don’t tend to use split views in terminal except in TMux);
  3. Ctrl+d ; and Ctrl+d ' – move to next and previous panes in TMux;
  4. Alt+; and Alt+' – move to next and previous panes in an application (currently only in Vim and Neovim).

So far, the approach seems to work Ok. I’ve quickly got accustomed to the new window/pane navigation shortcuts.

The main problem are the programs that don’t allow changing shortcuts (Firefox for example) or don’t allow creating shortcuts with some key combinations (using Ctrl+; in Vim or Neovim does not work, while it works with Alt).

Because of those limitations, the modifiers are not as clear cut as they ideally would be. Ideally, each category would have its own single modifier, instead of, for example, having a mix of Alt and Ctrl in the /specific application/ category, and using a modifier combination like Ctrl+Shift for the /terminal application/.

I’ve also redefined all my Plasma and KWin shortcuts to be location-on-keyboard-based, but more on that later.

Pitfalls in PySide6

PySide6 is easy to use and powerful but there’s a few pitfalls to look out for. We’ll investigate a performance issue in a large data model, find out that function calls are expensive, and that some of Qt’s C++ APIs had to be adapted to work in a Python environment.

Continue reading Pitfalls in PySide6 at basysKom GmbH.

Sunday, 22 June 2025

A very long awaited milestone has been reached: Today the KMyMoney team announces the availability of the latest stable version of its Personal Finance Manager together with its companion library Alkimia..

Since the last stable release almost 3 years ago, the developers made 3440 changes to the main code base and 800+ changes to the Alkimia library.

Here’s an overview of some major functionality changes and improvements made among all the little bug fixes along the way (with more details on a separate page):

Multi account ledger view

KMyMoney now allows to open the ledger of multiple accounts in tabs side by side in the ledger view.

New and improved transaction editors

The transaction editors have completely rewritten. They now open a widget directly in the ledger area and there is no distinction between form based and register based method anymore. The sometimes confusing tabs showing Deposit/Transfer/Withdrawal have been removed and the amount entry now provides two mutually exclusive widgets for debit and credit. These changes also found their way into the split editor.

And for transfers you now simply type/select the account name in the category widget.

Customize tab order for transaction editors

Another feature of the transaction entry is to customize the tab order for data entry. Pressing Ctrl+Alt+T opens the tab order editor and the user can select the order of the widgets that are visited when pressing the TAB key.

Open categories in ledger view

With the new version, it is now possible to open categories in the ledger and enter transactions. This has been a long standing user request.

Customize order of columns in tabular views via drag and drop

The order of the columns of e.g. the ledger, accounts or categories view can now be modified by the user by simply dragging the header column to its new location.

Move accounts in hierarchy via drag and drop

Moving accounts in the hierarchy is now possible using drag and drop.

Load passwords from gpg encrypted password store

Passwords for e.g. the KBanking backend can now be loaded from the standard Unix password store pass with a simple mouse click. Pass uses strong GPG based encryption to store its information. A Qt based GUI frontend for pass is also available.

Improved handling of tags

The support for tags has been overhauled. Especially the reporting section for tags received many improvements.

Link documents to transactions

KMyMoney now provides a feature to link documents stored in the filesystem to transactions. This can be automated to support recurring transactions (e.g. your phone bill) by simple configuration using regular expressions per payee.

Online exchange rate download available for other finance apps

Online currency exchange rate and stock price download has been moved over to the Alkimia library and then re-integrated into KMyMoney. This makes it available for other applications by simply linking to Alkimia.

Updated handbook

The KMyMoney handbook has received many changes to reflect the new functionality.

A big thank you goes out to those who supported us by reporting problems and helping to identify their root cause. In case you have a question about the usage of some new features or even old ones, please post your question on the KDE user forum. If you are sure you found real problem or want to ask for a new feature, please do so on our bugtracker.

Tackling the Migration Agent

For week three, I finished resolving the configuration window issue for the EteSync resource by hiding the default configuration window and programmatically linking the wizard’s “Accepted” and “Rejected” states to the configuration window’s accept() and reject() methods. This ensured that the wizard cleanly replaced the built-in dialog without leaving a “zombie” window behind. I’ve submitted a merge request for these changes so it can be reviewed and integrated upstream.

With that resolved, I moved on to a new and intriguing component: the PIM Migration Agent. This agent is responsible for managing data migrations between different Akonadi versions or formats — a critical part of ensuring smooth transitions when updating KDE PIM components.

And like the other agents and resources, it was time for it to shed its QtWidgets dependency.


Decoupling the UI

Following the established pattern, I began by:

  • Creating a dedicated UI plugin for the migration agent’s configuration dialog

  • Removing the old configure() method from the agent’s core logic

  • Updating the relevant CMakeLists.txt files to support the plugin and cleanly separate UI code from the core agent

However, while this transition was relatively smooth, the plugin-agent communication needed more work to function correctly in this new structure.


Creating a D-Bus Interface for Plugin-Agent Communication

To enable proper communication between the configuration plugin and the migration agent, I created a new D-Bus interface:
org.kde.Akonadi.MigrationAgent

This interface allows the plugin to:

  • Receive status or configuration information from the agent

  • Send information back if needed (e.g., configuration changes)

To support this, I also:

  • Modified the CMakeLists.txt to include the interface and generate the corresponding D-Bus adaptor

  • Updated both the migrationagent and migrationstatuswidget files to use the new D-Bus interface for interaction

This ensures the plugin can communicate cleanly with the agent without relying on any hard-coded QtWidgets calls or tightly coupled logic.


The KUiServerJobTracker Problem (Still Pending)

While working on the migration agent, I encountered a significant QtWidget dependency:
KUiServerJobTracker, which handles job progress display by showing dialogs and notifications automatically.

Removing it is straightforward — but it leaves a gap:

How should the migration agent report progress to the user once KUiServerJobTracker is gone?

I’m currently exploring options for replacing it, possibly using a D-Bus-based mechanism where the agent broadcasts progress updates and a separate component (e.g., the plugin or a tray app) displays them. This would decouple the presentation layer from the agent’s logic, but I haven’t yet finalized the design.


What’s Next?

My immediate priority is to test the new plugin and the communication logic to ensure everything works correctly. In parallel, I’ll continue thinking through a robust replacement for KUiServerJobTracker, aiming for a modular, widget-free solution.

This week introduced new architectural challenges, but also laid the groundwork for cleaner, more maintainable agents. I’m excited to keep building on this momentum next week!

Saturday, 21 June 2025

It took a year for me to actually make a release, but KTimeTracker 6.0.0 is now out!

Major changes

  • The major thing about it is that KTimeTracker has been ported to Qt6. For end users this means up-to-date Linux distributions that had orphaned KTimeTracker will get the package back once a package maintainer steps up.

  • KTimeTracker has long had a (currently) X11-exclusive feature where it detects the virtual desktop you’re in and uses that to start/stop tracking a task. This does not work on Wayland and Windows, and now it won’t show up on either platform so you don’t attempt to use something that doesn’t work!

Wednesday, 18 June 2025

Tuesday, 17 June 2025

Car Game

This project began as a casual college game I developed in my second year, using Pygame. The idea was simple. You’re driving a car, and your job is to survive enemy attacks, collect energy to stay alive, and shoot down as many opponents as you can. The more you destroy, the higher your score.

The core gameplay loop was designed in Pygame and includes:

  • A player car that moves left and right.
  • Opponent cars that spawn and rush toward the player.
  • Energy pickups that keep your car alive.
  • Bullets using which you take down enemy cars.

Each component is managed by its respective class: MyCar, Opponent, Fire, and Explosion.

The original version used keyboard input for movement and shooting. The objective was to survive as long as possible while scoring points by destroying opponents.

While building the game, I found myself knee-deep in things I hadn’t anticipated—like why a car would randomly vanish mid-frame, or why every collision either did nothing or ended in total chaos. I spent hours tweaking bounding rectangles, trying to get explosions to appear in the right place, and making sure enemy cars didn’t spawn on top of each other. Most of my time went into figuring out how to reset things properly after a crash or making sure the game didn’t freeze when too many things happened at once. It was messy, confusing, and at times exhausting, but weirdly satisfying when everything finally came together.

Recently, I revisited this project with the idea of automating it. I wanted to see if the car could make its own decisions—to dodge, shoot, or stay put—all without human input. That’s where Monte Carlo Tree Search (MCTS) came in. Being a decision-making algorithm, it’s particularly useful in many strategic games when the search space is large and rewards are sparse or delayed—perfect for a chaotic survival game like mine.

Implementation Details

The first step was to abstract the game state into a simplified object. I created a GameState class in mcts_car_shooter.py that captures:

  • My car’s x position.
  • Remaining energy and current score.
  • Positions and energy levels of alive opponents.
  • Fire coordinates (optional) and energy pickup position.

This allowed the MCTS algorithm to run without needing to interact with the actual rendering or physics code.

In the main game loop, every 5 frames, I pass the current game state to the MCTS engine:

if frame_counter % 5 == 0:
    state = get_game_state_from_main(mycar, energy, score, list(opponent))
    action = mcts_search(state, computation_time=0.05)

The result is one of four possible actions: "left", "right", "shoot", or "none".

Once the decision is made, the game responds accordingly:

if action == "left":
    mycar.move("left")
elif action == "right":
    mycar.move("right")
elif action == "shoot":
    fire_sound.play()

So here’s what’s actually going on behind the scenes every time the AI makes a move. The MCTS algorithm starts by traversing the existing tree of game states to find the most promising node to explore—this is the selection step. Once it lands on that node, it simulates one new possible action from there, which is the expansion phase. From that new state, it plays out a few random steps of the game using a basic policy (like “shoot if you see enemies” or “don’t move if energy is low”)—this is the simulation part. And then finally, based on how well or badly that rollout went, it backpropagates the reward back up the tree so that decisions that led to good outcomes get reinforced and are more likely to be chosen in the future. Each loop tries to balance exploration (trying out new stuff) and exploitation (doing what’s already known to work), and this constant balance somehow ends up producing surprisingly smart behavior out of nothing but random simulations and reward math.

After integrating MCTS, the game now plays itself. The car intelligently avoids enemy fire, conserves energy, and shoots at the right moments. It’s not perfect—but it’s good enough to survive for a few minutes and rack up a decent score.

However, one limitation of the current setup is that the AI doesn’t retain any memory of past games—it starts from scratch every time the game restarts. The MCTS algorithm only simulates forward from the current state and doesn’t learn or adapt across episodes. So while it can make fairly smart decisions in the moment, it has no long-term strategy or evolving understanding of what works best over time. There’s no persistence of experience, which means it can’t build on previous runs to improve future performance. This makes it efficient for one-off decisions but not ideal for learning patterns or refining behavior over multiple plays.

Next, I’m planning to take things a bit further. I want to train a policy network on the trajectories generated by MCTS so the model can learn from past simulations and make better long-term decisions without needing to simulate every time. I’m also thinking of adding a simple GUI to visualize how the MCTS tree grows and changes in real time—because watching the AI think would honestly be super fun. And eventually, I’d like to give players the option to toggle between AI-controlled and manual play, so they can either sit back and watch the car do its thing or take control themselves. You can find the full implementation on my GitHub. Thanks for reading!