webcodr

Fix Omarchy Gaming (Vulkan)

After setting up my new notebook, I wanted to try some games on Steam. Works like a charm on my mini PC with Omarchy, but not this time. After starting a game, it takes a few seconds and nothing, the game silently crashes.

After fiddling and searching around, Proton logging etc., still no cause in sight. Then I tried Doom 2016 and it worked! But why? Doom starts with an OpenGL renderer, after switching to Vulkan it behaves like the other games. So there’s something wrong with Vulkan?

Vulkan Tools (Arch package vulkan-tools) to the rescue! After running vulkaninfo it became pretty clear that something important was missing: the Radeon Vulkan driver. No driver, no Vulkan. No Vulkan, no Proton …

Thankfully it’s easy to fix, just install the missing packages:

sudo pacman -S vulkan-radeon mesa mesa-vdpau lib32-vulkan-radeon lib32-mesa

It should work immediately after the installation. There’s already a GitHub issue with a pull request to solve this, but it’s open for a weeks now and it’s not clear, when the fix will be merged and released.

I don’t know why it’s no problem with my other PC, but I have installed Omarchy the old way on this machine (Arch Install and the Omarchy setup script). My best guess is that the bug was introduced with Omarchy’s ISO setup.

How does it run?

Well, that Ryzen AI MAX+ 390 is an absolute beast. Doom 2016 runs with over 100 fps with max settings on the internal display (2880x1800) and still far beyond 60 fps in 4K on my main monitor. Just don’t use fullscreen mode, it flickers like hell (known problem of Doom 2016 with the Vulkan renderer).

The HP ZBook Ultra no gaming notebook, but it has a really powerful APU and is still a quite compact and light device on the level of a 14" MacBook Pro. I don’t want a 250 W 4 kg behemoth of a notebook.

Hyprland Trackpad Tips & Tricks

For first time in my life I bought a PC notebook. I didn’t even consider to boot in the pre-installed Windows 11 and installed Omarchy right away.

The HP ZBook has a quite good trackpad, even compared to MacBooks, but some things seemed off. No right-click with two fingers, instead it would only work in the lower right corner. And I absolutely hate tapping, I want real clicks.

Turns out, it’s not the hardware. It’s all configurable in Hyprland. In Omarchy you can find the settings in .config/hypr/input, sub-category input:touchpad.

Here are some handy options to tweak the settings:

input {
  touchpad {
    # Enable two-finger clicks for right-clicking
    clickfinger_behaviour = true

    # Disable tapping
    tap-to-click = false

    # Enable natural scrolling
    natural_scroll = true

    # Disable the trackpad while typing (accidental tapping etc.)
    disable_while_typing = true
  }
}

There is even more like tapping maps, middle button emulation etc. – you can find all options here.

More awesome CLI tools

This a follow up article to Awesome CLI tools with even more useful CLI tools.

btop

It’s top on steroids with a beautiful terminal UI. Way more information at a glance and just a joy to use.

delta

Do you often look at git diffs? Are you missing syntax highlighting? delta is here to help. Just install it, configure delta as pager for git and voila, diffs with syntax highlighting.

dog

Dog is a modern DNS lookup tool with colored output and a simple interface.

tokei

Are you a fan of statistics? Tokei can summarize file types, lines of code, comments or blank spaces of your project.

I’m currently converting a large Java project to Kotlin and tokei makes it easy to see the progress.

procs

A modern replacement for ps with colored output, keyword search, tree view and some useful helpers for networking (ports) and Docker.

lazygit

One of my favorites. Lazygit is a dead simple, yet powerful and elegant to use terminal UI for git. I’m not a fan of the git CLI and always preferred a GUI tool like Tower, but nowadays lazygit is my git client of choice. I don’t think there is better git client out there. Even many NeoVim distributions support it out-of-the-box (Lazyvim with LEADER + gg).

sd

Do you use sed? Do you like it? I don’t. sd is better, much better.

I'm using Arch btw

Well, that escalated quickly. One week ago I would have said never to touch Arch (except SteamOS). Nothing personal, it’s just that I am primarily a Mac guy and never stayed with Linux that long. I really enjoyed Pop!_OS. System76 created a really nice flavor of Ubuntu, but it’s still Ubuntu, so packages are often out of date and you always have to rely on custom repos or other means to get current versions.

