Friendship

I remember from when I joined my current $DAYJOB, the CTO looked me in the eyes and explained that I could choose my own work environment with a caveat: if I picked Windows the rest of the team would be instructed to always point a finger in my direction and laugh whenever they see me.

I don’t remember my exact reaction, but I was probably trying hard to suppress a smile, maintaining a poker face and replying with what I hoped was a casual “cool”.

Today I’m writing about one of those topics that can end a friendship like a favorite operating system, text editor or desktop environment.

So of course my favorite operating system is Fedora, but I’m not using Fedora Workstation because Gnome 3 drives me mad whenever I get to use it. I started with Gnome 2 and was very satisfied with it, but I would run into magic corners and other features that weren’t compatible with me. I tried Unity and actually enjoyed what they were pushing but again, small usability quirks that didn’t work for me on a daily basis eventually led to an accumulation of frustration that was unbearable for me. And incidentally I was using Unity at a previous $DAYJOB and didn’t have much of a choice.

So instead I quickly settled for Xfce. It hardly gets in my way, it’s sort of minimalist and yet featureful, and its overall architecture makes sense to me on paper: I have no experience with desktop environment development. Except that of course it also manages to drive me mad under certain very narrow circumstances when I’m giving a presentation or training a team: when I press Alt+Tab to cycle between open windows it shows up on the presentation screen or projector.

Please note that any harsh words are not directed to a group of people, but merely stating how I feel about software. Let’s stay on friendly terms, pretty please 😊.

(My) Xfce in Fedora

While I’m a big fan of Xfce, I think that the stock experience leaves too much to desire. Thankfully I have all I need on Fedora to fix everything I don’t like. For example I use the Whisker Menu and previously used Docky to replace the dock-like panel from the stock setup until it went defunct. Actually further than its retirement, I used it until it would block a major Fedora upgrade. That’s when I discovered Plank that thankfully worked as a drop-in replacement. Despite my distaste for Gnome, I still prefer their display manager over Xfce’s, so I happily take what works for me. I know, I’m praising the same folks who make a desktop environment that “drives me mad” to quote myself.

I switch themes every couple years if I manage to get enough bored to look for a new one so all in all Xfce despite a bad stock experience, it is with little customization effort that I got myself a very serviceable desktop environment that has pleased me for a while now, including some of the stock applications.

A year ago I tried the MATE desktop and found it amazing and a lot more polished than Xfce and yet it wasn’t enough to make me switch. I also ran into a lot of bugs when the Xfce maintainer for Fedora polled our mailing list to find whether we should follow the unstable 4.13 branch. A lot of bugs here means a couple, because yes, even running into a few bugs is more than I’m used to with Xfce. This was also their transition from GTK+ 2 to GTK+ 3, and others on the mailing list were less lucky and ran into many severe bugs.

But I digress here, now that Xfce 4.14 is available on Fedora 30, I’m saw what’s on the other side of the 4.13 tunnel and while it was pleasant to get the new features incrementally, I didn’t get one small change I’ve wanted for a few years already. I had a short window of free time where I felt I could look into that, and so I did.

Hacking Xfce (on Fedora)

To be clear, I’m mostly documenting this for myself just in case I revisit Xfce in the future. If you find this helpful, don’t hesitate to let me know.

Trial and (mostly) error

My first thought was to build my own RPM in /usr/local and change some systemd configuration to point to /usr/local/bin/xfwm4. I could easily revert that change without network access or further packaging update (likely a downgrade) if I ended up bricking my desktop.

In hindsight I’m really happy I couldn’t figure how my system knew which window manager to pick (if you know, feel free to email me) so I decided to look at Xfce’s documentation and found nothing really helpful. I did come across xfce-test and besides being in unfamiliar Ubuntu territory (I only realized later that a Fedora 29 branch exists) it also was denied execution by the default SELinux policy. I might shave this yak someday, but this was enough to convince me not to engage this route: besides pleasing SELinux I ultimately needed to also learn how to write automated tests.

