Un-Pixelifying i3-WM

LuLu
6 min read

Well well well... i3, long time no see!

I've been an avid BSPWM enjoyer for some time, but I've forced myself onto i3 as I was looking for an alternative to BSPWM which was also light weight. I'm personally not that fond of i3's blocky style, so in this blog post I'll be going over what I did to make it more appealing to look at while still keeping the minimal, snappy feel that i3 provides.

The "Blocky-ness"

i3 wm 2016

Now, this doesn't look bad by any means in terms of how the windows themselves look. But it can be so much nicer. Hell, even something like 3px gaps on the windows in that screenshot would help tidy stuff up a bit.

I love Hyprland. I love the way it looks, feels, the animations are exceptional. But god damn NVIDIA just wont let me have it will they? So what's the next best thing you ask?

i3 + Picom + xborder

Holy moly was I happy to find a border alternative. I mean just look at it:

Combining Picom's shadows and rounded corners + xborder results in a truely "hyprland" feeling experience, which I clearly yearn for. The configuration for xborder is super simple, I've got my configuration stored in ~/.config/xborder/config.json and i3 is set to auto launch it:

exec --no-startup-id /home/lu/Documents/Github/xborder/./xborders --config ~/.config/xborder/config.json

Simply lovely! It doesn't get much easier than that.

Picom Configuration & Why not compton?

Not to disappoint, but I purely went with picom because its newer. That's honestly it. I might give compton a go in the future. But as of right now, picom is where it's at for me.

I spent a looong while looking through Picom docs and the default config to see what I can mess with and what I can achieve. What I ended up with is honestly way better than what I was expecting when moving to i3.

  1. Blur

    Who doesn't love a bit of blur? I'm running 0.9 opacity on non-focused windows, combining this with a low strength blur results in a super nice frosted glass look, while not being too distracting:

  2. Opacity

    I know some people that run like .5 or even .25 for inactive windows. I assume it's a fun game for them to squint their eyes trying to read a cmake build error log? Unsure...

    I myself run .9 on all non-focused windows and 1.0 on focused (duh). I have been tinkering with the idea of a time based opacity script but that is on the back burner.

  3. Shadows

    I configured my shadows to be quite prominent as I think with the opacity + blur + strong shadow combo looks super good after some testing. I am torn though, as it looks quite good without shadows. I've seen no downside performance wise to running shadows though.

Window Rules

Shoot me in the shins and call me Shirley, I hate window rules.

Don't get me wrong, they're helpful. Super helpful. But I feel like I'm forever just finding new windows that need to bypass the composition pipeline completely, or just the blur, or just the opacity. Gawd dayum. I've typed xprop more times into my terminal than I'd like to admit. I will probably just end up making a script that functions the same as xprop, but just appends whatever window class ID was found right into the excludes...

xprop isn't annoying to use, it just gets repetitive really quickly. By typing xprop into your terminal and then clicking on a window, you will receive an output that looks something like this:

╭─lu on SuperComputer in ~ 
╰─❯ xprop
_NET_WM_ICON(CARDINAL) =     Icon (128 x 128):
    (not shown)

_NET_WM_DESKTOP(CARDINAL) = 1
_NET_WM_STATE(ATOM) = 
WM_STATE(WM_STATE):
        window state: Normal
        icon window: 0x0
_NET_WM_ICON_NAME(UTF8_STRING) = "~"
_NET_WM_NAME(UTF8_STRING) = "~"
WM_LOCALE_NAME(STRING) = "en_GB.UTF-8"
WM_CLIENT_MACHINE(STRING) = "SuperComputer"
WM_ICON_NAME(STRING) = "~"
WM_NAME(STRING) = "~"
XdndAware(ATOM) = BITMAP
WM_CLASS(STRING) = "kitty", "kitty"
WM_NORMAL_HINTS(WM_SIZE_HINTS):
        window gravity: Static
WM_HINTS(WM_HINTS):
        Initial state is Normal State.
_NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_NORMAL
_NET_WM_PID(CARDINAL) = 28600
WM_PROTOCOLS(ATOM): protocols  WM_DELETE_WINDOW, _NET_WM_PING

We're looking for the WM_CLASS string, in this case "kitty".

You can use this class to force rules per window, in my case, I prefer all steam windows to float, so in my i3 config:

for_window [class="steam"] floating enable
for_window [class="steamwebhelper"] floating enable

The same applies for picom compositing rules. Here, i3 bar and Apex Legends are both now bypassing the corner rounding pipeline:

rounded-corners-exclude = [
    "class_g = 'i3bar'",
    "class_g = 'steam_app_1172470'"
];

Auto-tiling

GOD I WANT HYPRLAND SO BAD

Right, so by default, i3 doesn't take your active window's dimensions or screen position into account when you're opening a new window that will be tiled.

I.E. They all open like this by default:

God I hate this. I don't even know why. If you've used sway or hyprland before you will know exactly what I'm talking about.

But luckily for us, autotiling exists! God bless the open source community.

It's super easy to install, the github provides self build instructions, or if you're on Arch you can grab it from the AUR with yay -S autotiling. Once done, configuration is even easier. There basically is none.

Well... That's a lie

$ autotiling -h
usage: main.py [-h] [-d] [-v] [-o [OUTPUTS ...]] [-w [WORKSPACES ...]]
               [-l LIMIT] [-sw SPLITWIDTH] [-sh SPLITHEIGHT] [-sr SPLITRATIO]
               [-e [EVENTS ...]]

options:
  -h, --help            show this help message and exit
  -d, --debug           print debug messages to stderr
  -v, --version         display version information
  -o [OUTPUTS ...], --outputs [OUTPUTS ...]
                        restricts autotiling to certain output; example:
                        autotiling --output DP-1 HDMI-0
  -w [WORKSPACES ...], --workspaces [WORKSPACES ...]
                        restricts autotiling to certain workspaces; example:
                        autotiling --workspaces 8 9
  -l LIMIT, --limit LIMIT
                        limit how often autotiling will split a container; try
                        "2" if you like master-stack layouts; default: 0 (no
                        limit)
  -sw SPLITWIDTH, --splitwidth SPLITWIDTH
                        set the width of the vertical split (as factor);
                        default: 1.0;
  -sh SPLITHEIGHT, --splitheight SPLITHEIGHT
                        set the height of the horizontal split (as factor);
                        default: 1.0;
  -sr SPLITRATIO, --splitratio SPLITRATIO
                        Split direction ratio - based on window height/width;
                        default: 1;try "1.61", for golden ratio - window has
                        to be 61% wider for left/right split; default: 1.0;
  -e [EVENTS ...], --events [EVENTS ...]
                        list of events to trigger switching split orientation;
                        default: WINDOW MODE

But I'm going to be honest, I just don't have a use case for most of these. The defaults replicate Hyprlands autotiling basically perfectly. Don't fix it if it ain't broken!

You can autolaunch this script via i3 with an exec --no-startup-id command!

The result from autotiling:

Conclusion

i3 was probably the easiest wm I've ever configured. I thought BSPWM was simple and user friendly but god damn, I can just have everything in a single i3 config if I really wanted. But that would be masochism.

If you're looking for something new to just try out and learn, or if you're looking for a permanent alternative to your current WM, I urge you to give i3-WM a fair shot.

Just because it's hard and blocky on the outside, doesn't mean it can't be beautiful to look at.

0
Subscribe to my newsletter

Read articles from Lu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Lu
Lu