With Arch and its rolling release model the latest versions are almost immediately available. Sounds good? Of course there’s a catch: breaking changes are also immediately available. Updating all dependencies can break many things at once and that can get very annoying pretty fast. From what I’ve heard the situation improved over the years, so if you’re doing nothing too crazy, it should be okay.

A few days ago I discovered Omarchy from DHH (creator of Ruby on Rails). He created an opinionated Arch setup for developers. Just install Arch via archinstall and run Omarchy’s installation script and you’re mostly done. A few days ago Omarchy version 2.0 was released and brings a complete ISO file with its own installer. Just boot the ISO from a USB drive, enter some things like your user name, password, hostname etc. and wait a few minutes. That’s it!

Omarchy ships with Hyprland, a really efficient and fast tiling Wayland compositor that doesn’t look like it’s from the 1980s. There’s all sorts of eyecandy, everything is customizable and there are also many cool plugins.

Setting up Hyprland and plugins on your own can be daunting task and a very time-consuming one as well. Omarchy to the rescue! It ships with a really good default configuration and is perfectly useable from the get-go, but that’s only the beginning of the journey. DHH considers Omarchy a starting point for you own configuration and therefore made it very easy to customize the configuration for your needs. There is even a special Omarchy configuration tool for switching the global color theme (for Hyprland, Alacritty, Neovim etc.) or setting up fingerprint sensors for biometric authentication (should be working very well with Framework devices).

Omarchy also installs many useful terminal and GUI programs, like Alacritty (terminal emulator), Neovim, Spotify, Lazygit, fd etc. All GUI programs are mapped to intuitive hotkeys, f.e. SUPER + M for Spotify or SUPER + B for the browser (Chromium by default). SUPER + Space displays the application launcher (like a simple version of Spotlight or Raycast). There’s much more for controlling the current window size to move the focus, switch virtual desktops, fullscreen etc.

All hotkeys are easily customizable in .config/hypr/bindings.conf. You don’t like Chromium? Just install your favorite browser with the Pacman (Arch’s package manager) and update the variable $browser in the Hyprland config. That’s it. The browser is also used for webapps mapped on hotkeys, ChatGPT f.e. is mapped on SUPER + A.

A little example to change the music hotkey to Apple Music as webapp (there is no native Linux version).

bindd = SUPER, M, Apple Music, exec, $webapp="https://music.apple.com/de/library/recently-added?l=en"

Pressing SUPER + M now opens a browser window in app mode (no address bar etc.) with the Apple Music website. Nice. I also changed SUPER + A from ChatGPT to Theo Browne’s awesome T3.chat.

That’s far from all that Omarchy offers, but would be a bit much to list every feature in this blog post. Omarchy has a pretty good documentation with many useful tips, a troubleshooting section etc. They even have advice on my problems with Bluetooth. The Minis Forum UM760 comes with a MediaTek WiFi card and sadly MediaTek doesn’t offer Linux drivers for this particular model. I switched to an Intel AX210 card for a few bucks and now it works like a charm.

According to DHH they will even ship an ISO file with Arch and a custom installer in the near future. It’s also planned to switch the default terminal from Alacritty to Ghostty, but there are still some issues to solve.

TL;DR

If you’re not afraid of Linux and want a decent base installation of Arch with many developer-focused feature, give Omarchy a chance!

Updates

  1. 2025-08-05: Omarchy 1.11 brings a new Hyprland config file structure and bindd command. I updated the file path for key bindings and the example for Apple Music accordingly.
  2. 2025-08-28: a small update about the release of Omarchy 2.0 with its own ISO and installer.

Please don't do this with switch statements

The classic C-like switch statement is fine, but it has its flaws. It’s no coincidence that modern languages like Kotlin or Rust offer alternatives like when or match or a more fine-tuned version of switch like Zig.

I’m currently the in early stages of rewriting a large and complex Java code base to Kotlin. Some parts of this codebase are really ugly and uncessarily complicated and convoluted. Yesterday I crossed the path of a nasty use of a switch statement in a operation on a Java Stream. Unfortunately I can’t share the real code, but imagine something like this:

int value1;
int value2;

for (...) {
    switch (enum) {
        case Enum.FOO:
            if (something) {
                if (somethingElse) {
                    value1 = someConvulutedStreamOperations();
                    value2 = someOtherConvulutedStreamOperations();
                    // imagine 30 more lines here
                } else {
                    value2 = 0;
                }

                break;
            }

        case Enum.BAR:
        default:
            value1 = 1;
            value2 = 2;

            break;
    }
}

