Playing around with KDE and symlinks


Introduction #


Developing for the KDE ecosystem is pretty fun, and sometimes it leads to interesting discoveries through experimentation. Today we're gonna try something cursed: installing KDE components through symlinking, and the implications that follow from doing so.

The basics #


To clear out some common knowledge, it's well known that under the Linux desktop you can install components either locally or systemwide, such as icons, sounds, fonts, etc. This is done by dropping the appropriate stuff in either ~/.local/share or /usr/share for user and system installations respectively. This is standard practice, and KDE software follows this standard as well. So things like plasmoids, global themes, KWin effects and scripts end up being placed in the aforementioned directories when installed either manually or through kpackagetool6.

Nothing out of the ordinary so far, right? Now, here's where things get a little interesting.

Local overrides #


When KDE software tries loading packages installed like this, it does so by prioritizing locally-installed packages first. So, if it can find the package within ~/.local/share, it'll attempt to load that. If not, it'll go to /usr/share instead.

What this means is that the user can effectively take systemwide packages, install them locally (by moving them from /usr/share to ~/.local/share) and make changes to them, which would then be loaded first upon reloading Plasma and/or KWin, depending on what packages have been moved.

The following question naturally arises from this: What are all the things that we can install locally and expect them to work? Well, almost pretty much everything found in /usr/share/plasma and /usr/share/kwin.

There are probably more things that can be installed locally, but these two directories are the most practical, so they will be the focus of this post.

This has the interesting consequence of being able to locally install components that were never meant to be installed like that, much less modified afterwards. Things like the outline directory, which contains code that defines the window frame that appears when snapping windows to screen edges, and the shells directory, which has plenty of QML code for various aspects of the desktop shell.

Adding symlinks #


Perhaps the earlier section was also common knowledge among KDE power users out there, so let's get to the point and take this to the next logical step, which is to replace these locally installed packages with symlinks, and see what happens.

There are two main ways of approaching this:

  1. We link entire directories like plasmoids, shells, effects, etc. to the appropriate locations
  2. We link the actual packages individually
This initially came up as an idea for when I wanted to think of a way to install a good chunk of AeroThemePlasma in a way that automates things and makes the process of updating mostly boil down to doing a git pull. This idea has since been scrapped as it ended up being impractical and weird, especially when kpackagetool6 exists for most of the components relevant to this topic anyway.

The first one is straightforward; we just create a symlink which points to any desired directory like this:

This just seems to work completely fine, as expected. KDE doesn't seem to think anything is out of the ordinary with this. This works best for directories that don't have anything interesting going on for the end-user (like shells). As of the release of KDE Plasma 6.4, where KWin got split into KWin and KWin-X11, this method has the legitimate use case (possibly the only legit use case, lol) of symlinking the kwin folder found in ~/.local/share to kwin-x11 and kwin-wayland, since any code found there is limited to QML and JavaScript, and most of that code doesn't care about what display server you're using.

So this is cool and all, but this isn't particularly great for folders where things like plasmoids or KWin scripts are stored, since the user is likely to install a bunch of these over time. These packages shouldn't be installed in some arbitrary directory that could be located quite literally anywhere.

Individually symlinking packages #

Let's say that we want to perform some kind of symlinking in those cases anyway (suspend your disbelief for a moment). Going by the same principle as provided above, we get this:

From now on I'll only be discussing this topic in terms of plasmoids, as honestly I gave up on this rather esoteric idea a while ago. I imagine the same logic tracks for KWin scripts, but I'm not gonna attempt to confirm that claim.

Assuming all of these have already been placed in some kind of containment (panel or desktop), upon restarting Plasmashell everything seems to work correctly. The plasmoids have been loaded correctly! That is, until you try adding a plasmoid that hasn't been loaded into a containment beforehand. Most of the symlinked plasmoids are nowhere to be found in the menu for adding widgets. Not even kpackagetool6 recognizes their existence.

The only exception to this seems to be plasmoids that already exist in /usr/share/plasma/plasmoids, like built-in plasmoids. After loads of trial and error (more than what would be considered sane behavior by most), I ended up devising a workaround that would make the other invisible plasmoids show up again.

Setup

Let's take Simple Application Launcher as an example plasmoid:

The magic is shown on the bottom half of the image. Plasma requires there to be at least one true directory (as in, not a symlink) with the relevant plasmoid ID, and additionally a metadata.json file inside it, although that file can be a symlink as well. With this rather insane setup, the plasmoid can finally be viewed by the "Add widgets" menu and by kpackagetool6:


The caveat is that kpackagetool6 treats the plasmoid as a globally installed plasmoid, which makes sense given that it can only acknowledge its existence through this skeleton directory that we've created.

You can now unsuspend your disbelief, as it's easy to see how this only makes things more convoluted, with any hypothetical benefits not really justifying the weirdness of the setup required. This ended up being more of a mini-rabbit hole that was simply interesting to delve into to see how much you can "trick" Plasma.