My next step was to try inside a virtual machine, something I for some reason have trouble doing with a Fedora guest. I turned to my familiar tools, Vagrant and Virtual Box. A quick search taught me how to start a VM with multiple monitors (something I wasn’t even sure xfce-test supported) and looking through Vagrant’s atlas I found official Fedora images hosted by the Fedora Project itself (yay!) but the only one available was the Cloud (meh!) edition.

First success

I started a VM, struggled to install Xfce (more on that later), struggled again to install my modified copy of xfwm4, and then checked that it contained my modifications. At this point it was only an arbitrary string checked with the strings and grep utilities. So far so good, time to reboot the VM and check that this no-op change didn’t break everything. I’m prompted for a text mode login, and from there I have no idea how to actually start Xfce (much like I have no idea how xfwm4 is started). A quick search without leaving the VM and I quickly find the magic incantation:

sudo systemctl start graphical.target

And voilà, in my two VirtualBox windows the Xfce wallpaper shows up and after a couple eternities the desktop is fully functional! Time to automate the process, which is easily done with the following Vagrantfile:

$install_xfce = <<-EOF
dnf -y upgrade # somehow fails
dnf -y upgrade # succeeds
dnf -y group install 'Xfce Desktop'
EOF

Vagrant.configure("2") do |config|
	config.vm.box = "fedora/30-cloud-base"
	config.vm.provision "shell", inline: $install_xfce, privileged: true
	config.vm.provider :virtualbox do |vb|
		vb.gui = true
		vb.customize ["modifyvm", :id, "--monitorcount", "2"]
	end
end

You may notice that I needed to upgrade the package set twice to bring my VM up to speed. Initially I didn’t understand why, and I didn’t understand why I couldn’t install my xfwm4 package. The answer was unpleasant.

Fedora constrained

I should probably mention that I was slightly sleep-deprived as I was spending my first evening trying to crack this nut. I made my first visible change to the window manager and still didn’t realize that something was very wrong:

  • DNF crashed with a mere “Killed” in its output
  • Xfce took forever to start default applications
  • Once started the UI was functional but unresponsive

Several brain cycles later it occurred to me that the VM wasn’t fundamentally slow, it was just swapping like hell. A quick inspection shows defaults of 512MB of RAM and a single CPU with that Vagrantfile.

I find it sad that DNF is so resource hungry that it can’t both update its index and compute a transaction with limited resources. I’m wondering how the Fedora IoT project copes with that, maybe they use rpm-ostree instead. One more thing that happened with DNF is that I couldn’t install my modified package:

sudo dnf upgrade /vagrant/xfm4*.rpm

No matter what I would try, it would crash. So instead I opted for rpm -U and it happened almost instantly. DNF is way too hungry, and I understand that it adds value on top of RPM, but I can see the spectre of bloat looming above. DNF is a black hole: when RPM transactions enter time flies around them and resources get sucked in by its gravity.

Clunky workflow

Once I realized what was wrong, I was ready to start hacking in the source code. I wrote a small shell script that would build a make dist archive from source, build an RPM, upload it to the VM using SSH and upgrade the package. Then with a simple restart of the VM I could give a try to my changes. In this case I made a change in a label, just to validate the workflow. I updated my Vagrantfile to provision a usable system:

$install_xfce = <<-EOF
dnf -y upgrade
dnf -y group install 'Xfce Desktop'
EOF

Vagrant.configure("2") do |config|
	config.vm.box = "fedora/30-cloud-base"
	config.vm.provision "shell", inline: $install_xfce, privileged: true
	config.vm.provider :virtualbox do |vb|
		vb.gui = true
		vb.memory = 2048
		vb.cpus = 2
		vb.customize ["modifyvm", :id, "--monitorcount", "2"]
	end
end

At this point I don’t need to ask for an upgrade twice… I specifically don’t enable the graphical.target unit in systemd because I don’t know how to test the changes without a restart, so for a given VM I get one free try without a restart. In theory I only need to provision the VM once before I iterate with my changes, but in practice I rebuilt it from scratch a couple times before and after I was completely done.

