Skip to main content

Ralsina.Me — Roberto Alsina's website

Posts about qtile

Color Coordination Using Base16

The Problem

A few days ago I wrote about how I moved from a al­l-in­clu­sive en­vi­ron­ment (KDE Plas­ma) to Qtile, a tiling win­dow man­ager, and all the things I did to have the ameni­ties I missed from Plas­ma.

One thing there that may need fur­ther ex­pla­na­tion is the sec­tion about col­or schemes be­cause the con­cepts them­selves are fair­ly un­usu­al.

So, let's dig in­to it a bit more.

This is how a few apps look by de­fault if you don't con­fig­ure things:

unconfigured apps

One is a Qt ap­p, and the oth­er is a GTK app and they look ... bad?

I mean, I don't re­al­ly care all that much, but not on­ly are the wid­get styles to­tal­ly dif­fer­en­t, but the col­ors are all over the place.

And the same hap­pens to Qtile's bar, and my ter­mi­nal (alacrit­ty) and my tmux­er (zel­li­j) and my web brows­er and so on.

Ev­ery­thing is a dif­fer­ent col­or.

In Plas­ma, a comon col­or scheme is en­forced on most apps (not in chrome, though) and it makes things much nicer.

So, what's the equiv­a­lent in the Win­dow Man­ag­er world? There is­n't one, re­al­ly, but there are bits and pieces you can make in­to ... 80% of it.

Here's what I did.

My Solution

First, you need a source of con­sis­tent col­or schemes. If you just re­al­ly, re­al­ly, re­al­ly like a sin­gle one, then they may have a site full of con­fig­u­ra­tions for dif­fer­ent apps to force them to fol­low it.

For ex­am­ple: Drac­u­la or Nord

But I want­ed to be able to try a bunch and see which one I liked.

For this, there is noth­ing like base16

Base16 de­fines a stan­dard. You will have 16 col­ors. Those col­ors mean things.

  • Col­or 0 is the back­ground.
  • Col­or 1 is the fore­ground.

And so on.

Then it de­fines some oth­er things, like "If you are do­ing a 'dark' scheme, col­ors go from dark­er to lighter" and so on.

Then they pro­vide a col­lec­tion of themes, which are each 16 col­ors fol­low­ing those rules.

For ex­am­ple, this is the Bro­gram­mer Theme:

scheme: "Brogrammer"
author: "Vik Ramanujam (http://github.com/piggyslasher)"
base00: "1f1f1f"
base01: "f81118"
base02: "2dc55e"
base03: "ecba0f"
base04: "2a84d2"
base05: "4e5ab7"
base06: "1081d6"
base07: "d6dbe5"
base08: "d6dbe5"
base09: "de352e"
base0A: "1dd361"
base0B: "f3bd09"
base0C: "1081d6"
base0D: "5350b9"
base0E: "0f7ddb"
base0F: "ffffff"

Now, sup­pose you want an app to fol­low the "base 16 stan­dard" to see how it looks in Bro­gram­mer style.

All you need to do is take that ap­p's con­fig file and put the right col­or where it needs it.

So, for ex­am­ple, for the Alacrit­ty ter­mi­nal:

colors:
  # Default colors
  primary:
    background: '0x1b1918'
    foreground: '0xa8a19f'

  # Colors the cursor will use if `custom_cursor_colors` is true
  cursor:
    text: '0x1b1918'
    cursor: '0xa8a19f'

  # Normal colors
  normal:
    black:   '0x1b1918'
    red:     '0xf22c40'
    green:   '0x7b9726'
    yellow:  '0xc38418'
    blue:    '0x407ee7'
    magenta: '0x6666ea'
    cyan:    '0x3d97b8'
    white:   '0xa8a19f'

Of course edit­ing all the con­fig files ev­ery time you want to change your col­or scheme is a pain.

So the base16 project al­so col­lects tem­plates. Those are files that when com­bined with a theme gen­er­ate the con­fig­u­ra­tion file for an ap­pli­ca­tion.

This is the Alacrit­ty tem­plate:

