This is basically my manifesto of why Linux sucks and I keep using Windows as a desktop OS. This is both as a developer platform and end-user targeting.
Look: I would love to be able to use Linux as a daily driver. KDE is amazing and they clearly put far more effort into the desktop experience/UI than Windows (just the volume mixer alone). There are simply far too many underlying and infrastructural problems to the Linux desktop that none of KDE's great UI changes can make up for. I want Linux fanboys, developers, etc... to stop sticking their damn head in the sand about these issues and admit that Linux is still decades behind in some basic infrastructure. This shit can't get fixed if people refuse to admit it's broken in the first place, which some people are far too happy to do.
Windows has far better desktop apps than Linux, and thanks to WSL, I have all the CLI apps of Linux too. While I do believe KDE Plasma is a much better desktop environment than Windows 10, the app ecosystem on Windows beats Linux into the ground hard. This goes for normal poweruser stuff aswell as programmer-oriented. I can't provide a perspective on non-poweruser/non-technical people because, well, I'm not one of those. Coming up with lists like these is hard because I take everything here for granted now, so if I remember this document exists this list will expand a lot.
The common thread here between all of these tools I will be listing is that all "alternatives" you can have on Linux have 10% of the features at best, or are way more cumbersome to use. Getting any more than that probably involves anything from shell scripting and awkward command line tools to trying to interpret /proc.
For poweruser tools, here are some of my favorite that have literally no comparable analogue on Linux:
- ShareX: Yeah, there's screenshot utilities, but those are all a bad joke compared to the impressively useful feature set of ShareX. You'll never know how useful easy-to-access OCR is until you need it and remember it exists.1 All "alternatives" proposed for Linux are cumbersome to use, don't have 10% of the feature set, far more complex to set up with hotkeys and all that, etc...
Programmer tools are even worse here. While on Windows most of your functionality will be accessible through GUI tools, on Linux often the best you have is some curses-based terminal tool. Bad tools are gonna be bad tools, but good GUI tools will destroy anything that is "good" by CLI standards. The general impression I get is that Linux users put up with subpar commandline development tools, while Windows devs get far-superior GUI tools. If poweruser/casual stuff was all of it, I wouldn't even have this whole header here because obviously a large part of the problem is just desktop marketshare. But I am not sure how much that holds up when my biggest problem here is developer tools, not poweruser/casual stuff. You would think that Linux, an OS often associated with developers, would have better tools here. But nope, that's not the case.
Programmer tools:
- Process Hacker: it's htop on steroids.
- Debugging tools: the state of the art for Linux here is still GDB/LLDB or barely functional wrappers around it. WinDBG and many other tools like x64dbg are way ahead and have functional UIs.
- Event Tracing for Windows (ETW) and accompanying tools like WPA
- DependenciesGui/Process Hacker's peview.exe: I hate having to use a vague combination of
nm
,ldd
, etc... with barely rememberable flags. These tools are much more well-organized and easier to use.
I would like to give honors to Powershell, which is now cross-platform so technically doesn't fit on this list (and yes, I use it as daily-driver Linux shell). But it's still a Windows-centric Microsoft invention that kicks the teeth out of any Linux shell, in my humble opinion.
I also need to clarify that, because I mostly work in C#/.NET, some of the tools I use for that are Windows-only (PerfView comes to mind). I recognize that this is not Linux' fault however so will not be listing them here. I am happy to report that Rider is a VERY good developer experience for C# on Linux however.
DPI scaling is the act of scaling an app up by factors like 150% to make UI elements have the correct size on devices with different DPI factors. A typical "100%" desktop monitor is generally ~96 DPI (number used by Windows and some X11 apps), while macOS generally used to use ~72 DPI as baseline IIRC. If you have a 1080p 15" laptop monitor, that is closer to 125% DPI scale for Windows, so all UI elements need to be 125% bigger in pixel size to have the same size on a typical desktop monitor compared to the aforementioned laptop. "Fractional" refers to allowing apps to scale non-integer amounts (125%) because doing purely integer amounts is easier than fractional.
Fractional DPI scaling is critical, since many configurations (especially laptops) need it to have the UI be sized appropriately. Microsoft has realized this technology is necessary for more than a decade now. Windows Vista (2007) had fractional DPI scaling on a per-system basis (Windows XP allowed changing font size DPI but other app elements didn't scale with properly). WPF (2006) was designed with vector drawing in mind and, as such, is fully fractional DPI scalable. They also managed to retrofit old win32 apps to support fractional DPI scaling, with some caveats. More modern Windows versions have made various incremental improvements along the ways and fractional DPI scaling works very well: multimonitor DPI scaling (different monitors can have different scale factors, and moving apps between automatically adjusts the app's scale factor to match), proper handling of apps that are not DPI-aware on various levels (scaling them up blurrily in compositor; better than nothing), allowing per-app overrides for this, etc... Basically it works great if you don't mind the occassional app old app being broken.
Apple couldn't be arsed and instead of implementing proper fractional DPI scaling in their toolkits, they just started shipping "retina" displays with all their hardware (because, you know, Apple exclusively controls both their own hardware and software they ship). These retina displays are more than 2x the DPI of most typical hardware from other (lower cost) vendors. For fractional scaling, macOS just draws apps at 2x internally and uses the compositor to scale it. Again, they can get away with this because their displays are so damn high-DPI that you can't notice. They also had to break a few eggs and kill features like subpixel font rendering for this, and to this day macOS' font rendering is trash on low-DPI displays.2 Basically it works great if you only ever buy 4-figure 5K external monitors from Apple.
Where is Linux on this? basically nowhere, and it's actively regressing.
X11 reported DPI to applications and some apps were able to take advantage of the raw DPI number. There was no compositor-level support for scaling unaware apps, apps generally sucked at implementing it, no multimonitor DPI, etc...
Wayland decided that integer DPI scaling is all you're gonna get. At least it has multi-monitor DPI and compositor handling now. Yes. This means my 1080 15" laptop (which isn't an uncommon configuration) will not have fractional DPI scaling. All apps will be blurry nightmares by being downscaled by the compositor.
A huge part of this is because UI toolkits do not support fractional DPI scaling. It's 2022 and GTK/GNOME actively refuses to acknowledge that fractional DPI scaling is even necessary. It's been 15 years since WPF came out, with full fractional scaling support. GNOME/GTK/System76 developers actually intend to let integer scaling + compositor downscaling be the solution for fractional DPI scaling on Linux. Saying "Apple does it like this, it's the correct solution". As I have stated before, this is complete horseshit because Apple only gets away with this because they butchered their font stack and started shipping retina displays in everything. I don't know what the plan for System76's hardware line is but GNOME developers cannot take this shit for granted.
Qt has some support for DPI scaling since ~2016 but fractional DPI scaling, while somewhat implemented, is not officially supported. In my experience using KDE's apps on Windows it mostly works, but there are some problems which seem to stem more from themes and such. I hope it may be possible to fix this on KDE's side, and to eventually enable it for real on Wayland.
All in all, the situation here is bleak if KDE developers never get a proper fractional DPI scaling protocol into Wayland/their themes and GNOME/Sys76 developers keep larping as a more incompetent form of Apple.
UPDATE: a fractional scaling protocol is in the works. I expect GNOME to never properly support it.
Linux' graphics infrastructure is held together by strings and duct tape. And no, that's not because "nvidia is bad". Wayland clearly had no trouble leaving Nvidia behind when they refused to support GBM, so Nvidia is not holding us back here. The APIs involved are decades behind and just awful.
OpenGL and everything surrounding it is a terrible API. I don't know if you've ever had the misfortune of using it, but D3D and DXGI on Windows are so much nicer and functional in every way. DXGI originates from 2006 (Windows Vista/D3D10) and OpenGL is still a nightmare in comparison. EGL is such a pile of garbage, I hate having to use it. Did they really need separate extensions for "the concept of GPUs exists", "you can enumerate GPUs", "you can get info about enumerated GPUs"? Complete nightmare to work with. And the extensions are inconsistently implemented and there's no writing on what is available where. DXGI just fucking works and does all of this in a clear API doc on docs.microsoft.com with a 100% guaranteed of what APIs will be available where.
Vulkan fixes most of the problems with EGL being awful, but then has more of its own such as old hardware support. On Windows, I can use D3D11 and support 10-15 years of hardware (depending on minimum FL). The API will be 10x more sane than the nightmare that is OpenGL, is more than sufficient for my use cases, and far more compatible than Vulkan (you get like 6 years of hardware support on Vulkan Windows, for intel iGPUs). It will have functional and modern WSI (window system integration) which is not the case with OpenGL. Etc... I am genuinely considering making a D3D11 renderer for my cross-platform game because of all the advantages it would provide to Windows players, despite obviously leaving mac/Linux users in the dust. I also have no need for the huge added complexity of Vulkan, so a Vulkan renderer would be harder to develop.
Windows' graphics stack is also much more robust. On Windows, I can force all (all, I am on a laptop) my GPUs to be reset (dxcap -forcetdr
) and I get about 2-3 second of black monitor and almost all apps except games stay open without a hitch. On Linux you would currently be logged out or have to reboot your system. This is also how you can update GPU drivers on a live system without trouble. A lot of the problem here is that OpenGL was never designed for this (defaults to trying to hide device loss errors, which doesn't work with a modern graphics API model at all).
Jesus Christ I have to talk about this joke of a protocol. See, on Linux there's currently two protocols for doing stuff like opening windows, receiving keyboard input, and so on:
- X11, which dates back even older than Windows.
- Wayland, which is the new up and coming thing.
X11 is extremely old and crusty. So old that at some point it was decided that it would be better to re-invent the whole goddman wheel and start from scratch3. And now we have Wayland.
Because Wayland is new, of course, they decided to try to "fix" god damn everything. So now the whole thing is engineered on a basis of privacy and sandboxing and all that goodness. This means many fundamental things all other OSes allow like apps moving your cursor around are now illegal, and they're effectively working backwards towards basic functionality even X11 has. But uh, more secure I guess.
It's a fractured nightmare of extensions and protocols. The whole standards process is slow as molasses and basic functionality gets bikeshedded about for years.
Some of the hilarious4 bullshit that comes out of this. I'm sure there's going to be more:
-
GNOME effectively does not implement Wayland as a public protocol. See heading down below.
-
Wayland surface suspension troubles
- Wayland compositor blocks client apps if they're minimized. No other windowing system does it like this.
- This leads to terrible hacks as programs try to work around this insane behavior.
- The proposal to fix this nightmare is filled with highlights such as:
- Blaming all games for being terrible software or something nonsensical like that.
- This is an intentional feature to save power, we can't trust the apps to do this themselves (despite many apps like Firefox having worse power use due to inability to suspend video decode).
- Alternative proposals that don't fix the core problem.
- More blaming games.
- A Valve employee getting the Vulkan spec updated to make this behavior illegal, therefore hard-forcing this issue to get fixed sanely eventually.
- Arguments about privacy, saying that apps should not be allowed to know.
-
Wayland doesn't have a protocol for standard mouse cursors. This means that if an app wants to show "vertical resize bar" that matches your OS cursor theme, you have to dig through a ton of wild desktop-specific locations to load the correct bitmap and upload it directly.
-
Wayland's clipboard protocol requires the copied-from app to remain active or the clipboard will be lost. This is because clipboard content is only ever sent app-to-app via an IPC pipe, and there is no system for the data being saved if the app closes. Microsoft figured this out decades ago: copying small data is just stored by the OS somewhere, whereas large data copies can be sent app-to-app on paste with the app prompting "you will lose clipboard data" if closing it would lose the clipboard.
- A command-line "copy to clipboard" utility has to run a background daemon to hold the clipboard alive. Christ.
Yes, of course GNOME implements it, but not in a way anybody can actually fucking use.
This is quite evident from the following issue: GNOME doesn't support server-side decorations. This is a basic requirement of a windowing API, I want to be able to open a window and customize the contents, while the window border looks appropriate for the OS the user is running. This works on Windows, macOS, X11, Wayland KDE... and not Wayland GNOME. There's no protocol.
See, GNOME actually says you should just GTK. Full stop. Use GTK. People pointing out you can't make a Vulkan context in GTK? Crickets. Shit I'd be horrified if I had to interop with a library like GTK for my game's windowing layer. Use GTK. This is of course complete bullshit in every way: GNOME is literally telling people they shouldn't use Wayland and shouldn't use GTK. So GNOME doesn't publically support using Wayland, it's just an internal protocol GTK uses to communicate to their compositor.
Wow, that's good. I should start throwing the above claim out as an excuse to link this document to people more!
Linux uses ELF for binaries and dynamic linking. Unlike Windows' PE32, ELF does not keep track of which symbols are imported from which modules. That means that unlike on Windows, where foo.dll
imports memcpy
from VCRUNTIME140.dll
or what have you, on Linux libfoo.so
depends on libc.so
and needs memcpy
from anywhere. Symbols on Linux are process-global because of how the linkage model works, and this is a massive nightmare for forwards compatibility if you are shipping applications. Example down below.
This can already cause dumb linking bugs on its own. Static linking of dependencies has to be done carefully to avoid accidentally exporting them and conflicting with other dependencies that may be loaded. Etc...
To make matters worse, there is no direct syscall interface library like Windows' KERNEL32.dll
. libc
is both the expected kernel API interface on the system, and the sole C runtime library. The C runtime library is an implementation detail of the programming language you are using to build your app, and should be nothing else. Combining these into one library is massively problematic, because you cannot load multiple libc
s into a process (due to the aforementioned ELF-sucks linking problems), but you need to be able to do that for forward and backwards compatibility, versioning, and encapsulation of dependencies (since libc
is an implementation detail of your C compiler, NOT a system API). Windows can do this: you can have multiple C/C++ runtimes loaded into the process (although it's ill-advised for performance reasons, it can happen) and stuff just works. KERNEL32.dll
contains the actual low-level kernel APIs like CreateFile
and VirtualAlloc
, while the C/C++ runtime libraries contain malloc
, memcpy
, open
, etc...
Why do you need to be able to load multiple C/C++ runtimes into the process? Flatpak. The point of Flatpak is to provide a consistent base for applications. One of the libraries of that consistent base is libc
. You know what else needs to be loaded by Flatpak apps? Your graphics driver. Guess what your graphics driver userspace libraries depend on? Flatpak works around this for Nvidia drivers by keeping a copy of the nvidia drivers that has to be separately updated and versioned from your system's drivers. As expected, this quickly breaks down, because Nvidia requires the user-space part of the driver to match the kernel-level version. However much people on Reddit and Twitter bash Nvidia for this, This is not Nvidia's fault for having a proprietary driver, having to duplicate the userspace video driver is insane whether it's Nvidia or Mesa. It's Linux' fault for having a fundamentally fucked dynamic linking model compared to Windows. I can only imagine the coming bullshit when "Discord doesn't work with the new AMD GPU because the Mesa in the Discord flatpak is too old".
My understanding is that libcapsule
aims to be a solution here by allowing proper dependency encapsulation, and it's used by Steam's Linux Runtime/pressure vessel stuff (which is a far saner way to do it than Flatpak). I know of at least one distro which basically just loads the host OS graphics drivers into flatpak directly with no capsuling or anything which sounds like a compat nightmare but well not my problem.
And no, directly syscall
ing Linux is a bad solution that should not be necessary. Not to mention how difficult that is because, again, libc
is both the syscall interface and the C runtime library. There are many nightmares this can cause because libc is a control freak. For example: if you never use pthread to make new threads, std::shared_ptr
will be non-atomic because "well, there's no extra threads, doesn't need to be!". So creating threads without libc
's pthread is unsafe and can cause bugs. Not great if you're going for compatibility. This also means you can't static link libc. I am not aware of any official glibc documentation for where it is legal to bypass glibc (directly syscalling). I'll concede I haven't looked, but knowing the glibc devs there isn't any, and you should not rely on them to not eat your face for using syscall without their consent.
This is absolutely never getting fixed at this point and it's gonna keep being a huge problem forever. Joy. It's not a problem on Windows because KERNEL32.dll
and the C/C++ runtime are separate, and PE32 linking actually specifies which module (dll) to load symbols from.
glibc 2.35 (feb 2022) shipped a new dependency sorter or whatever. It is (at the time of writing) extremely buggy causing anything from angry valgrind, assert aborts, erroneous failing library loading behavior, to straight segfaults. All in ld.so
. I had to spend a whole weekend debugging this, and as far as I could tell I was the first to find this. Then after another week of Linux users crashing in various new scenarios I said "fuck it" and passed a workaround env var from the launcher.
Look, bugs happen, but this is literally the lowest and most fundamental component in the system above the kernel. This leaves an extremely sour taste in my mouth, you know?
So glibc 2.36 came out and it broke EAC. Why did this happen? Well because glibc changed the default compiler options to remove DT_HASH
structures from their ELF files in favor of exclusively their own DT_GNU_HASH
structures.
DT_HASH
is an entry in an ELF file to accelerate symbol lookups. It is standardized in the ELF specification and mandatory. It's not technically optimal, so around 2006 GNU came up with DT_GNU_HASH
which is supposed to be better. Files can contain both, and if available glibc will use DT_GNU_HASH
to go faster. Great! Glibc removed their DT_HASH
structures (by just changing a linker option) from their own ELFs and this broke EAC (and some other software).
Some people try to lay blame on EAC for "why are they using this ancient format" but uh:
DT_GNU_HASH
was never officially documented or standardized anywhere, except "look at the source code" (which is under (L)GPL by the way).DT_HASH
is plenty documented on the other hand.DT_HASH
is literally standardized as being required in the ELF format.- GNU amd LLVM's linker only started adding
DT_GNU_HASH
by default to compilations since 2018 (of course software still included both for way longer if they manually passed the flags).
If you're the EAC dev and you need to manually parse ELF files as a tamper check, which would you pick?
Why did glibc change this default by the way? Oh yeah, to save 16 kilobytes of the binary. Amazing savings!
When shit started hitting the fan, people in the glibc bug reports of course started pulling out arguments like "EAC's devs should just support the newer standard! It's existed for 15 years already!". Read above to see what the problem with that nonsensical counter argument is.
glibc should not be breaking ABI like this. I'm sure they didn't expect this to break ABI so mistakes happen, but they should have instantly reverted it as soon as it did instead of doubling down. All for 16 kilobytes.
Bonus points: claiming DT_GNU_HASH
is documented by linking to your own email from 15 years ago, which says it isn't
Outside of the obvious jerk that Windows wastes memory by being bloated compared to most Linux distros, Windows has far superior memory management for desktop use.
Windows has no overcommit crap, so there's no OOM killer nonsense either. All memory can be backed either by physical RAM or by page file. It is also much better at swap management. If on Linux you run out of memory and the OOM killer doesn't fire its blindfolded drunk shotgun in time, your system is probably gonna completely grind to a halt and has to be REISUB'd. Windows has much better swap management and isn't as prone to this. As far as I know, Linux still cannot have memory compression together with disk swap at the same time. While you can set up multiple swap devices (one for memory compression, one for disk swap), the kernel cannot intelligently move data between these (basically, it fills up memory compression then starts dumping stuff to disk). Windows and macOS can do this no problem. Windows can also manage swap prioritization and such based on desktop use (e.g. prioritizing active window, deprioritizing minimized windows). Good luck getting this on Linux, System76 only just came out with their script for CPU prioritization based on active window (technology that Windows probably had for 2 decades by now)
OpenSSL 3 broke ABI compatibility massively. Ubuntu 22.04 only ships OpenSSL 3, not including OpenSSL 1.1 for backwards compat (like Arch seems to do).
If you published a .NET 5 app in August 2022, it will not work on Ubuntu 22.04. This is a distro which came out only 8 months after you published your app. .NET Core 3.1 was still in LTS (and also broken).
OpenSSL 3 was released only 7 months before Ubuntu 22.04 came out. The fact that Ubuntu deems it reasonable to just completely break backwards compatibility on such a tight timeframe is absolutely fucking obscene. I have no words to describe the degree of blatant disregard for basic backwards compatibility here. This is the kind of slow-moving break one would do over the span of a decade, not in a 7 month speedrun schedule to save a couple goddamn megabytes.
Somebody at Canonical thought twice about dropping OpenSSL 1.1, and decided that yes, Ubuntu 22.04 should just shit all over backwards compatibility to save some megabytes. When one of the biggest Linux distros, often considered a "baseline" for various things, just completely disregards basic principles like "a program compiled a year ago should keep working" I genuinely do not know what to tell you except that there is no fucking hope for this platform in its current governance.5
"Linux is better for developers" is a common mantra. What is commonly left out is that it's better for developers to work on. Not for developers to target. I can say with massive certainty that Linux is far more effort to target, all while being a SIGNIFICANTLY lower market share. The reasons why are basically explained at length in points above, but seriously. Linux breaks more and has higher maintenance effort, while also having significantly worse tech.
If I only targeted a single OS, a Linux-exclusive version would still be much more effort to develop and maintain than a Windows-exclusive version. Now consider that Linux is a tiny marketshare compared to Windows and run the numbers on that. This is not for lack of familiarity, but simply due to worse underlying technology.
Look, I'm still committed to supporting Linux, but holy shit, can redditors stop putting their damn head in the sand about how broken parts of their favorite OS can be?
- Win32 Is The Only Stable ABI on Linux
- Desktop Linux Platform Issues
- Mastodon thread about Wayland and GNOME
- An even bigger list than I can provide
Footnotes
-
Hell, we have 4K 27" external monitors at work we plug our macbooks into, and the best any text has ever looked on those was when I RDP'd into Windows Server 2022. ↩
-
People will say that the reason this HAD TO HAPPEN was because X11 was "fundamentally insecure" or "fundamentally fixeable". This is of course complete horseshit: of course the protocol is fixeable. Realistically just nobody wanted to touch the codebase with a 10 foot pole because they aren't Microsoft and they aren't getting paid enough to give a shit. ↩
-
It's hilarious for me, the bystander who is comfortably typing this passage from my Windows system. I'd be fucking fuming if I had to use Wayland as a daily driver and basic technical functionality on my system was getting bogged down by this bullshit. ↩
-
Based approach: Valve is the actual governor of Linux at this point and you can trust them to not fuck this up. ↩
I feel like we're both sticking our heads in the sand