The only problem with this workflow is that VirtualBox’s Linux kernel modules aren’t all part of the mainline releases, and because of Fedora’s policy on out-of-tree modules I get VirtualBox from RPM Fusion but I digress…

Working with xfwm4

This was not my first dive in the xfwm4 code base, I tried to look at this problem a couple years ago and didn’t get anywhere. I never really had enough spare time I was willing to allocate to this and it had already taken me some time to figure which one of the Xfce projects was responsible for the Alt+Tab window. This time I was ready to give it all my spare time until the problem was solved or until I’d throw the towel. After one evening spent on setting up a test environment, I was pleasantly surprised that it would only take me one evening to write a satisfying patch.

All out war

If a discussion about desktop environments or text editors can break a friendship, arguing about code style can lead to war. Even simple questions as indentation can become complicated beyond just spaces vs tabs, because I mean… How many spaces? Where do you align line continuations? For languages with a C-like syntax, where do you open curly brackets? Some code styles like FreeBSD’s simply troll you by having both spaces and tabs, open curly brackets both on the same or next line etc… And yet it’s the C code style I enjoy.

Thankfully, xfwm4 doesn’t follow the GNU code style for C, which I find deeply unreadable and that is probably a pain to write unless you have very good support in your text editor. I’m not fond of xfwm4’s style, and especially the inconsistencies across files, but it was fine.

Working without safety

In recent years I have been spoiled by my $DAYJOB where I work on a code base with extensive functional testing, and between 5 to 10% of the code being assertions shipped to production. This contributes to increasing my confidence in making changes, because many mistakes tend to manifest themselves quickly during the development process and in a helpful way. And of course once you build enough domain knowledge it becomes easier to deal with an increasingly familiar code base.

So leaving my comfort zone to work on a window manager did not bring much confidence to begin with. Building the code and then running make check doesn’t seem to do anything. One more reason why xfce-test wouldn’t help is that even if I built an SELinux module to deal with the denial I wasn’t even sure I could even deal with emulating multiple monitors.

And yet…

The code wasn’t that hard to browse and adding this feature only took a second evening, two in total. Working without my beloved seat belts (plenty of tests and assertions) turned out to be fine even though the project was throwing a lot of unfamiliar things at me: new code base, GTK+, Glade etc. In no time I was able to piece things together with reasonable confidence in the absence of automated and comprehensive testing. I need to stress this point: the GTK+ documentation was easy to browse (winking in the general direction of the Varnish project) and I found all my answers on the first try. I think I visited 3 pages of API descriptions in total, way to go!

Working on Fedora

One thing I love about Fedora is its packaging policy, and in general working with tooling that allows me to easily build and install my own packages and not feel that I’m throwing random files in random locations that I may forget about after a couple days.

I eventually installed the same RPM I was building for the development VM on my own system and it just worked fine also on bare metal. After submitting the patches upstream I decided to build the xfwm4 RPM that Fedora ships and simply incorporate them in the RPM spec. It didn’t work…

Being familiar with autotools, I quickly realized that a make dist archive wouldn’t consider the updated Glade files, since those are meant to be processed by maintainers. The solution was to --enable-maintainer-mode in the ./configure invocation and bring in the missing build dependencies. The point of processing them beforehand being that downstream maintainers don’t have to bother with architecture-independent build dependencies.

With that I was able to build a COPR repository and make it available for anyone to give it a try on Fedora very easily. Unfortunately I collected no test feedback, and the patches have been sitting in Bugzilla limbos ever since I submitted them, but it seems to be the case for a lot of xfwm4 tickets.

Future contributions

I’m still impressed by the tools we get for free and how it has become possible to easily test a change to an operating system without risking to brick a device. At least in this very case.

Packaging software for Fedora has taken me outside of my comfort zone more than I can remember, and after this first contact with Xfce code I think I may attempt more drive-by contributions.