# Base16 {{scheme-name}} - alacritty color config
# {{scheme-author}}
colors:
  # Default colors
  primary:
    background: '0x{{base00-hex}}'
    foreground: '0x{{base05-hex}}'

  # Colors the cursor will use if `custom_cursor_colors` is true
  cursor:
    text: '0x{{base00-hex}}'
    cursor: '0x{{base05-hex}}'

  # Normal colors
  normal:
    black:   '0x{{base00-hex}}'
    red:     '0x{{base08-hex}}'
    green:   '0x{{base0B-hex}}'
    yellow:  '0x{{base0A-hex}}'
    blue:    '0x{{base0D-hex}}'
    magenta: '0x{{base0E-hex}}'
    cyan:    '0x{{base0C-hex}}'
    white:   '0x{{base05-hex}}'

See those bits like {{base05-hex}}? That one gets replaced with your theme's color 5.

But again, us­ing the tem­plate for each app you want to theme is bor­ing.

So you need a tool to do that. The one I use is called flavours

What flavours does is:

  • Get all the base16 col­or themes
  • Get all the base16 app con­fig tem­plates
  • Fol­low­ing a con­fig­u­ra­tion file, gen­er­ate all the con­fig files you need.
  • Op­tion­al­ly: run com­mands so the apps no­tice their con­figs have changed.

For ex­am­ple, this is my con­fig­u­ra­tion for Alacrit­ty:

[[items]]
file = "~/.config/alacritty/alacritty.yml"
template = "alacritty"
subtemplate = "default-256"
rewrite = false