This was part of a Java class with over 1,000 lines of code. Streams with many operations everywhere and sometimes very deep nesting thanks to old-style Java code. The original case for Enum.FOO stretches over almost the whole display space, so it’s not easy to spot any potential pitfalls. After I ran IntelliJ’s Kotlin migration tool and cleaned up all errors, it was time to run the unit tests and four out of 24 failed.

As you can imagine it’s not straight forward to find problems in such a large and complex class but I came across a notice of an unused value assignment. Why it was there was also not clear immediately, so I compared the original Java file with the Kotlin version.

Since Kotlin has no switch IntelliJ converted it to when. Unlike it’s Java counterpart when can handle null and is exhaustive with enums. There also no break keyword and that’s exactly the problem here.

Look at the Java code and think about what happens on Enum.FOO if something is true but somethingElse isn’t. The break keyword is not triggered and the switch statement goes on to the default block. As result value1 and value2 are assigned their values.

IntelliJ’s migration tool is quite good, but it didn’t catch that and the generated when statement was wrong. That’s also the reason for the unused assignment notice. I assume the migration tool was confused by the scope of break since both switch and for can use it. Also there is no real match in Kotlin for such a structure. You have to assign the values of both variables for every branch inside the when statement. After fixing that all tests ran successfully.

It’s a really good example how not to use switch statements, especially with nested control structures in a case. Since the original author of the code is no longer available, I can only guess why it was written this way. Probably to avoid duplication or even unintended. Well, it could seem as an elegant solution if you’re familiar with the code, but to other people it’s just an unnecessary pitfall that should be avoided. It’s untuitive at best, not easy to comprehend and can lead to nasty bugs, especially if it’s unintended behaviour. That’s why Kotlin, Rust, Zig and other modern languages avoid breaks in their alternatives to switch statements in the first place.

Fixing no A2DP with Bluetooth headsets on Linux

Please beware that the following instructions are suitable for media consumption only! After this changes your headset can’t make any calls without a dedicated microphone until you undo them.

Having trouble with the audio quality of your Bluetooth headset on Linux? It sounds awful if you’re listing to music and videos? Well, congratulations, I had the same problem and found a solution. At least if you’re only into listening and won’t make any calls. This works on Ubuntu or Ubuntu-based distributions like Pop!_OS or any other distribution that relies on Blue Z and Pipewire/WirePlumber for Bluetooth audio.

What’s wrong?

Bluetooth has different profiles for different things. If you want to make a call, your headset will switch to the Hands-free Profile (HFP). The available bandwidth will be shared for audio input and output and different audio codecs will be used. It’s good for calls, but really shitty if you want to listen to music. The headset needs to switch to A2DP (Advanced Audio Distribution Profile) for good sound quality. This should happen automatically and HFP should only be active, if you’re making a call. I had never trouble on macOS or Windows with this, but I’m trying Pop!_OS now. It worked for a few days, but today the headset would only connect with the HFP and streaming music or watching videos was as pleasant as dental treatment with a power drill.

Many searches later, I found out that WirePlumper (the session manager for the Pipewire multimedia framework) has some bugs that will trigger HFP on BT headsets even if there’s no call. That’s pretty annoying but at least somewhat easy to solve. There’s a solution in the Arch Linux Wiki, but it needs some modifications for Ubuntu-based distributions.

The solution

First you need to create a directory path in your home directory:

mkdir -p ~/.config/wireplumber/bluetooth.lua.d/

This will create an directory that allows you to override the default WirePlumper Bluetooth config without overwriting the original file.

Now copy the original config to the overwrite directory:

cp /usr/share/wireplumber/bluetooth.lua.d/50-bluez-config.lua ~/.config/wireplumber/bluetooth.lua.d/

You can now edit the file in the override directory.

Beware that the original instructions from the Arch Linux Wiki contain a conf file, but at least with Ubuntu (and Pop!_OS) the config file is written in lua, so it’s a completely different syntax.

Look for bluez5.roles – it should be commented out. I would recommend not replace the comment and just put the following line below. It’s easier to undo if something goes wrong or you need to enable HFS.

["bluez5.roles"] = "[ a2dp_sink a2dp_source ]"

Save the file and restart the Bluetooth service:

sudo systemctl restart bluetooth

Now reconnect your device and A2DP should be working fine.

Awsome CLI Tools

There are some incredibly useful CLI tools out there. Here’s a list with some awesome tools I’m using for my daily work.

