
Speaking of manpages:
“Man pages are great, man readers are the problem”
https://whynothugo.nl/journal/2025/04/09/man-pages-are-great-man-readers-are-the-problem/
Speaking of manpages:
“Man pages are great, man readers are the problem”
https://whynothugo.nl/journal/2025/04/09/man-pages-are-great-man-readers-are-the-problem/
mandoc is nicer to read/write than the man
macro package and, most importantly, it’s semantic markup.
HTML output is a bit broken in GNU groff, though (OpenBSD on the left, GNU on the right):
https://movq.de/v/f1898e648f/s.png
🤔
Still, I’m inclined to convert my manpages to mandoc.
In 1996, they came up with the X11 “SECURITY” extension:
https://www.reddit.com/r/linux/comments/4w548u/what_is_up_with_the_x11_security_extension/
This is what could have (eventually) solved the security issues that we’re currently seeing with X11. Those issues are cited as one of the reasons for switching to Wayland.
That extension never took off. The person on reddit wonders why – I think it’s simple: Containers and sandboxes weren’t a thing in 1996. It hardly mattered if X11 was “insecure”. If you could run an X11 client, you probably already had access to the machine and could just do all kinds of other nasty things.
Today, sandboxing is a thing. Today, this matters.
I’ve heard so many times that “X11 is beyond fixable, it’s hopeless.” I don’t believe that. I believe that these problems are solveable with X11 and some devs have said “yeah, we could have kept working on it”. It’s that people don’t want to do it:
Why not extend the X server?
Because for the first time we have a realistic chance of not having to do that.
https://wayland.freedesktop.org/faq.html
I’m not in a position to judge the devs. Maybe the X.Org code really is so bad that you want to run away, screaming in horror. I don’t know.
But all this was a choice. I don’t buy the argument that we never would have gotten rid of things like core fonts.
All the toolkits and programs had to be ported to Wayland. A huge, still unfinished effort. If that was an acceptable thing to do, then it would have been acceptable to make an “X12” that keeps all the good things about X11, remains compatible where feasible, eliminates the problems, and requires some clients to be adjusted. (You could have still made “X11X12” like “XWayland” for actual legacy programs.)
“Wayland Will Never Be Ready For Every X.Org User”
Do I buy a new monitor or do I live with the burn-ins all the time? It’s getting annoying. When I edit images in GIMP, I have to double check if something is a pixel or a burn-in.
You know you’re getting old when there’s quite a few scripts in your ~/bin
that you use daily, but you haven’t edited them once in well over 10 years …
Here’s an example of X11/Xlib being old and archaic.
X11 knows the data type “cardinal”. For example, the window property _NET_WM_ICON
(which holds image data for icons) is an array of “cardinal”. I am already not really familiar with that word and I’m assuming that it comes from mathematics:
https://en.wikipedia.org/wiki/Cardinal_number
(It could also be a bird, but probably not: https://en.wikipedia.org/wiki/Cardinalidae)
We would probably call this an “integer” today.
EWMH says that icons are arrays of cardinals and that they’re 32-bit numbers:
https://specifications.freedesktop.org/wm-spec/latest-single/#id-1.6.13
So it’s something like 0x11223344
with 0x11
being the alpha channel, 0x22
is red, and so on.
You would assume that, when you retrieve such an array from the X11 server, you’d get an array of uint32_t
, right?
Nope.
Xlib is so old, they use char
for 8-bit stuff, short int
for 16-bit, and long int
for 32-bit:
That is congruent with the general C data types, so it does make sense:
https://en.wikipedia.org/wiki/C_data_types
Now the funny thing is, on modern x86_64
, the type long int
is actually 64 bits wide.
The result is that every pixel in a Pixmap, for example, is twice as large in memory as it would need to be. Just because Xlib uses long int
, because uint32_t
didn’t exist, yet.
And this is something that I wouldn’t know how to fix without breaking clients.
I was drafting support for showing “application icons” in my window manager, i.e. the Firefox icon in the titlebar:
https://movq.de/v/0034cc1384/s.png
Then I realized: Wait a minute, lots of applications don’t set an icon? And lots of other window managers don’t show these icons, either? Openbox, pekwm, Xfce, fvwm, no icons.
Looks like macOS doesn’t show them, either?!
Has this grown out of fashion? Is this purely a Windows / OS/2 thing?
Only figured this out yesterday:
pinentry
, which is used to safely enter a password on Linux, has several frontends. There’s a GTK one, a Qt one, even an ncurses one, and so on.
GnuPG also uses pinentry
. And you can configure your frontend of choice here in gpg-agent.conf
.
But what happens when you don’t configure it? What’s the default?
Turns out, pinentry
is a shellscript wrapper and it’s not even that long. Here it is in full:
#!/bin/bash
# Run user-defined and site-defined pre-exec hooks.
[[ -r "${XDG_CONFIG_HOME:-$HOME/.config}"/pinentry/preexec ]] && \
. "${XDG_CONFIG_HOME:-$HOME/.config}"/pinentry/preexec
[[ -r /etc/pinentry/preexec ]] && . /etc/pinentry/preexec
# Guess preferred backend based on environment.
backends=(curses tty)
if [[ -n "$DISPLAY" || -n "$WAYLAND_DISPLAY" ]]; then
case "$XDG_CURRENT_DESKTOP" in
KDE|LXQT|LXQt)
backends=(qt qt5 gnome3 gtk curses tty)
;;
*)
backends=(gnome3 gtk qt qt5 curses tty)
;;
esac
fi
for backend in "${backends[@]}"
do
lddout=$(ldd "/usr/bin/pinentry-$backend" 2>/dev/null) || continue
[[ "$lddout" == *'not found'* ]] && continue
exec "/usr/bin/pinentry-$backend" "$@"
done
exit 1
Preexec, okay, then some auto-detection to use a toolkit matching your desktop environment …
… and then it invokes ldd
? To find out if all the required libraries are installed for the auto-detected frontend?
Oof. I was sitting here wondering why it would use pinentry-gtk
on one machine and pinentry-gnome3
on another, when both machines had the exact same configs. Yeah, but different libraries were installed. One machine was missing gcr
, which is needed for pinentry-gnome3
, so that machine (and that one alone) spawned pinentry-gtk
…
Since Wayland compositors handle input devices on a lower level than X11 window managers, every compositor has to figure out on their own what a “mouse wheel click” is:
(I think “Wayland compositor” is a misnomer. They are full-blown display servers that also do compositing, plus Wayland window management, plus X11 window management.)
One can only hope that all this eventually gets moved into the wlroots library. (I’m not sure if that’s possible, nor if people would want that.)
I wore a Kubernetes shirt, in public, by accident, and now I feel dirty and ashamed. 😢
Xfce does one thing very right: It stores its settings in plain-text XML files. This allows me to easily read, track, and maybe even distribute these settings to other machines.
(Unlike GNOME’s dconf, which uses some binary file format. Fun fact: The older and now deprecated gconf also used XML files.)
Nooooooooooooooooooooooooooooooooooooooooooooooooooooooo, the doctors have started using AI voice agents and they understand jack shit. 😭😭😭
HTTP referrers are quite broken, aren’t they?
Because of that recent storm on my blog, I had a peek at them. There’s a lot of garbage in there. For example, https://docs.freebsd.org/en/books/handbook/disks-virtual.html is supposed to refer to one of my blog posts …
What’s going on here?
AI this, AI that.
Tech is no longer interesting. I need to find a new field.
Thinking about doing “Wayland Wednesday”. Only use Wayland every Wednesday. Collect bugs, report bugs, fix bugs.
PSA: setpriv
on Linux supports Landlock.
If this twt goes through, then restricting the filesystem so that jenny can only write to ~/Mail/twt
, ~/www/twtxt.txt
, ~/.jenny-cache
, and /tmp
works.
Something happened with the frame rate of terminal emulators lately. It looks like there’s a trend to run at a high framerate now? I’m not sure exactly. This can be seen in VTE-based terminals like my xiate or XTerm on Wayland. foot and st, on the other hand, are fine.
My shell prompt and cursor look like this:
$ █
When I keep Enter pressed, I expect to see several lines like so:
$
$
$
$
$
$
$ █
With the affected terminal emulators, the lines actually show up in the following sequence. First, we have the original line:
$ █
Pressing Enter yields this as the next frame:
$
█
And then eventually this:
$
$ █
In other words, you can see the cursor jumping around very quickly, all the time.
Another example: Vim actually shows which key you just pressed in the bottom right corner. Keeping j
pressed to scroll through a file means I get to see a j
flashing rapidly now.
(I have no idea yet, why exactly XTerm in X11 is fine but flickering in Wayland.)
The WM_CLASS
Property is used on X11 to assign rules to certain windows, e.g. “this is a GIMP window, it should appear on workspace number 16.” It consists of two fields, name
and class
.
Wayland (or rather, the XDG shell protocol – core Wayland knows nothing about this) only has a single field called app_id
.
When you run X11 programs under Wayland, you use XWayland, which is baked into most compositors. Then you have to deal with all three fields.
Some compositors map name
to app_id
, others map class
to app_id
, and even others directly expose the original name
and class
.
Apparently, there is no consensus.
QEMU on Wayland unusable, because it can’t grab the mouse … I’ll add it to my TODO list and investigate/report it eventually.
The lack of suckless-like simple, hackable software these days is appalling.
The Linux installation on my main PC turned 14 today:
$ head -n 1 /var/log/pacman.log
[2011-07-07 11:19] installed filesystem (2011.04-1)
I bought the “remastered” versions of Grim Fandango and Forsaken on GOG, because they’re super cheap at the moment. Both have native Linux versions.
And both these Linux version crap their pants. 🫤 The bundled SDL2 of Forsaken says it “can’t find a matching GLX visual” and I couldn’t figure out how to fix that. I didn’t spend a lot of time on Grim Fandango.
Both work great in Wine. 🤦
(I do have the original version of Grim Fandango from the 1990ies, but that one does not work so well in Wine. I figured, if it’s so cheap, why not. And I now get to play the english version. 😃 The german dub is pretty damn good, actually, but I always prefer the original these days.)
Okay, now this is a very interesting Rust feature:
https://blog.rust-lang.org/2025/07/03/stabilizing-naked-functions/
This (and inline assembly) makes Rust really interesting for very low-level stuff. 🥳
It took about a year, I think, but I’ve now finished another run of Tomb Raider I, II, and III. And I have, for the first time, played the two bonus packs “Unfinished Business” (for TR I) and “Golden Mask” (for TR II). They’re available as a free download, if you have the original games. (The bonus pack for TR III is not free.)
I just love these games – and the game mechanics. It’s just the right balance between challenging and relaxing.
What kind of half-assed nonsense is this? They only broadcast half of the current european soccer cup … (Let me guess, I’m supposed to subscribe to some streaming service if I want to watch every game, right?)
This aggressive auto-logout on my bank’s website …
Dude, you want me to print something, sign it, and scan it back in. This takes forever and I’ll have to re-login a dozen times. Narf.
TIL: The logo of sudo
is a sandwich. 🫠 https://www.sudo.ws/
Ted Unangst’s snarky (and entertaining) remarks this month:
Someone did a thing:
https://social.treehouse.systems/@ariadne/114763322251054485
I’ve been silently wondering all the time if this was possible, but never investigated: Keep doing X11 but use Wayland as a backend.
This uses XWayland’s “rootful” mode, which basically just gives you a normal Wayland window with all the X11 stuff happening inside of it:
https://www.phoronix.com/news/XWayland-Rootful-Useful
In other words, put such a window in fullscreen and you (more or less) have good old X11 running in a Wayland window.
(For me, personally, this won’t be the way forward. But it’s a very interesting project.)
Just realized: One of the reasons why I don’t like “flat UIs” is that they look broken to me. Like the program has a bug, missing pixmaps or whatever.
Take this for example:
https://movq.de/v/8822afccf0/a.png
I’m talking about this area specifically:
https://movq.de/v/8822afccf0/a%2Dhigh.png
One UI element ends and the other one begins – no “transition” between them.
The style of old UIs like these two is deeply ingrained into my brain:
https://movq.de/v/8822afccf0/b.png https://movq.de/v/8822afccf0/c.png
When all these little elements (borders, handles, even just simple lines, …) are no longer present, then the program looks buggy and broken to me. And I’m not sure if I’ll ever be able to un-learn that.
We’re entering the “too hot to think”-season in 3, 2, 1 … and we’re live!
I did a “lecture”/“workshop” about this at work today. 16-bit DOS, real mode. 💾 Pretty cool and the audience (devs and sysadmins) seemed quite interested. 🥳
The 8086 / 16-bit real-mode DOS is a great platform to explain a lot of the fundamentals without having to deal with OS semantics or executable file formats.
Now that was a lot of fun. 🥳 It’s very rare that we do something like this, sadly. I love doing this kind of low-level stuff.
Saw this on Mastodon:
https://racingbunny.com/@mookie/114718466149264471
18 rules of Software Engineering
- You will regret complexity when on-call
- Stop falling in love with your own code
- Everything is a trade-off. There's no "best" 3. Every line of code you write is a liability 4. Document your decisions and designs
- Everyone hates code they didn’t write
- Don't use unnecessary dependencies
- Coding standards prevent arguments
- Write meaningful commit messages
- Don't ever stop learning new things
- Code reviews spread knowledge
- Always build for maintainability
- Ask for help when you’re stuck
- Fix root causes, not symptoms
- Software is never completed
- Estimates are not promises
- Ship early, iterate often
- Keep. It. Simple.
Solid list, even though 14 is up for debate in my opinion: Software can be completed. You have a use case / problem, you solve that problem, done. Your software is completed now. There might still be bugs and they should be fixed – but this doesn’t “add” to the program. Don’t use “software is never done” as an excuse to keep adding and adding stuff to your code.
Okay, here’s a thing I like about Rust: Returning things as Option
and error handling. (Or the more complex Result
, but it’s easier to explain with Option
.)
fn mydiv(num: f64, denom: f64) -> Option<f64> {
// (Let’s ignore precision issues for a second.)
if denom == 0.0 {
return None;
} else {
return Some(num / denom);
}
}
fn main() {
// Explicit, verbose version:
let num: f64 = 123.0;
let denom: f64 = 456.0;
let wrapped_res = mydiv(num, denom);
if wrapped_res.is_some() {
println!("Unwrapped result: {}", wrapped_res.unwrap());
}
// Shorter version using "if let":
if let Some(res) = mydiv(123.0, 456.0) {
println!("Here’s a result: {}", res);
}
if let Some(res) = mydiv(123.0, 0.0) {
println!("Huh, we divided by zero? This never happens. {}", res);
}
}
You can’t divide by zero, so the function returns an “error” in that case. (Option
isn’t really used for errors, IIUC, but the basic idea is the same for Result
.)
Option
is an enum. It can have the value Some
or None
. In the case of Some
, you can attach additional data to the enum. In this case, we are attaching a floating point value.
The caller then has to decide: Is the value None
or Some
? Did the function succeed or not? If it is Some
, the caller can do .unwrap()
on this enum to get the inner value (the floating point value). If you do .unwrap()
on a None
value, the program will panic and die.
The if let
version using destructuring is much shorter and, once you got used to it, actually quite nice.
Now the trick is that you must somehow handle these two cases. You must either call something like .unwrap()
or do destructuring or something, otherwise you can’t access the attached value at all. As I understand it, it is impossible to just completely ignore error cases. And the compiler enforces it.
(In case of Result
, the compiler would warn you if you ignore the return value entirely. So something like doing write()
and then ignoring the return value would be caught as well.)
We really are bouncing back and forth between flat UIs and beveled UIs. I mean, this is what old X11 programs looked like:
https://www.uninformativ.de/desktop/2025%2D06%2D21%2D%2Dkatriawm%2Dold%2Dxorg%2Dapps.png
Good luck figuring out which of these UI elements are click-able – unless you examine every pixel on the screen.
Speaking of Wine, Arch Linux completely fucked up Wine for me with the latest update.
Arch is shipping a WoW64 build now, which is not yet ready for prime time.
And then I realized that there’s actually only one stable Wine release per year but Arch has been shipping development releases all the time. That’s quite unusual. I’m used to Arch only shipping stable packages … huh.
Hopefully things will improve again. I’m not eager to build Wine from source. I’d rather ditch it and resort to my real Windows XP box for the little (retro)gaming that I do … 🫤
To really annoy my neighbors and everyone in a 5 mile radius, I might take my Model M and type a blogpost on the balcony. 😈
Fuck me sideways, Rust is so hard. Will we ever be friends?
OpenBSD has the wonderful pledge()
and unveil()
syscalls:
https://www.youtube.com/watch?v=bXO6nelFt-E
Not only are they super useful (the program itself can drop privileges – like, it can initialize itself, read some files, whatever, and then tell the kernel that it will never do anything like that again; if it does, e.g. by being exploited through a bug, it gets killed by the kernel), but they are also extremely easy to use.
Imagine a server program with a connected socket in file descriptor 0. Before reading any data from the client, the program can do this:
unveil("/var/www/whatever", "r");
unveil(NULL, NULL);
pledge("stdio rpath", NULL);
Done. It’s now limited to reading files from that directory, communicating with the existing socket, stuff like that. But it cannot ever read any other files or exec()
into something else.
I can’t wait for the day when we have something like this on Linux. There have been some attempts, but it’s not that easy. And it’s certainly not mainstream, yet.
I need to have a closer look at Linux’s Landlock soon (“soon”), but this is considerably more complicated than pledge()
/unveil()
:
“Learn Something Old Every Day, Part XV: KEYB Is Half of Keyboard BIOS”
https://www.os2museum.com/wp/learn-something-old-every-day-part-xv-keyb-is-half-of-keyboard-bios/
fn sub(foo: &String) {
println!("We got this string: [{}]", foo);
}
fn main() {
// "Hello", 0x00, 0x00, "!"
let buf: [u8; 8] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x00, 0x21];
// Create a string from the byte array above, interpret as UTF-8, ignore decoding errors.
let lossy_unicode = String::from_utf8_lossy(&buf).to_string();
sub(&lossy_unicode);
}
Create a string from a byte array, but the result isn’t a string, it’s a cow 🐮, so you need another to_string()
to convert your “string” into a string.
I still have a lot to learn.
(into_owned()
instead of to_string()
also works and makes more sense to me, it’s just that the compiler suggested to_string()
first, which led to this funny example.)
So I was using this function in Rust:
https://doc.rust-lang.org/std/path/struct.Path.html#method.display
Note the little 1.0.0
in the top right corner, which means that this function has been “stable since Rust version 1.0.0”. We’re at 1.87 now, so we’re good.
Then I compiled my program on OpenBSD with Rust 1.86, i.e. just one version behind, but well ahead of 1.0.0.
The compiler said that I was using an unstable library feature.
Turns out, that function internally uses this:
https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.display
And that is only available since Rust 1.87.
How was I supposed to know this? 🤨
Gopher server is back online and I’ll be phasing out Mastodon.
gopher://uninformativ.de
(No, I won’t do multi-protocol twtxt again. 😅)