My Qtile window manager configuration
git clone https://github.com/m-col/qtile-config
Files | Refs | Readme

Commit 4957dd312ee19f7097988154dbd556f6d1244031
Parent: 3b2e333c9731b4145f78c63ac45e5c9244f7c1cd
Author: mcol <mcol@posteo.net>
Date: 2022-02-06 19:00:24 +0000
Committer: mcol <mcol@posteo.net>
Committed: 2022-02-06 19:00:24 +0000

updates

x11.py Modified

@@ -11,19 +11,26 @@
 mod = "mod1" if IS_XEPHYR else "mod4"
 
 
-term = 'xterm' if IS_XEPHYR else 'urxvt'  # urxvt doesn't like xephyr
-wmname = 'LG3D'
+term = "xterm" if IS_XEPHYR else "urxvt"  # urxvt doesn't like xephyr
+wmname = "LG3D"
 keys_backend = []
 
 
 # Keys to run X11-only launchers
-keys_backend.extend([
-    #([mod],             'd',
-    #   lazy.spawn('rofi -show run -theme ~/.config/rofi/common-large.rasi'), "rofi: run"),
-    ([],    'XF86PowerOff',     lazy.spawn('power-menu'),                   "Power menu"),
-    ([mod, 'shift'],    'x',    lazy.spawn('set_monitors'),                 "Configure monitors"),
-    ([mod, 'shift'],    'i',    lazy.spawn('slock systemctl suspend -i'),   "Suspend system and lock"),
-])
+keys_backend.extend(
+    [
+        # ([mod],             'd',
+        #   lazy.spawn('rofi -show run -theme ~/.config/rofi/common-large.rasi'), "rofi: run"),
+        ([], "XF86PowerOff", lazy.spawn("power-menu"), "Power menu"),
+        ([mod, "shift"], "x", lazy.spawn("set_monitors"), "Configure monitors"),
+        (
+            [mod, "shift"],
+            "i",
+            lazy.spawn("slock systemctl suspend -i"),
+            "Suspend system and lock",
+        ),
+    ]
+)
 
 
 # Auto-float some windows
@@ -34,5 +41,5 @@
         return
 
     hints = window.window.get_wm_normal_hints()
-    if hints and 0 < hints['max_width'] < 1920:
+    if hints and 0 < hints["max_width"] < 1920:
         window.floating = True

wayland.py Modified

@@ -6,30 +6,33 @@
 import os
 import subprocess
 
-from libqtile import hook, qtile
-from libqtile.backend.wayland.window import Window
+from libqtile import hook, qtile, backend
+
 from libqtile.lazy import lazy
 from libqtile.log_utils import logger
 
 IS_XEPHYR = int(os.environ.get("QTILE_XEPHYR", 0))
 mod = "mod1" if IS_XEPHYR else "mod4"
 
-term = 'footclient'
+term = "footclient"
 keys_backend = []
 
 
 # Keys to change VT
-keys_backend.extend([
-    ([mod], 'F1',     lazy.core.change_vt(1),    "Change to VT 1"),
-    ([mod], 'F2',     lazy.core.change_vt(2),    "Change to VT 2"),
-    ([mod], 'F3',     lazy.core.change_vt(3),    "Change to VT 3"),
-    ([mod], 'F4',     lazy.core.change_vt(4),    "Change to VT 4"),
-])
+keys_backend.extend(
+    [
+        ([mod], "F1", lazy.core.change_vt(1), "Change to VT 1"),
+        ([mod], "F2", lazy.core.change_vt(2), "Change to VT 2"),
+        ([mod], "F3", lazy.core.change_vt(3), "Change to VT 3"),
+        ([mod], "F4", lazy.core.change_vt(4), "Change to VT 4"),
+    ]
+)
 
 
 # Configure input devices
 try:
     from libqtile.backend.wayland import InputConfig