atuin

Atuin is a history replacement with a fuzzy finding search and sync/backup options (self-hosted if you need). It’s written in Rust (blazingly fast!) and stores the history entries in a SQLite db. You can even import your current history from your shell. Atuin supports bash, zsh, fish and NuShell.

bat

Need cat a lot? Bat is a cat clone on steroids with syntax highlighting, themes, git integration and much more.

eza

Everyone needs ls ? Nope, eza is much better. Colored output, icons via nerd fonts, git status tracking per file, tons of display options …

tldr

Reading an man page can be frustrating, why can’t I just have the TL;DR version? Well, tldr does exactly that. Just use it like man and enjoy the TL;DR version of a man page.

zoxide

As his siblings cd is a little dated and clunky. With zoxide you can easily jump to directories without typing the full path. It stores a history of your visited paths and you can jump via keywords to your directories.

chezmoi

You’re using multiple computers or just want a simple and reliable way to store your dot files? Chezmoi is your friend and stores your dot files in a git repo with syncing capabilities to other devices. It’s even possible to encrypt your files. If you have secrets in your dot files, chezmoi comes with integrations for many password managers to safely store your passwords, tokens etc.

starship

Your shell looks boring? Just theme it with starship! It’s pretty easy to build your own and if you don’t want to, there many themes available. Starship also has integrations for many dev tools to show the current git status or currently active versions of your runtime environments like NodeJS, Rust, Go, Java etc.

fzf

Finding files with the usual suspects works fine, but fzf can do this faster and much more intuitive. It’s a fuzzy finding search within your current directory, processes, git commits, history (if you don’t like Atuin) and much more.

ripgrep

Ripgrep is a really fast regex-based search tool and can do much more than grep alone.

btop

Another top variant? Yup, but btop is way more like it’s modern GUI-based colleagues on macOS or Windows with CPU and GPU usage, process trees, I/O and disk activities, battery status …

Micro DSLs for builders with Kotlin

The builder pattern is a great tool and it’s heavily used in many Java projects and dependencies. But in a Kotlin code base it’s looks a little odd and out-of-date. In this short post I will show you how to write a micro DSL on top of builder with just a few lines of code.

I’m using Spring’s ResponseCookie class as base for the DSL as it has a builder already on-board.

A little example:

val cookie = ResponseCookie
    .from("cookie name", "cookie value")
    .httpOnly(true)
    .path("/")
    .build()

How would this look like with a micro DSL?

val cookie = createCookie("cookie name", "cookie value") {
    httpOnly(true)
    path("/")
}

Instead of calling the static method ResponseCookie.from() which returns a ResponseCookieBuilder object, you just give the function three parameters: two strings for name and value, and trailing lambda with the builder context. There is also no need to call ResponseCookieBuilder.build() anymore. It’s shorter and better to read. Since this is only a small example the advantages are not that big. Micro DSLs really shine with large and often used builders. They can also help to automate things, see below.

How?

fun createCookie(
    name: String, 
    value: String, 
    lambda: ResponseCookieBuilder.() -> Unit
) = ResponseCookie.from(name, value).apply(lambda).build()

Et voila, a new micro DSL is born. Deriving the trailing lambda function from ResponseCookieBuilder does the trick. The lambda function takes an instance of ResponseCookieBuilder as context of this, so it’s possible to access the methods of the given ResponseCookieBuilder inside the lambda function. All we have to do is to create an instance of ResponseCookieBuilder with ResponseCookie.from() and call Kotlin’s apply method on the builder object with the lambda function. It will automatically inject the current instance of ResponseCookieBuilder into the lambda function and apply the instructions inside the lambda function on the instance. To create a ResponseCookie object from the builder, just call the build method and return the result.

You can use this little trick with all builders. Need more automation? No problem! In my current project we’re using such micro DSLs to create product configurations. The factory method contains sanity checks after the lambda function was applied to the builder object. It will also fetch a YAML file via the product ID given to the builder. This data is parsed into an object and will be put into a property of the builder object. After the configuration object is created, the factory method will add to a map and return the instance to store in a variable. It’s now possible to directly access the configuration via its variable name or to fetch it from the map. The variable is very useful for tests, but if we have to fetch the configuration dynamically by ID from a string, the map is the way to go.