It says:

  • Generate ~/.config/alacritty/alacritty.yml
  • Use the alacritty template
  • From that template use the version called default-256 (don't worry)
  • DO NOT RECRE­ATE THE WHOLE FILE

That last bit is im­por­tan­t. That file does­n't just have the the­me, it has a lot of oth­er im­por­tant stuff, so I don't want it to just have the theme in it.

So, I had to ed­it it once and put these lines be­fore and af­ter where the theme goes:

# Start flavours
... theme goes here
# End flavours

So when I use flavours to apply a theme it will only replace that bit and leave the rest.

Since alacritty notices when the config has changed, I don't need a hook entry. In other cases you can use it. Here's mine for qtile, to let it know it should reread its config:

hook = "killall -USR1 qtile"

So, I configured this for a bunch of things, and at this point, I can just run something like flavours apply atelier-forest and ...

color coordinated apps

Yeah, they are not per­fec­t. I still need to tweak some stuff, but it's get­ting there.

Cur­rent­ly my flavours con­fig­u­ra­tion co­or­di­nates the­se:

  • Alacrit­ty
  • Qtile
  • Zel­lij
  • Rofi
  • Qt
  • Gtk (us­ing Flat­col­or)
  • VS Code
  • Qute­Brows­er

That's rough­ly ev­ery app I use (yes, I am mi­grat­ing from Chrome to Qute­Brows­er for no good rea­son)

And be­cause I am not ok, I wrote a cou­ple of tiny things to make things eas­i­er for me.

A rofi-based theme pick­er called rofi-base16:

rofi-base16 in action

And a base 16 tem­plate for Zel­li­j.

I al­so wrote an ug­ly script to set VS Code's theme but it's shame­ful, so will not be shown here.

Hope­ful­ly this ex­plains some­thing to some­one. If it does­n't, well, it will help me when I in­evitably for­get how this works :-)

Desktop setup

Introduction

I am a long-term KDE us­er. How long ter­m? Well, since 1997 or so with in­ter­mit­ten­cies. A cou­ple of weeks ago, I switched my per­son­al desk­tops to QTile.

Manda­to­ry screen­shot:

A desktop

Why?

Well, I like to switch things around ev­ery few years. I have used Uni­ty, and MATE, and even LxQt at some point, and I have no­ticed over the years that I don't tend to over­lap win­dows, so tiling win­dow man­agers are the nat­u­ral pro­gres­sion of my us­age.

For a few years I have used Krohnkite an awe­some KWin ex­ten­sion that has re­al­ly been a plea­sure to use and proved me that a tiling WM, with a few rea­son­able key­board short­cut­s, was a work­able so­lu­tion.

Late­ly I have been build­ing a home­brew lap­top which has fair­ly lim­it­ed hard­ware, but I want to use for re­al. So I al­so want­ed to ex­plore ways to low­er re­source us­age.

Oth­er re­lat­ed con­cern­s:

  • I have very dy­nam­ic mon­i­tor con­fig­u­ra­tions. My com­put­ers gain and lose mon­i­tor all the time, and this needs to be han­dled au­to­mat­i­cal­ly.
  • I have mul­ti­ple com­put­ers (work, home, home­brew) and they all have very dif­fer­ent set­ups and con­fig­u­ra­tions, but I want them all to work the same.
  • I use chez­moi to man­age my con­fig­u­ra­tions so I want con­fig to be sim­ple text files I can store with a min­i­mum of tem­plat­ing.
  • I want com­fort. I want disk au­to­moun­ing, vol­ume keys, blue­tooth menus, wifi con­fig­u­ra­tion, a clock, bat­tery in­for­ma­tion, no­ti­fi­ca­tions and so on.

But why QTile specif­i­cal­ly:

  • It's tiling but with a nice at­ti­tude. The whole "hard­core" thing in both tiling WM users and projects is a bit tire­some.
  • It's writ­ten, con­fig­urable and ex­tend­able in Python, which I like.
  • It works rea­son­ably out of the box. No, a black screen where you can do noth­ing with­out look­ing up short­cuts in your phone at first is too much.
  • It's "dy­nam­ic". This is one of those minute sub­cat­e­gories you can see in WM fan­dom, but ba­si­cal­ly: win­dows get placed more or less cor­rect­ly by de­fault, while oth­er WMs will make you ex­plic­it­ly han­dle each win­dow as it ap­pears, which sounds sort of like tor­ture to me.

So what?

So I will now de­scribe painstak­ing­ly ev­ery de­ci­sion I made as of to­day, and de­scribe the im­por­tant con­fig­u­ra­tion, and bore any­one who tries to read this to tears, that's what.

In an at­tempt to make this a bit less an­noy­ing I will set­up a prob­lem, then de­scribe the so­lu­tion I found.

Uniform UI for utilities

The Problem

There are a num­ber of ways you in­ter­act with your "desk­top". You may want to lo­gout. Or start an ap­p. Or run a com­mand. Or con­fig­ure a blue­tooth speak­er. Or see the clip­board his­to­ry. Or con­fig­ure wifi.

In the desk­top world, this all comes in­clud­ed in the pack­age deal. In the Win­dow Man­ag­er uni­verse, you have to roll your own, and most set­ups end with a dis­parate col­lec­tion of util­i­ties, like blue­man and net­work-­man­ag­er and clip­it and so on plus what­ev­er the WM it­self pro­vides.

Well, but what if they could be the same thing?

What if in ad­di­tion they could all share a love­ly, cus­tom­iz­a­ble UI?

The Solution

En­ter rofi

the most com­mon us­age for rofi is as an app launcher, and it's great at that, but it's just scratch­ing the sur­face:

rofi as launcher

Be­cause of its flex­i­ble ar­chi­tec­ture, it can be used to con­fig­ure your net­work us­ing net­work­man­ager-d­menu:

rofi as network manager

As your pow­er/l­o­gout menu via rofi-pow­er-­menu

rofi as power menu

And much more. I al­so use it as a front­ed to a pas­word man­ag­er, a fron­tend to ssh, a sim­ple To­do list, an emo­ji pick­er, as a fan­cy win­dow switcher, and as a clip­board his­to­ry man­ag­er us­ing rofi-­green­clip

rofi as clipboard manager

Rofi starts so fast that it's of­ten im­posi­ble to no­tice the dif­fer­ence be­tween hav­ing a tool al­ways run­ning and with an icon in the systray and just hav­ing a but­ton that launch­es rofi.

So, how do I use it in my ~/.config/qtile/config.py?

Bound to some key short­cut­s:

keys = [
    # Launch things
    Key([mod], 'p', lazy.spawn('rofi -show drun'), desc="Run app"),
    Key([mod, "shift"], 'p', lazy.spawn('rofi -show run'), desc="Run Command"),
    Key([mod], 's', lazy.spawn('rofi -show ssh'), desc="Ssh"),
    Key([mod], 'e', lazy.spawn('rofi -show emoji'), desc="Emoji"),
    Key([mod], 'v', lazy.spawn('rofi -modi "clipboard:greenclip print" -show'), desc="Clipboard")
    Key([mod, alt], "Tab", lazy.spawn('rofi -show window'), desc="Move focus to next window"),

I al­so added a Launch­Bar wid­get to my QTile bar where it re­places some­thing like clip­it, the blue­man ap­plet, a pass­word man­ag­er and a to­do ap­p:

    widget.LaunchBar(padding=0, text_only=True, font="Font Awesome 6 Free", fontsize=16, foreground="2e3440", progs=[
        ("", "rofi-bluetooth", "Bluetooth"),
        ("", "rofi -modi 'clipboard:greenclip print' -show", "Clipboard History"),
        ("", "rofi-pass", "Passwords"),
        ("", "rofi-todo -f /home/ralsina/todo.txt", "Todo"),
        ("", "flameshot gui", "Screenshot"),
    ]),

The first field that prob­a­bly looks like a square in there are uni­code char­ac­ters dis­played on the desk­top us­ing font awe­some:

launchbar that uss rofi

Al­so for mouse in­ter­ac­tions with the Wlan wid­get:

widget.Wlan(format=" {percent:2.0%}",width=54, mouse_callbacks={'Button1': lazy.spawn('networkmanager_dmenu')}),

I even use it to show me the short­cut list in case I for­got any!

rofi showing shortcuts

I could write some mi­nor code and use it for oth­er things too. Rofi is a mag­nif­i­cent ex­am­ple of a tool with a nice API that you can lever­age to make it do many dif­fer­ent things.

Display Configuration

The Problem

My dis­play con­fig­u­ra­tions are many. I use note­book­s, so they all have one "de­fault" con­fig­u­ra­tion where they use their own screen.

In one case, it's all it can use.

In an­oth­er, it's per­ma­nent­ly docked to a mon­i­tor above its screen, but some­times I close the note­book and the on­ly dis­play is the mon­i­tor.

In an­oth­er it's usu­al­ly docked to a mon­i­tor be­sides its screen, but it's shared with my work com­put­er, so it los­es and re­cov­ers that mon­i­tor dozens of times via a HD­MI switch through the day as I switch tasks.

While QTile han­dles grace­ful­ly the ad­di­tion and re­moval of dis­plays in gen­er­al, there are some is­sues.

  • Us­ing the HD­MI switch did­n't make the mon­i­tor "go away" which caused some is­sues with bar­ri­er
  • The con­fig file de­scribes the "bars" for each screen. So I need to put the main bar that has the systray on a screen that is al­ways on. But the screens are one per-out­put and in a ma­chine-de­pen­dent or­der, and things got com­pli­cat­ed re­al­ly quick. Here Plas­ma does the right thing ef­fort­less­ly.

The Solution

En­ter au­toran­dr an awe­some tool that will re­act to changes in your sit­u­a­tion and do the right thing.

How?

  • Set your machine as you normally use it say, with two monitors using arandr or something. Then autorandr -s two-monitors
  • Set your machine in its alternate config, say, with the lid closed. Then autorandr -s closed-lid
  • Put autorandr -c in your ~.xprofile

This is enough for most things, ex­cept the bar sit­u­a­tion in QTile.

For that, I wrote two things. First a shell script that goes in ~/.config/autorandr/postswitch This is a "hook" that gets executed after the display configuration changes. Here's mine:

#!/bin/bash -x

config="$AUTORANDR_CURRENT_PROFILE"
notify-send "Switched to config $AUTORANDR_CURRENT_PROFILE"
killall -USR1 qtile  # Make qtile reread configuration

And why do I want qtile to reread its con­fig­u­ra­tion? Be­cause I want the systray to be on the "last" screen, which is al­ways larg­er :-)

Here's the relevant fragment from ~/.config/qtile/conf.py

screen_count = len(subprocess.check_output(shlex.split("xrandr --listmonitors")).splitlines()) -1

screens = [
    Screen(
        bottom=bar.Bar(
            [ ... # List of widgets for this bar
            # Add systray in the LAST screen, otherwise invisible widget
            widget.Systray(padding=10) if i==screen_count-1 else widget.Sep(linewidth=0),),
    ) for i in range(screen_count)]  

This so­lu­tion re­quires a min­i­mum of set­up for my stan­dards (just con­fig­ure your screens once and a bit of code) and a max­i­mum of ben­e­fit:

  • The same con­fig works on all my ma­chi­nes, ex­cept for au­toran­dr pro­files, which have to be done on each com­put­er
  • It works in a to­tal­ly au­to­mat­ed man­ner, I nev­er need to move screens around
  • It will work on any fu­ture com­put­ers thank to chez­moi

Chrome hates WMs

The Problem

I have seen it a few times in the past. I change WMs or desk­tops and sud­den­ly chrome has no idea where my pass­words wen­t. They are there in pass­word­s.­google.­com but chrome nev­er aut­ofill­s, and they are not list­ed in my lo­cal pro­file.

The Solution

Make sure you are running gnome-keyring-daemon and add this in ~/.config/chrome-flags.conf

--password-store=gnome

The Other Solution

You want a pass­word store that is not chrome. I have one in ad­di­tion of chrome, us­ing pass

This keeps my pass­words en­crypt­ed in Git (y­ou can use GitHub, I use my per­son­al gitea) and you can ac­cess them from the com­mand line, from a Chrome ex­ten­sion, or from a but­ton in my desk­top bar us­ing rofi-­pass

This en­sures I nev­er will lose ac­cess to my pass­word­s, and max­i­mizes the ways I can get to them while keep­ing things nice and se­cure.

Automounting Devices

The Problem

While I know per­fect­ly well how to mount de­vices in Lin­ux, I'd rather they mount them­selves.

The Solution

Use ud­iskie it's lightweight, nice­ly in­te­grat­ed, and it just works well by de­fault.

Notifications

The Problem

I want desk­top no­ti­fi­ca­tion­s.

The solution

Use dun­st it's lightweight, nice­ly in­te­grat­ed, and it just works well by de­fault.

Screen Locking

The Problem

I want my screen to lock, but don't want any­thing fan­cy.

The Solution

Use xss-lock and slock

While slock in itself works great, xss-lock integrates it with loginctl so it works better. Just add this to your ~/.xprofile:

xss-lock slock &

Pretty Icons and Colors

The Problem

I want sim­ple icons that are not very col­or­ful, so they in­te­grate with what­ev­er col­or scheme I am us­ing.

The Solution

Since I am fol­low­ing more or less a nord col­or scheme I am us­ing a com­bi­na­tion of Nordzy icons and just us­ing font-awe­some

Here are some snip­pets from the qtile con­fig. Where I am point­ing icons to a spe­cif­ic fold­er, that fold­er con­tains copies of nordzy icons con­vert­ed to PNG with trans­par­ent back­ground­s:

bottom=bar.Bar(
    [ ...
        widget.GroupBox(highlight_method="block", foreground="2e3440", active="d08770", inactive="4c566a", this_current_screen_border="242831",this_screen_border="88c0d0"),
        widget.BatteryIcon(theme_path="/home/ralsina/.config/qtile/battery_icons", background="d8dee9", mouse_callbacks=power_callbacks),
        
        widget.Volume(fmt="",padding=0, theme_path="/home/ralsina/.config/qtile/battery_icons"),
        
        widget.LaunchBar(padding=0, text_only=True, font="Font Awesome 6 Free", fontsize=16, foreground="2e3440", progs=[
                    ("", "rofi-bluetooth", "Bluetooth"),
                    ("", "rofi -modi 'clipboard:greenclip print' -show", "Clipboard History"),
                    ("", "rofi-pass", "Passwords"),
                    ("", "rofi-todo -f /home/ralsina/todo.txt", "Todo"),
                    ("", "flameshot gui", "Screenshot"),
                ]),
    ],
    background=["d8dee9"],
    border_width=[1, 0, 0, 0],  # Draw top and bottom borders
    border_color=["b48ead","b48ead","b48ead","b48ead" ],  # Borders are magenta

Then I configured styles and icons for gtk apps (using lxappearance) and Qt apps (using qt5ct) and things now look decent.

Color Schemes

The Problem

I want a co­or­di­nat­ed col­or scheme in the dis­parate tools I am us­ing.

The Solution

Up­date: I wrote a more tu­to­ri­al-­like thing about this called Col­or Co­or­di­na­tion Us­ing Base16

En­ter flavours, a tool to gen­er­ate con­fig­u­ra­tions for dif­fer­ent things out of sim­ple tem­plates and pre­de­fined col­or schemes.

It al­ready sup­port­ed my ter­mi­nal (alacrit­ty) and rofi, just need­ed to cre­ate tem­plates for my qtile con­fig:

colors = {
    "background": "#{{base00-hex}}",
    "background-lighter": "#{{base01-hex}}",
    "selection-background": "#{{base02-hex}}",
    "comments": "#{{base03-hex}}",
    "foreground-dark": "#{{base04-hex}}",
    "foreground": "#{{base05-hex}}",
    "foreground-light": "#{{base06-hex}}",
    "background-light": "#{{base07-hex}}",
    "variables": "#{{base08-hex}}",
    "integers": "#{{base09-hex}}",
    "bold": "#{{base0A-hex}}",
    "strings": "#{{base0B-hex}}",
    "quotes": "#{{base0C-hex}}",
    "headings": "#{{base0D-hex}}",
    "italic": "#{{base0E-hex}}",
    "tags": "#{{base0F-hex}}",
}

And my tmux­er (zel­li­j):

themes {
    default {
        fg "#{{base05-hex}}"
        bg "#{{base00-hex}}"
        black "#{{base00-hex}}"
        red "#{{base08-hex}}"
        green "#{{base0B-hex}}"
        yellow "#{{base0A-hex}}"
        blue "#{{base0D-hex}}"
        magenta "#{{base0E-hex}}"
        cyan "#{{base0C-hex}}"
        white "#{{base05-hex}}"
        orange "#{{base09-hex}}"
    }
}

And put the right in­can­ta­tions in the flavours con­fig (par­tial):

[[items]]
file = "~/.config/qtile/colors.py"
template = "ralsina"
subtemplate = "qtile"
rewrite = true
hook = "killall -USR1 qtile"

[[items]]
file = "~/.config/zellij/themes/default.kdl"
template = "ralsina"
subtemplate = "zellij"
rewrite = true

I will have to configure everything I want to follow my colour scheme but I can apply it to everything with a simple command like flavours apply monokai

As an ex­tra, here's a script that lets you choose the col­or scheme us­ing rofi:

#!/bin/sh

LAST_SCHEME=$(cat ~/.local/share/flavours/lastscheme)
SELECTED=$(flavours list | sed 's/\s/\n/g' | grep -n $LAST_SCHEME | cut -d: -f1)

flavours apply $(flavours list| sed 's/\s/\n/g' | rofi -dmenu -selected-row $SELECTED)

rofi choosing a color scheme

Conclusion

What can I say, I like it, it's not ug­ly, has all the func­tion­al­i­ty I want­ed and us­es around 400MB of RAM be­fore I start chrome. I am quite en­joy­ing it.

While this site (in­ten­tion­al­ly) has no com­ments, feel free to dis­cuss this in Red­dit


Contents © 2000-2024 Roberto Alsina