+
     wl_input_rules = {
         "type:keyboard": InputConfig(
             kb_options="caps:swapescape,altwin:swap_alt_win",
@@ -47,12 +50,18 @@
 
 
 @hook.subscribe.client_new
-def _(window):
+def _(win):
     # Auto-float some windows
-    if type(window) is Window:
-        state = window.surface.toplevel._ptr.current
+    if isinstance(win, base.Window):
+        state = win.surface.toplevel._ptr.current
         if 0 < state.max_width < 1920:
-            window.floating = True
+            win.floating = True
+
+
+@hook.subscribe.client_managed
+def _(win):
+    if win.name == "Firefox — Sharing Indicator":
+        win.place(win.x + win.borderwidth, 0, win.width, win.height, 0, None)
 
 
 @hook.subscribe.startup_once
@@ -67,7 +76,7 @@
     env["WOB_PADDING"] = "0"
     env["WOB_BACKGROUND"] = "#00000000"
     env["WOB_BAR"] = "#66CFCCD6"
-    HOME = os.path.expanduser('~')
+    HOME = os.path.expanduser("~")
     p = subprocess.Popen(f"{HOME}/.config/qtile/startup.sh", shell=True, env=env)
     hook.subscribe.shutdown(p.terminate)
 

startup.sh Modified

@@ -8,32 +8,28 @@
 {
     foot --server &
     #swaybg -c '#1B2021' &
-} &> /dev/null
-
+    xsetroot -cursor_name left_ptr
 
-# wob
-FIFO=/tmp/wob-$WAYLAND_DISPLAY
-test -e $FIFO || mkfifo $FIFO
-tail -f $FIFO | wob -a bottom \
-    -M ${WOB_MARGIN:-0} -H ${WOB_HEIGHT:-32} -W ${WOB_WIDTH:-400} \
-    -o ${WOB_OFFSET:-0} -b ${WOB_BORDER:-0} -p ${WOB_PADDING:-0} \
-    --background-color ${WOB_BACKGROUND:-#ff000000} \
-    --bar-color ${WOB_BAR:-#00ffffff} -t 800 &
+    # wob
+    FIFO=/tmp/wob-$WAYLAND_DISPLAY
+    test -e $FIFO || mkfifo $FIFO
+    tail -f $FIFO | wob -a bottom \
+	-M ${WOB_MARGIN:-0} -H ${WOB_HEIGHT:-32} -W ${WOB_WIDTH:-400} \
+	-o ${WOB_OFFSET:-0} -b ${WOB_BORDER:-0} -p ${WOB_PADDING:-0} \
+	--background-color ${WOB_BACKGROUND:-#ff000000} \
+	--bar-color ${WOB_BAR:-#00ffffff} -t 800 &
 
-
-# TTY session only
+} &> /dev/null
 
 run_if_new() { ps aux | grep -v grep | grep -q $1 || $@; }
 
 [[ -z "$QTILE_XEPHYR" ]] && {
-    wlsunset -t 2500 -T 5700 -l 55.7 -L -3.1 &
     kanshi &
+    wlsunset -t 2500 -T 5700 -l 55.7 -L -3.1 &
     run_if_new firefox &
     run_if_new irc &
-
-    # For screensharing
     systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
-
+    swayidle &
     sleep 3
     check_systemd
 } &> /dev/null

scratchpad.py Modified

@@ -13,7 +13,7 @@
 from libqtile.config import DropDown, ScratchPad
 from libqtile.lazy import lazy
 
-HOME: str = os.path.expanduser('~')
+HOME: str = os.path.expanduser("~")
 IS_WAYLAND: bool = qtile.core.name == "wayland"
 IS_XEPHYR: bool = int(os.environ.get("QTILE_XEPHYR", 0)) > 0
 mod = "mod1" if IS_XEPHYR else "mod4"
@@ -36,16 +36,33 @@
 
 dropdowns = [
     DropDown("tmux", term + "tmux", height=0.4, **conf),
-    DropDown("ncmpcpp", term + "ncmpcpp", x=0.12, y=0.2, width=0.56, height=0.7, **conf),
+    DropDown(
+        "ncmpcpp", term + "ncmpcpp", x=0.12, y=0.2, width=0.56, height=0.7, **conf
+    ),
     DropDown("python", term + "python", x=0.05, y=0.1, width=0.2, height=0.3, **conf),
 ]
 
 
 # Keybindings to open each DropDown
 keys_scratchpad = [
-    ([mod, 'shift'], 'Return', lazy.group['scratchpad'].dropdown_toggle('tmux'), "Toggle tmux scratchpad"),
-    ([mod, 'control'], 'm', lazy.group['scratchpad'].dropdown_toggle('ncmpcpp'), "Toggle ncmpcpp scratchpad"),
-    ([mod], 'c', lazy.group['scratchpad'].dropdown_toggle('python'), "Toggle python scratchpad"),
+    (
+        [mod, "shift"],
+        "Return",
+        lazy.group["scratchpad"].dropdown_toggle("tmux"),
+        "Toggle tmux scratchpad",
+    ),
+    (
+        [mod, "control"],
+        "m",
+        lazy.group["scratchpad"].dropdown_toggle("ncmpcpp"),
+        "Toggle ncmpcpp scratchpad",
+    ),
+    (
+        [mod],
+        "c",
+        lazy.group["scratchpad"].dropdown_toggle("python"),
+        "Toggle python scratchpad",
+    ),
 ]
 
 scratchpad = ScratchPad("scratchpad", dropdowns)

power_menu.py Modified

@@ -1,8 +1,4 @@
-from qtile_extras.popup.toolkit import (
-    PopupRelativeLayout,
-    PopupImage,
-    PopupText
-)
+from qtile_extras.popup.toolkit import PopupRelativeLayout, PopupImage, PopupText
 
 from libqtile.lazy import lazy
 
@@ -15,10 +11,7 @@
             pos_y=0.1,
             width=0.1,
             height=0.5,
-            mouse_callbacks={
-                "Button1":
-                lazy.spawn("swaylock")
-            }
+            mouse_callbacks={"Button1": lazy.spawn("swaylock")},
         ),
         PopupImage(
             filename="~/pictures/icons/sleep.svg",
@@ -26,10 +19,7 @@
             pos_y=0.1,
             width=0.1,
             height=0.5,
-            mouse_callbacks={
-                "Button1":
-                lazy.spawn("systemctl suspend", shell=True)
-            }
+            mouse_callbacks={"Button1": lazy.spawn("systemctl suspend", shell=True)},
         ),
         PopupImage(
             filename="~/pictures/icons/shutdown.svg",
@@ -38,26 +28,13 @@
             width=0.1,
             height=0.5,
             highlight="A00000",
-            mouse_callbacks={
-                "Button1":
-                lazy.spawn("backup-home-and-poweroff")
-            }
+            mouse_callbacks={"Button1": lazy.spawn("backup-home-and-poweroff")},
         ),
         PopupText(
-            text="Lock",
-            pos_x=0.1,
-            pos_y=0.7,
-            width=0.2,
-            height=0.2,
-            h_align="center"
+            text="Lock", pos_x=0.1, pos_y=0.7, width=0.2, height=0.2, h_align="center"
         ),
         PopupText(
-            text="Sleep",
-            pos_x=0.4,
-            pos_y=0.7,
-            width=0.2,
-            height=0.2,
-            h_align="center"
+            text="Sleep", pos_x=0.4, pos_y=0.7, width=0.2, height=0.2, h_align="center"
         ),
         PopupText(
             text="Shutdown",
@@ -65,7 +42,7 @@
             pos_y=0.7,
             width=0.2,
             height=0.2,
-            h_align="center"
+            h_align="center",
         ),
     ]
     PopupRelativeLayout(
@@ -77,6 +54,7 @@
         initial_focus=2,
     ).show(centered=True)
 
+
 keys_power_menu = [
-    ([], 'XF86PowerOff', lazy.function(show_power_menu), "Display the power menu.")
+    ([], "XF86PowerOff", lazy.function(show_power_menu), "Display the power menu.")
 ]

groups.py Modified

@@ -28,12 +28,12 @@
 
 
 groups: List[Group] = [
-    Group('1', label='term'),
-    Group('2', label='term'),
-    Group('3', label='term'),
-    Group('q', label='surf', matches=[Match(wm_class='firefox')]),
-    Group('w', label='mail', matches=[Match(wm_class='geary'), Match(title='Geary')]),
-    Group('e', label='chat', matches=[Match(title='irc')]),
+    Group("1", label="term"),
+    Group("2", label="term"),
+    Group("3", label="term"),
+    Group("q", label="surf", matches=[Match(wm_class="firefox")]),
+    Group("w", label="mail", matches=[Match(wm_class="geary"), Match(title="Geary")]),
+    Group("e", label="chat", matches=[Match(title="irc")]),
 ]
 
 
@@ -44,29 +44,42 @@
     E.g. going to the fourth group when the first group (and first screen) is focussed
     will also change the screen to the second screen.
     """
+
     def _inner(qtile: Qtile) -> None:
         if len(qtile.screens) == 1:
             qtile.groups_map[name].cmd_toscreen(toggle=True)
             return
 
         old = qtile.current_screen.group.name
-        if name in '123':
+        if name in "123":
             qtile.focus_screen(0)
-            if old in '123' or qtile.current_screen.group.name != name:
+            if old in "123" or qtile.current_screen.group.name != name:
                 qtile.groups_map[name].cmd_toscreen(toggle=True)
         else:
             qtile.focus_screen(1)
-            if old in 'qwe' or qtile.current_screen.group.name != name:
+            if old in "qwe" or qtile.current_screen.group.name != name:
                 qtile.groups_map[name].cmd_toscreen(toggle=True)
 
     return _inner
 
 
 for i in groups:
-    keys_group.extend([
-        ([mod],             i.name, lazy.function(_go_to_group(i.name)), f"Go to group {i.name}"),
-        ([mod, 'shift'],    i.name, lazy.window.togroup(i.name), f"Send window to group {i.name}"),
-    ])
+    keys_group.extend(
+        [
+            (
+                [mod],
+                i.name,
+                lazy.function(_go_to_group(i.name)),
+                f"Go to group {i.name}",
+            ),
+            (
+                [mod, "shift"],
+                i.name,
+                lazy.window.togroup(i.name),
+                f"Send window to group {i.name}",
+            ),
+        ]
+    )
 
 
 def _scroll_screen(direction: int) -> Callable:
@@ -74,6 +87,7 @@
     Scroll to the next/prev group of the subset allocated to a specific screen. This
     will rotate between e.g. 1->2->3->1 when the first screen is focussed.
     """
+
     def _inner(qtile: Qtile):
         if len(qtile.screens) == 1:
             current = qtile.groups.index(qtile.current_group)
@@ -91,24 +105,26 @@
     return _inner
 
 
-keys_group.extend([
-    ([mod], 'm', lazy.function(_scroll_screen(1)),  "Screen groups forward"),
-    ([mod], 'n', lazy.function(_scroll_screen(-1)), "Screen groups backward"),
-])
+keys_group.extend(
+    [
+        ([mod], "m", lazy.function(_scroll_screen(1)), "Screen groups forward"),
+        ([mod], "n", lazy.function(_scroll_screen(-1)), "Screen groups backward"),
+    ]
+)
 
 
 @hook.subscribe.startup
 def _():
     # Set initial groups
     if len(qtile.screens) > 1:
-        qtile.groups_map['1'].cmd_toscreen(0, toggle=False)
-        qtile.groups_map['q'].cmd_toscreen(1, toggle=False)
+        qtile.groups_map["1"].cmd_toscreen(0, toggle=False)
+        qtile.groups_map["q"].cmd_toscreen(1, toggle=False)
 
 
 @hook.subscribe.screen_change
 def _(_):
     # Set groups to screens
     if len(qtile.screens) > 1:
-        if qtile.screens[0].group.name not in '123':
-            qtile.groups_map['1'].cmd_toscreen(0, toggle=False)
-        qtile.groups_map['q'].cmd_toscreen(1, toggle=False)
+        if qtile.screens[0].group.name not in "123":
+            qtile.groups_map["1"].cmd_toscreen(0, toggle=False)
+        qtile.groups_map["q"].cmd_toscreen(1, toggle=False)

config.py Modified

@@ -20,7 +20,7 @@
 from libqtile.widget.battery import Battery, BatteryState
 
 if TYPE_CHECKING:
-    from typing import Any, List, Tuple
+    from typing import Any
 
     from libqtile.core.manager import Qtile
 
@@ -102,7 +102,7 @@
     alt = "mod1"
 
 # My keybindings - they are converted at the bottom of this file
-my_keys: List[Tuple[List[str], str, Any, str]] = []
+my_keys: list[tuple[list[str], str, Any, str]] = []
 my_keys.extend(keys_backend)
 my_keys.extend(keys_group)
 my_keys.extend(keys_scratchpad)
@@ -203,8 +203,9 @@
             lazy.widget["mpd2"].function(lambda w: w.button_press(0, 0, 4)),
             "Previous song",
         ),
+        ([], "Pause", lazy.spawn("mpc toggle"), "Play/unpause music"),
         # Launchers
-        ([mod], "d", lazy.spawncmd(shell=False), "Spawn with Prompt"),
+        ([mod], "d", lazy.spawn("rofi -show run"), "Spawn with rofi"),
         ([mod], "Return", lazy.spawn(term), "Spawn terminal"),
         ([mod, "shift"], "f", lazy.spawn("firefox"), "Spawn Firefox"),
         (
@@ -217,7 +218,6 @@
         (["shift"], "Print", lazy.spawn("screenshot"), "Screenshot to file"),
         ([mod], "p", lazy.spawn("get_password_rofi"), "Keepass passwords"),
         ([mod], "i", lazy.spawn("systemctl suspend -i"), "Suspend system"),
-        ([], "Pause", lazy.spawn("mpc toggle"), "Play/unpause music"),
     ]
 )
 
@@ -229,7 +229,7 @@
         [mod],
         "Button1",
         lazy.window.set_position_floating(),
-        #move_snap_window(snap_dist=20),
+        # move_snap_window(snap_dist=20),
         start=lazy.window.get_position(),
     ),
     Drag(
@@ -245,12 +245,6 @@
 border_focus = [colours[5]]
 border_normal = "001122"
 
-# import qtools.borders
-# qtools.borders.enable('cde')
-# border_focus = [border_focus, colours[5]]
-# border_normal = [colours[0], colours[8]]
-
-
 layouts = [
     layout.Columns(
         insert_position=1,
@@ -274,10 +268,10 @@
     fullscreen_border_width=0,
     float_rules=[
         Match(title="Open File"),
-        Match(title="Unlock Database - KeePassXC"),  # Wayland
-        Match(title="File Operation Progress", wm_class="thunar"),  # Wayland
-        Match(wm_class="thunar"),  # Wayland
-        Match(title="Firefox — Sharing Indicator"),  # Wayland
+        Match(title="Unlock Database - KeePassXC"),
+        Match(title="File Operation Progress", wm_class="thunar"),
+        Match(wm_class="thunar"),
+        Match(title="Firefox — Sharing Indicator"),
         Match(wm_class="Arandr"),
         Match(wm_class="org.gnome.clocks"),
         Match(wm_class="org.kde.ark"),
@@ -302,8 +296,9 @@
         Match(wm_class="Xephyr"),
         Match(wm_type="dialog"),
         Match(role="gimp-file-export"),
-        Match(func=lambda c: c.has_fixed_size()),
+        Match(func=base.Window.has_fixed_size),
         Match(func=lambda c: bool(c.is_transient_for())),
+        Match(title="KDE Connect Daemon"),
     ],
 )
 
@@ -337,29 +332,6 @@
     font="TamzenForPowerline Bold",
     update_interval=10,
 )
-mpd2.mouse_buttons = {v: k for k, v in widget.mpd2widget.keys.items()}
-
-cpugraph = widget.CPUGraph(
-    graph_color=colours[12],
-    fill_color=colour_unfocussed,
-    border_width=0,
-    margin_x=10,
-    margin_y=4,
-    samples=50,
-    line_width=4,
-    width=50 if IS_XEPHYR else 200,
-    type="box",
-    frequency=1,
-)
-
-bklight = widget.Backlight(
-    backlight_name=os.listdir("/sys/class/backlight")[0],
-    step=1,
-    update_interval=None,
-    format="",
-    change_command=None,
-    foreground=colours[3],
-)
 
 
 class MyVolume(widget.Volume):
@@ -410,6 +382,15 @@
         self._update_drawer(wob=False)
 
 
+bklight = widget.Backlight(
+    backlight_name=os.listdir("/sys/class/backlight")[0],
+    step=1,
+    update_interval=None,
+    format="",
+    change_command=None,
+    foreground=colours[3],
+)
+
 volume = MyVolume(
     fontsize=18,
     channel="PCM",
@@ -434,6 +415,11 @@
 
 
 class MyBattery(Battery):
+    """
+    This is basically the Battery widget except it uses some icons, and if you click it
+    it will show the percentage numerically for 1 second.
+    """
+
     def build_string(self, status):
         if self.layout is not None:
             self.layout.colour = self.foreground
@@ -491,6 +477,7 @@
     foreground=colours[7],
     update_interval=60,
 )
+
 time = widget.Clock(
     fontsize=20,
     font="TamzenForPowerline Medium",
@@ -512,34 +499,16 @@
         groupboxes[0].visible_groups = ["1", "2", "3", "q", "w", "e"]
 
 
-@hook.subscribe.client_focus
-def _(_):
-    # Keep Static windows on top
-    for window in qtile.windows_map.values():
-        if isinstance(window, base.Static):
-            if hasattr(window, "cmd_bring_to_front"):
-                window.cmd_bring_to_front()
-
-
-# @hook.subscribe.screens_reconfigured
-# async def _():
-#    # Reconfigure GroupBox visible groups
-#    if len(qtile.screens) > 1:
-#        groupboxes[0].visible_groups = ['1', '2', '3']
-#    else:
-#        groupboxes[0].visible_groups = ['1', '2', '3', 'q', 'w', 'e']
-#    if hasattr(groupboxes[0], 'bar'):
-#        groupboxes[0].bar.draw()
-
+@hook.subscribe.screens_reconfigured
+def _():
+    # Reconfigure GroupBox visible groups
+    if len(qtile.screens) > 1:
+        groupboxes[0].visible_groups = ["1", "2", "3"]
+    else:
+        groupboxes[0].visible_groups = ["1", "2", "3", "q", "w", "e"]
+    if hasattr(groupboxes[0], "bar"):
+        groupboxes[0].bar.draw()
 
-prompt = widget.Prompt(
-    fontsize=20,
-    font="TamzenForPowerline Medium",
-    cursor_color=foreground,
-    visual_bell_color=foreground,
-    background=colours[5],
-    bell_style="visual",
-)
 
 bar_border_width = [0, 3, 0, 3]
 bar_border_color = ["000000", colours[5], "000000", colours[5]]
@@ -549,22 +518,18 @@
         bottom=bar.Bar(
             [
                 groupboxes[0],
-                # cpugraph,
-                prompt,  # Left
-                # widget.TaskList(icon_only=True),
                 widget.Spacer(),
-                mpd2,  # Centre
+                mpd2,
                 widget.Spacer(),
                 systray,
-                bklight,
                 volume,
+                bklight,
                 wlan,
                 battery,
                 date,
-                time,  # Right
+                time,
             ],
             28,
-            # background=background + '33',
             background=background + "00",
         ),
         top=bar.Gap(outer_gaps),
@@ -577,17 +542,15 @@
         bottom=bar.Bar(
             [
                 groupboxes[1],
-                # cpugraph,
-                prompt,  # Left
                 widget.Spacer(),
-                mpd2,  # Centre
+                mpd2,
                 widget.Spacer(),
-                bklight,
                 volume,
+                bklight,
                 wlan,
                 battery,
                 date,
-                time,  # Right
+                time,
             ],
             28,
             background=background + "00",
@@ -601,34 +564,21 @@
 ]
 
 
-# Graphical notifications
-reload("graphical_notifications")
-from graphical_notifications import Notifier
-
-notifier = Notifier(
-    background=colours[12],
-    foreground=background,
-    x=50,
-    y=50,
-    width=320,
-    height=100,
-    font_size=18,
-    font="TamzenForPowerline Bold",
-)
+@hook.subscribe.client_focus
+def _(_):
+    # Keep Static windows on top
+    for window in qtile.windows_map.values():
+        if isinstance(window, base.Static):
+            if hasattr(window, "cmd_bring_to_front"):
+                window.cmd_bring_to_front()
 
-my_keys.extend(
-    [
-        ([mod], "grave", lazy.function(notifier.prev), "Previous notification"),
-        ([mod, "shift"], "grave", lazy.function(notifier.next), "Next notification"),
-    ]
-)
 
-# Config variables
 reconfigure_screens = True
 follow_mouse_focus = True
 bring_front_click = True
 cursor_warp = False
 auto_fullscreen = True
 focus_on_window_activation = "smart"
+
 keys = [Key(mods, key, cmd, desc=desc) for mods, key, cmd, desc in my_keys]
 groups.append(scratchpad)