Of course there are other ways of achieving such automation, but the micro DSL approach is simple, improves readability and can also reduce redundant code. You can even easily nest builders to create a more powerful DSL. Spring’s Kotlin extensions also rely on micro DSLs and extension functions. Take Spring Security for example. Their fluent interface for the security configuration is awful to read and difficult to understand, but Spring also provides a Kotlin extension with a micro DSL for that. So much more intuitive and better to read. There are many more extensions like for bean creation, the MVC mock in tests etc.

US International Keyboard Layout Without Dead Keys

Depending on your country’s keyboard layout writing code can be quite annoying. The German ISO layout is an exceptional pain in the ass, as almost all relevant symbols require a modifier key, sometimes even two (I’m looking at you, Apple). German has some special characters (ä. ö, ü, ß) and it makes sense to have them readily available without modifier keys, but it just sucks for programming.

I decided to switch to the US ANSI layout and bought two new keyboards: a Keychron K3 Pro for my MacBook Pro (light and portable, perfect if I have to go to my company’s office) and Keychron Q1 version 2 for my Windows PC. By the way, the Q1 is heavily modded and will be tweaked further in the coming weeks. I will write an article about the mods after the keyboard is finished.

Both keyboards work great, but to type German special characters I have to use the US international layout on Windows and there’s a catch. Who would have thought, if Microsoft is involved? Certain keys like single quote/double quote or accents/tilde are so called dead keys.

If you press them, nothing will happen at first. Only after pressing the next key, the symbol will appear. So, if you want to type a text wrapped in double quotes, you press the the key and the double quote will appear, as soon as you type the first character of the actual text. It’s annoying as fuck and drives me crazy.

Solution

Unfortunately Microsoft does not ship an US international layout without dead keys for Windows. Most Linux distributions do exactly that, but I guess that’s to easy for a big international corporation like Microsoft.

What to do? After a little bit of googling, I found a neat little tool, the Microsoft Keyboard Layout Creator. I’ve never heard of this program before but it’s legit and works fine.

If you’re having trouble with the dead keys like me, I recommend the following steps:

  1. Download the Microsoft Keyboard Layout Creator from Microsoft’s website
  2. Open it and load the US international layout via File -> Load Existing Keyboard...
  3. All dead keys are shown with a light grey background. You can remove their dead key status via the context menu
  4. Don’t forget to activate the shift layer via the checkboxes left of the keyboard layout and disable the dead keys as well
  5. Save your config
  6. Build the layout via Project -> Build DLL and Setup Package
  7. After the build finished, a dialog will ask you to open the build directory. Open it and run setup.exe to install the new layout
  8. Restart Windows. This should not be necessary, but unfortunately Windows is Windows …
  9. Go to Settings -> Time and Language -> Language and Region
  10. Select your preferred language and click the three dots and choose Language options
  11. Use Add a keyboard and select United States International - No Dead Keys
  12. I recommend to remove all other keyboard layouts, so they can’t interfere

Now you should have a working US international layout without those annoying dead keys. Happy typing!

Introducing Server Runner

In my recent adventures with Rust, I planned to write a REST API with the help of the excellent book “Zero To Production In Rest” from Luca Palmieri. That’s still happing, but as small side project, I wanted to write some kind of CLI tool.

A few weeks ago I had wrote a bash script to run some web servers and check their status until they’re up and running. When all servers are ready, a command would be executed and all servers would be closed after this command is finished. Since I hate bash with passion, I asked an friend to help me: ChatGPT.

I would never trust an AI in this day and age to write a code base for me, but for small scripts? Why not. As long as the scope is small and I can understand the code, ChatGPT is a really good tool. That script does exactly what I want and it’s easy enough to understand, even for me as a bash hater.

But I wanted to this properly and so I decided to rewrite this script as a small CLI tool program in Rust: Server Runner. Well, not very creative name, but it does what is says.

Configuration

Server Runner is quite simple and just needs a small YAML file as configuration. Here’s a small example.

servers:
  - name: "Hello World"
    url: "http://localhost:3000"
    command: "node index.js"
command: "node sleep.js"

To start server runner, just run:

server-runner -c servers.yaml

Server Runner will execute all server commands defined in the config section servers and waits until the URLs return HTTP 200. When all servers are up and running, the primary command will be started. After the command finished, all server processes will be killed off.

How do I get it?

Server Runner is available as a Cargo Crate and will be published soon on NPM with executables for macOS (aarch64, x86_64), Linux (aarch64, x86_86) and Windows (x86_64).

Installation via Cargo

cargo add server-runner

The source code is available on GitHub.