From 6aaea9cf4b283d41016e60735f52c8feb3cd0c9e Mon Sep 17 00:00:00 2001
From: Justine Smithies <justine@smithies.me.uk>
Date: Tue, 22 Aug 2023 19:46:15 +0100
Subject: Initial commit

---
 .config/qtile/autostart.sh                |  41 +++++
 .config/qtile/colors.py                   |  51 ++++++
 .config/qtile/config.py                   |  43 +++++
 .config/qtile/groups.py                   |  58 +++++++
 .config/qtile/hooks.py                    |  82 +++++++++
 .config/qtile/keys.py                     | 277 ++++++++++++++++++++++++++++++
 .config/qtile/layouts.py                  |  56 ++++++
 .config/qtile/mouse.py                    |  28 +++
 .config/qtile/ordinaldate.py              |  20 +++
 .config/qtile/screens.py                  |  24 +++
 .config/qtile/statusbar/battery.py        |  89 ++++++++++
 .config/qtile/statusbar/brightnesscontrol |  44 +++++
 .config/qtile/statusbar/calendar.sh       |  26 +++
 .config/qtile/statusbar/idleinhibit       |  32 ++++
 .config/qtile/statusbar/network.sh        |  45 +++++
 .config/qtile/statusbar/void-updates.sh   |  13 ++
 .config/qtile/statusbar/volumecontrol     | 107 ++++++++++++
 .config/qtile/widgets.py                  | 106 ++++++++++++
 .config/qtile/workspaces.py               |  93 ++++++++++
 19 files changed, 1235 insertions(+)
 create mode 100755 .config/qtile/autostart.sh
 create mode 100644 .config/qtile/colors.py
 create mode 100644 .config/qtile/config.py
 create mode 100644 .config/qtile/groups.py
 create mode 100644 .config/qtile/hooks.py
 create mode 100644 .config/qtile/keys.py
 create mode 100644 .config/qtile/layouts.py
 create mode 100644 .config/qtile/mouse.py
 create mode 100644 .config/qtile/ordinaldate.py
 create mode 100644 .config/qtile/screens.py
 create mode 100755 .config/qtile/statusbar/battery.py
 create mode 100755 .config/qtile/statusbar/brightnesscontrol
 create mode 100755 .config/qtile/statusbar/calendar.sh
 create mode 100755 .config/qtile/statusbar/idleinhibit
 create mode 100755 .config/qtile/statusbar/network.sh
 create mode 100755 .config/qtile/statusbar/void-updates.sh
 create mode 100755 .config/qtile/statusbar/volumecontrol
 create mode 100644 .config/qtile/widgets.py
 create mode 100644 .config/qtile/workspaces.py

(limited to '.config/qtile')

diff --git a/.config/qtile/autostart.sh b/.config/qtile/autostart.sh
new file mode 100755
index 0000000..ebd7534
--- /dev/null
+++ b/.config/qtile/autostart.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Autostart script for Qtile
+
+export wallpaper='~/.cache/wallpaper'
+
+dbus-update-activation-environment --systemd \
+	WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=$XDG_CURRENT_DESKTOP
+
+# Authentication dialog
+
+pkill -f /usr/libexec/polkit-gnome-authentication-agent-1
+/usr/libexec/polkit-gnome-authentication-agent-1 &
+
+# Kill any existing pipewire / wireplumber daemons and only then try to start a new set.
+
+pkill -u "${USER}" -x pipewire\|wireplumber 1>/dev/null 2>&1
+dbus-run-session pipewire &> /dev/null &
+
+# Kanshi
+
+pkill -f kanshi
+kanshi &
+
+# Notification daemon
+
+pkill -f dunst
+dunst &
+
+# wlsunset
+
+pkill -f wlsunset
+wlsunset -l 57.4 -L -1.9 &
+
+# Swayidle daemon
+
+pkill -f swayidle
+swayidle \
+	timeout 5 'qtile cmd-obj -o core -f hide_cursor' resume 'qtile cmd-obj -o core -f unhide_cursor' \
+	timeout 300 'swaylock -f -i $wallpaper' \
+	timeout 600 'wlopm --off \*' resume 'wlopm --on \*' &
diff --git a/.config/qtile/colors.py b/.config/qtile/colors.py
new file mode 100644
index 0000000..749b12f
--- /dev/null
+++ b/.config/qtile/colors.py
@@ -0,0 +1,51 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# gruvbox color configuration
+# https://github.com/morhetz/gruvbox/blob/master/colors/gruvbox.vim
+colors = {
+    'dark0_hard': '#1d2021',
+    'dark0': '#282828',
+    'dark0_soft': '#32302f',
+    'dark1': '#3c3836',
+    'dark2': '#504945',
+    'dark3': '#665c54',
+    'dark4': '#7c6f64',
+    'dark4_256': '#7c6f64',
+
+    'gray_245': '#928374',
+    'gray_244': '#928374',
+
+    'light0_hard': '#f9f5d7',
+    'light0': '#fbf1c7',
+    'light0_soft': '#f2e5bc',
+    'light1': '#ebdbb2',
+    'light2': '#d5c4a1',
+    'light3': '#bdae93',
+    'light4': '#a89984',
+    'light4_256': '#a89984',
+
+    'bright_red': '#fb4934',
+    'bright_green': '#b8bb26',
+    'bright_yellow': '#fabd2f',
+    'bright_blue': '#83a598',
+    'bright_purple': '#d3869b',
+    'bright_aqua': '#8ec07c',
+    'bright_orange': '#fe8019',
+
+    'neutral_red': '#cc241d',
+    'neutral_green': '#98971a',
+    'neutral_yellow': '#d79921',
+    'neutral_blue': '#458588',
+    'neutral_purple': '#b16286',
+    'neutral_aqua': '#689d6a',
+    'neutral_orange': '#d65d0e',
+
+    'faded_red': '#9d0006',
+    'faded_green': '#79740e',
+    'faded_yellow': '#b57614',
+    'faded_blue': '#076678',
+    'faded_purple': '#8f3f71',
+    'faded_aqua': '#427b58',
+    'faded_orange': '#af3a03',
+}
diff --git a/.config/qtile/config.py b/.config/qtile/config.py
new file mode 100644
index 0000000..122d63a
--- /dev/null
+++ b/.config/qtile/config.py
@@ -0,0 +1,43 @@
+# Qtile Config File
+# http://www.qtile.org/
+
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+from typing import List  # noqa: F401
+import hooks
+from keys import mod, keys, home
+from workspaces import workspaces
+from groups import groups
+from layouts import layouts, floating_layout
+from widgets import widget_defaults, extension_defaults
+from screens import screens
+from mouse import mouse
+from libqtile.backend.wayland import InputConfig
+
+# Configure input devices
+
+wl_input_rules = {
+    "type:keyboard": InputConfig(
+        kb_layout='us',
+    ),
+}
+
+dgroups_key_binder = None
+dgroups_app_rules = []  # type: List
+follow_mouse_focus = True
+bring_front_click = False
+cursor_warp = True
+auto_fullscreen = True
+focus_on_window_activation = "smart"
+reconfigure_screens = True
+
+# XXX: Gasp! We're lying here. In fact, nobody really uses or cares about this
+# string besides java UI toolkits; you can see several discussions on the
+# mailing lists, GitHub issues, and other WM documentation that suggest setting
+# this string if your java app doesn't work correctly. We may as well just lie
+# and say that we're a working one by default.
+#
+# We choose LG3D to maximize irony: it is a 3D non-reparenting WM written in
+# java that happens to be on java's whitelist.
+wmname = "Qtile"
diff --git a/.config/qtile/groups.py b/.config/qtile/groups.py
new file mode 100644
index 0000000..3f13d52
--- /dev/null
+++ b/.config/qtile/groups.py
@@ -0,0 +1,58 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# Groups configuration
+
+from libqtile.config import Key, Group
+from libqtile.command import lazy
+from keys import mod, keys
+from workspaces import workspaces
+from screens import connected_monitors
+from libqtile.config import ScratchPad, DropDown
+from os import environ
+
+# Get terminal from environment variables
+terminal = environ.get("TERMINAL")
+
+groups = []
+for workspace in workspaces:
+    matches = workspace["matches"] if "matches" in workspace else None
+    layouts = workspace["layout"] if "layout" in workspace else None
+    groups.append(Group(workspace["name"], matches=matches, layout=layouts))
+    keys.append(Key([mod], workspace["key"], lazy.group[workspace["name"]].toscreen()))
+    keys.append(Key([mod, "shift"], workspace["key"], lazy.window.togroup(workspace["name"])))
+
+for i in range(connected_monitors):
+    keys.extend([Key([mod, "mod1"], str(i), lazy.window.toscreen(i))])
+
+# Append a scratchpad group
+
+conf = {
+    "warp_pointer": False,
+    "on_focus_lost_hide": False,
+    "opacity": 0.80,
+}
+
+groups.append(
+    ScratchPad(
+        "scratchpad", [
+            # Define a drop down terminal
+            # it is placed in the upper third of the screen by default
+            DropDown(
+                "term",
+                terminal + " -a 'Terminal'",
+                height=0.50,
+                width=0.50,
+                x=0.25,
+                y=0.2,
+                **conf
+            ),
+
+        ]
+    )
+)
+
+# Define keys to toggle the dropdown terminals
+keys.extend([
+    Key([mod, "shift"], "Return", lazy.group["scratchpad"].dropdown_toggle("term")),
+])
diff --git a/.config/qtile/hooks.py b/.config/qtile/hooks.py
new file mode 100644
index 0000000..295c265
--- /dev/null
+++ b/.config/qtile/hooks.py
@@ -0,0 +1,82 @@
+# Qtile Config File
+# http://www.qtile.org/
+
+# Justine Smithies
+# Hooks configuration
+
+import asyncio
+import os
+import subprocess
+import time
+from libqtile import qtile
+from libqtile import hook
+
+from groups import groups
+from libqtile.log_utils import logger
+
+
+@hook.subscribe.startup_once
+def autostart():
+    home = os.path.expanduser('~')
+    subprocess.Popen([home + '/.config/qtile/autostart.sh'])
+
+# Reload config on screen changes
+
+
+@hook.subscribe.screens_reconfigured
+async def outputs_changed():
+    logger.warning("Screens reconfigured")
+    await asyncio.sleep(1)
+    logger.warning("Reloading config...")
+    qtile.reload_config()
+
+# When application launched automatically focus it's group
+
+
+@hook.subscribe.client_new
+def modify_window(client):
+    for group in groups:  # follow on auto-move
+        match = next((m for m in group.matches if m.compare(client)), None)
+        if match:
+            targetgroup = client.qtile.groups_map[group.name]  # there can be multiple instances of a group
+            targetgroup.toscreen(toggle=False)
+            break
+
+# Hook to fallback to the first group with windows when last window of group is killed
+
+
+# @hook.subscribe.client_killed
+# def fallback(window):
+    #     if window.group.windows != [window]:
+#    if isinstance(window, base.Static) or window.group.windows != [window]:
+#        return
+#    idx = qtile.groups.index(window.group)
+#    for group in qtile.groups[idx - 1::-1]:
+#        if group.windows:
+#            qtile.current_screen.toggle_group(group)
+#            return
+#    qtile.current_screen.toggle_group(qtile.groups[0])
+
+# Work around for matching Spotify
+
+
+@hook.subscribe.client_new
+def slight_delay(window):
+    time.sleep(0.04)
+
+# If Spotify opens move it to group 6
+
+
+@hook.subscribe.client_name_updated
+def spotify(window):
+    if window.name == 'Spotify':
+        window.togroup(group_name='阮 ₆')
+
+# If mpv opens float it at pos x, y, w, h, borderwidth, border color
+
+
+@hook.subscribe.client_managed
+def repos(window):
+    if window.get_wm_class() and 'mpv' in window.get_wm_class():
+        window.floating = True
+        window.place(1200, 650, 640, 360, 2, "#ffffff")
diff --git a/.config/qtile/keys.py b/.config/qtile/keys.py
new file mode 100644
index 0000000..75e5bde
--- /dev/null
+++ b/.config/qtile/keys.py
@@ -0,0 +1,277 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# Key configuration
+
+import os
+from libqtile.config import Key
+from libqtile.command import lazy
+
+home = os.path.expanduser('~')
+terminal = os.environ.get("TERMINAL")
+mod = "mod4"
+
+keys = [
+    # Move focus to next screen
+    Key([mod, "control"], "period",
+        lazy.next_screen(),
+        desc="Move focus to next screen",
+        ),
+    Key([mod], "g",
+        lazy.screen.next_group(skip_empty=True),
+        desc="Move to next active group"
+        ),
+    Key([mod, "shift"], "g",
+        lazy.screen.prev_group(skip_empty=True),
+        desc="Move to previous active group"
+        ),
+    # Switch between windows in current stack pane
+    Key([mod], "h",
+        lazy.layout.left(),
+        desc="Move focus left in stack pane"
+        ),
+    Key([mod], "l",
+        lazy.layout.right(),
+        desc="Move focus right in stack pane"
+        ),
+    Key([mod], "k",
+        lazy.layout.down(),
+        desc="Move focus down in stack pane"
+        ),
+    Key([mod], "j",
+        lazy.layout.up(),
+        desc="Move focus up in stack pane"
+        ),
+    # Move window on the current screen
+    Key([mod, "shift"], "h",
+        lazy.layout.shuffle_left(),
+        desc='Shuffle left'
+        ),
+    Key([mod, "shift"], "l",
+        lazy.layout.shuffle_right(),
+        desc='Shuffle right'
+        ),
+    Key([mod, "shift"], "k",
+        lazy.layout.shuffle_down(),
+        desc='Shuffle down'
+        ),
+    Key([mod, "shift"], "j",
+        lazy.layout.shuffle_up(),
+        desc='Shuffle up'
+        ),
+    # For the BSP layout
+    Key([mod, "mod1"], "j",
+        lazy.layout.flip_down(),
+        desc='Flip down'
+        ),
+    Key([mod, "mod1"], "k",
+        lazy.layout.flip_up(),
+        desc='Flip up'
+        ),
+    Key([mod, "mod1"], "h",
+        lazy.layout.flip_left(),
+        desc='Flip left'
+        ),
+    Key([mod, "mod1"], "l",
+        lazy.layout.flip_right(),
+        desc='Flip right'
+        ),
+    Key([mod, "control"], "j",
+        lazy.layout.grow_down(),
+        desc='Grow down'
+        ),
+    Key([mod, "control"], "k",
+        lazy.layout.grow_up(),
+        desc='Grow up'
+        ),
+    Key([mod, "control"], "h",
+        lazy.layout.grow_left(),
+        desc='Grow left'
+        ),
+    Key([mod, "control"], "l",
+        lazy.layout.grow_right(),
+        desc='Grow right'
+        ),
+    Key([mod], "n",
+        lazy.layout.normalize(),
+        desc='normalize window size ratios'
+        ),
+    Key([mod], "m",
+        lazy.layout.maximize(),
+        desc='toggle window between minimum and maximum sizes'
+        ),
+    Key([mod], "o",
+        lazy.layout.grow(),
+        lazy.layout.increase_nmaster(),
+        desc='Expand window (MonadTall), increase number in master pane (Tile)'
+        ),
+    Key([mod], "i",
+        lazy.layout.shrink(),
+        lazy.layout.decrease_nmaster(),
+        desc='Shrink window (MonadTall), decrease number in master pane (Tile)'
+        ),
+
+    # Toggle floating
+    Key([mod, "shift"], "f", lazy.window.toggle_floating(),
+        desc="Toggle floating"
+        ),
+
+    # Toggle Fullscreen
+    Key([mod], "f",
+        lazy.window.toggle_fullscreen(),
+        lazy.hide_show_bar(position='all'),
+        desc='Toggle fullscreen and the bars'
+        ),
+
+    # Switch window focus to other pane(s) of stack
+    Key([mod], "space", lazy.layout.next(),
+        desc="Switch window focus to other pane(s) of stack"
+        ),
+
+    # Swap panes of split stack
+    Key([mod, "shift"], "space",
+        lazy.layout.rotate(),
+        desc="Swap panes of split stack"
+        ),
+
+    # Toggle between split and unsplit sides of stack.
+    # Split = all windows displayed
+    # Unsplit = 1 window displayed, like Max layout, but still with
+    # multiple stack panes
+    Key([mod, "shift"], "s",
+        lazy.layout.toggle_split(),
+        desc="Toggle between split and unsplit sides of stack"
+        ),
+    Key([mod], "Return",
+        lazy.spawn(terminal),
+        desc="Launch terminal"
+        ),
+
+    # Toggle between different layouts as defined below
+    Key([mod], "Tab",
+        lazy.next_layout(),
+        desc="Toggle between layouts"
+        ),
+    Key([mod], "w",
+        lazy.window.kill(),
+        desc="Kill focused window"
+        ),
+
+    # Toggle bars
+    Key([mod], "b",
+        lazy.hide_show_bar(position='all'),
+        desc="Toggle bars"
+        ),
+
+    # Qtile system keys
+    Key([mod, "shift", "control"], "l",
+        lazy.spawn("swaylock -f -i .cache/wallpaper"),
+        desc="Lock screen"
+        ),
+    Key([mod, "control"], "r",
+        lazy.reload_config(),
+        desc="Restart qtile"
+        ),
+    Key([mod, "control"], "q",
+        lazy.shutdown(),
+        desc="Shutdown qtile"
+        ),
+    Key([mod], "r",
+        lazy.spawncmd(),
+        desc="Spawn a command using a prompt widget"
+        ),
+    Key([mod, "control"], "p",
+        lazy.spawn("" + home + "/.local/bin/powermenu"),
+        desc="Launch Power menu"
+        ),
+
+    # Rofi
+    Key(["control"], "space",
+        lazy.spawn("fuzzel"),
+        desc="Launch Fuzzel menu"
+        ),
+    # Window Switcher
+    Key([mod, "control"], "w",
+        lazy.spawn(home + "/.local/bin/qtile-window-switcher.py"),
+        desc="Launch the Window Switcher",
+        ),
+    # Install updates
+    Key([mod, "control"], "u",
+        lazy.spawn(home + "/.local/bin/statusbar/arch-updates.sh key-update"),
+        desc="Install updates",
+        ),
+
+    # Cycle through windows in the floating layout
+    Key([mod, "shift"], "i",
+        lazy.window.toggle_minimize(),
+        lazy.group.next_window(),
+        lazy.window.bring_to_front()
+        ),
+
+    # ------------ Hardware Configs ------------
+    # Volume
+    Key([], "XF86AudioMute",
+        lazy.spawn(home + "/.config/qtile/statusbar/volumecontrol mute"),
+        desc='Mute audio'
+        ),
+    Key([], "XF86AudioLowerVolume",
+        lazy.spawn(home + "/.config/qtile/statusbar/volumecontrol down"),
+        desc='Volume down'
+        ),
+    Key([], "XF86AudioRaiseVolume",
+        lazy.spawn(home + "/.config/qtile/statusbar/volumecontrol up"),
+        desc='Volume up'
+        ),
+
+    # Media keys
+    Key([], "XF86AudioPlay",
+        lazy.spawn("dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify " "/org/mpris/MediaPlayer2 " "org.mpris.MediaPlayer2.Player.PlayPause"),
+        desc='Audio play'
+        ),
+    Key([], "XF86AudioNext",
+        lazy.spawn("dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify " "/org/mpris/MediaPlayer2 " "org.mpris.MediaPlayer2.Player.Next"),
+        desc='Audio next'
+        ),
+    Key([], "XF86AudioPrev",
+        lazy.spawn("dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify " "/org/mpris/MediaPlayer2 " "org.mpris.MediaPlayer2.Player.Previous"),
+        desc='Audio previous'
+        ),
+
+    # Brightness
+    Key([], "XF86MonBrightnessDown",
+        lazy.spawn(home + "/.config/qtile/statusbar/brightnesscontrol down"),
+        desc='Brightness down'
+        ),
+    Key([], "XF86MonBrightnessUp",
+        lazy.spawn(home + "/.config/qtile/statusbar/brightnesscontrol up"),
+        desc='Brightness up'
+        ),
+
+    # Screenshots
+    # Take a screenshot of the currently focused output and save it into screenshots
+    Key([], "Print",
+        lazy.spawn(home + "/.local/bin/screenshot.sh"),
+        desc='Save the screens of the currently focused output to the screenshots folder'
+        ),
+    # Take a screenshot of the selected region
+    Key([mod], "Print",
+        lazy.spawn(home + "/.local/bin/screenshot.sh selected-region"),
+        desc='Save the selected region of the screen to the screenshots folder'
+        ),
+    # Capture region of screen to clipboard
+    Key([mod, "shift"], "Print",
+        lazy.spawn(home + "/.local/bin/screenshot.sh save-to-clipboard"),
+        desc='Capture a region of the screen to the clipboard'
+        ),
+    # Take a screenshot of the selected window
+    Key([mod, "control"], "Print",
+        lazy.spawn(home + "/.local/bin/screenshot.sh selected-window"),
+        desc='Save the selected window to the screenshots folder'
+        ),
+]
+
+for i in range(1, 5):
+    keys.append(Key(["control", "mod1"], "F" + str(i),
+                    lazy.core.change_vt(i),
+                    desc='Change to virtual console ' + str(i)
+                    ),)
diff --git a/.config/qtile/layouts.py b/.config/qtile/layouts.py
new file mode 100644
index 0000000..94c8b6b
--- /dev/null
+++ b/.config/qtile/layouts.py
@@ -0,0 +1,56 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# Layout configuration
+
+from libqtile import layout
+from libqtile.config import Match
+from colors import colors
+
+# DEFAULT THEME SETTINGS FOR LAYOUTS #
+layout_theme = {"border_width": 2,
+                "margin": 10,
+                "border_focus": '#33eeffee',
+                "border_normal": colors['dark0']
+                }
+
+layouts = [
+    layout.MonadTall(**layout_theme, single_border_width=2),
+    layout.Stack(num_stacks=2, **layout_theme),
+    # layout.Max(),
+    # Try more layouts by unleashing below layouts.
+    layout.Bsp(**layout_theme),
+    layout.Columns(**layout_theme),
+
+]
+
+floating_layout = layout.Floating(
+    **layout_theme,
+    float_rules=[
+        # Run the utility of `xprop` to see the wm class and name of an X client.
+        # *layout.Floating.default_float_rules,
+        Match(title='Quit and close tabs?'),
+        Match(wm_type='utility'),
+        Match(wm_type='notification'),
+        Match(wm_type='toolbar'),
+        Match(wm_type='splash'),
+        Match(wm_type='dialog'),
+        Match(wm_class='gimp-2.99'),
+        Match(wm_class='Firefox'),
+        Match(wm_class='file_progress'),
+        Match(wm_class='confirm'),
+        Match(wm_class='dialog'),
+        Match(wm_class='download'),
+        Match(wm_class='error'),
+        Match(wm_class='notification'),
+        Match(wm_class='splash'),
+        Match(wm_class='toolbar'),
+        Match(title='About LibreOffice'),
+        Match(wm_class='confirmreset'),  # gitk
+        Match(wm_class='makebranch'),  # gitk
+        Match(wm_class='maketag'),  # gitk
+        Match(wm_class='ssh-askpass'),  # ssh-askpass
+        Match(title='branchdialog'),  # gitk
+        Match(title='pinentry'),  # GPG key password entry
+    ],
+)
diff --git a/.config/qtile/mouse.py b/.config/qtile/mouse.py
new file mode 100644
index 0000000..6cd0e88
--- /dev/null
+++ b/.config/qtile/mouse.py
@@ -0,0 +1,28 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# Mouse floating layouts.
+
+from libqtile.config import Drag, Click
+from libqtile.command import lazy
+from keys import mod
+
+mouse = [
+    Drag(
+        [mod],
+        "Button1",
+        lazy.window.set_position_floating(),
+        start=lazy.window.get_position()
+    ),
+    Drag(
+        [mod],
+        "Button3",
+        lazy.window.set_size_floating(),
+        start=lazy.window.get_size()
+    ),
+    Click(
+        [mod],
+        "Button2",
+        lazy.window.bring_to_front()
+    )
+]
diff --git a/.config/qtile/ordinaldate.py b/.config/qtile/ordinaldate.py
new file mode 100644
index 0000000..3376d62
--- /dev/null
+++ b/.config/qtile/ordinaldate.py
@@ -0,0 +1,20 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# Ordinal Date - Displays date in format Friday 11th March 2022 - 14:53
+
+# Add th, nd or st to the date - use custom_date in text box to display
+
+from datetime import datetime as dt
+
+
+def suffix(d):
+    return 'th' if 11 <= d <= 13 else {1: 'st', 2: 'nd', 3: 'rd'}.get(d % 10, 'th')
+
+
+def custom_strftime(format, t):
+    return t.strftime(format).replace('{S}', str(t.day) + suffix(t.day))
+
+
+def custom_date():
+    return custom_strftime('%A {S} %B %Y - %H:%M', dt.now())
diff --git a/.config/qtile/screens.py b/.config/qtile/screens.py
new file mode 100644
index 0000000..7921302
--- /dev/null
+++ b/.config/qtile/screens.py
@@ -0,0 +1,24 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# Multimonitor support
+
+from libqtile import qtile
+from libqtile.config import Screen
+from libqtile import bar
+from libqtile.log_utils import logger
+from widgets import primary_widgets, secondary_widgets
+
+
+def status_bar(widgets):
+    return bar.Bar(widgets, 20, background="#000000AA", margin=[10, 10, 0, 10])  # Margin = N E S W
+
+
+screens = [Screen(wallpaper='.cache/wallpaper', wallpaper_mode='fill', top=status_bar(primary_widgets))]
+
+connected_monitors = len(qtile.core.outputs)
+logger.warning(f"Found {connected_monitors} monitor(s)")
+
+if connected_monitors > 1:
+    for _ in range(1, connected_monitors):
+        screens.append(Screen(wallpaper='.cache/wallpaper', wallpaper_mode='fill', top=status_bar(secondary_widgets)))
diff --git a/.config/qtile/statusbar/battery.py b/.config/qtile/statusbar/battery.py
new file mode 100755
index 0000000..a4fa33a
--- /dev/null
+++ b/.config/qtile/statusbar/battery.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+
+import psutil
+import argparse
+import subprocess
+
+
+def secs2hours(secs):
+    mm, ss = divmod(secs, 60)
+    hh, mm = divmod(mm, 60)
+    return "%d:%02d:%02d" % (hh, mm, ss)
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--c',
+                    choices=('status', 'left-click', 'middle-click', 'right-click'),
+                    dest='command',
+                    default='status',
+                    help='Allowed values are status, left-click, middle-click and right-click'
+                    )
+args = parser.parse_args()
+
+battery = psutil.sensors_battery()
+icon = ""
+percent = int(battery.percent)
+time_left = battery.secsleft
+isPlugged = battery.power_plugged
+remaining = secs2hours(time_left)
+
+if args.command == "status":
+    if isPlugged:
+        if percent == 100:
+            icon = "󰂅"
+        elif percent > 89 and percent < 100:
+            icon = "󰂋"
+        elif percent > 79 and percent < 90:
+            icon = "󰂊"
+        elif percent > 69 and percent < 80:
+            icon = "󰢞"
+        elif percent > 59 and percent < 70:
+            icon = "󰂉"
+        elif percent > 49 and percent < 60:
+            icon = "󰢝"
+        elif percent > 39 and percent < 50:
+            icon = "󰂈"
+        elif percent > 29 and percent < 40:
+            icon = "󰂇"
+        elif percent > 19 and percent < 30:
+            icon = "󰂆"
+        elif percent > 9 and percent < 20:
+            icon = "󰢜"
+        elif percent > 0 and percent < 10:
+            icon = "󰢟"
+        message = str(percent) + "%"
+        print(icon, message, end="")
+    else:
+        if percent == 100:
+            icon = "󰁹"
+        elif percent > 89 and percent < 100:
+            icon = "󰂂"
+        elif percent > 79 and percent < 90:
+            icon = "󰂁"
+        elif percent > 69 and percent < 80:
+            icon = "󰂀"
+        elif percent > 59 and percent < 70:
+            icon = "󰁿"
+        elif percent > 49 and percent < 60:
+            icon = "󰁾"
+        elif percent > 39 and percent < 50:
+            icon = "󰁽"
+        elif percent > 29 and percent < 40:
+            icon = "󰁼"
+        elif percent > 19 and percent < 30:
+            icon = "󰁻"
+        elif percent > 9 and percent < 20:
+            icon = "󰁺"
+        elif percent > 0 and percent < 10:
+            icon = "󰂎"
+        message = str(percent) + "%"
+        print(icon, message, end="")
+if args.command == "left-click":
+    if not isPlugged:
+        subprocess.call(["notify-send", "-r", "55555", "-u", "normal", "Est remaining time left: " + remaining])
+    else:
+        subprocess.call(["notify-send", "-r", "55555", "-u", "normal", str(percent) + "% Charged"])
+if args.command == "middle-click":
+    print("Middle click")
+if args.command == "right-click":
+    print("Right click")
diff --git a/.config/qtile/statusbar/brightnesscontrol b/.config/qtile/statusbar/brightnesscontrol
new file mode 100755
index 0000000..304c187
--- /dev/null
+++ b/.config/qtile/statusbar/brightnesscontrol
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# You can call this script like this:
+# brightnessControl up
+# brightnessControl down
+
+# Script inspired by these wonderful people:
+# https://github.com/dastorm/volume-notification-dunst/blob/master/volume.sh
+# https://gist.github.com/sebastiencs/5d7227f388d93374cebdf72e783fbd6a
+
+function send_notification {
+  icon=/usr/share/icons/Papirus-Dark/16x16/actions/brightnesssettings.svg
+  brightness=$(light -G)
+  brightness=$(echo "$brightness" | awk '{print ($0-int($0)<0.499)?int($0):int($0)+1}')
+  # Make the bar with the special character ─ (it's not dash -)
+  # https://en.wikipedia.org/wiki/Box-drawing_character
+  bar=$(seq -s "─" 0 $((brightness / 10 )) | sed 's/[0-9]//g')
+  #brightness=$((brightness *100 / 255 ))
+  #echo $bar
+  #echo $test
+  # Send the notification
+  notify-send -i "$icon" -r 5555 -u normal "$bar  $brightness"
+}
+
+case $1 in
+  up)
+    # increase the backlight by 5%
+    light -A 5
+    send_notification
+    canberra-gtk-play -i audio-volume-change
+    ;;
+  down)
+    # decrease the backlight by 5%
+    light -U 5
+    send_notification
+    canberra-gtk-play -i audio-volume-change
+    ;;
+  *)
+    brightness=$(light -G)
+    brightness=$(echo "$brightness" | awk '{print ($0-int($0)<0.499)?int($0):int($0)+1}')
+    icon="󰃞"
+    printf "%s" "$icon $brightness" "%"
+    ;;
+esac
diff --git a/.config/qtile/statusbar/calendar.sh b/.config/qtile/statusbar/calendar.sh
new file mode 100755
index 0000000..71de700
--- /dev/null
+++ b/.config/qtile/statusbar/calendar.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Calendar script
+
+function ShowCalendar() {
+	notify-send -i "calendar"  "    📅 Calendar" "$(cal --color=always | sed "s/..7m/<b><span color=\"#fabd2f\">/;s/..27m/<\/span><\/b>/")" -r 124
+}
+
+function EditCalendar() {
+  echo 
+}
+
+case "$1" in
+        show)
+            ShowCalendar
+            ;;
+         
+        edit)
+            EditCalendar
+            ;;
+         
+        *)
+            echo $"Usage: ${0##*/} {show|edit}"
+            exit 1
+ 
+esac
diff --git a/.config/qtile/statusbar/idleinhibit b/.config/qtile/statusbar/idleinhibit
new file mode 100755
index 0000000..7ff61a6
--- /dev/null
+++ b/.config/qtile/statusbar/idleinhibit
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Qtile swayidle toggle
+function toggle {
+if pgrep "swayidle" > /dev/null
+then
+	pkill swayidle
+	notify-send -r 5556 -u normal "  Swayidle Inactive"
+else
+	swayidle \
+		timeout 5 'qtile cmd-obj -o core -f hide_cursor' resume 'qtile cmd-obj -o core -f unhide_cursor' \
+		timeout 300 'swaylock -f -i $wallpaper' \
+		timeout 600 'wlopm --off \*' resume 'wlopm --on \*' &
+	notify-send -r 5556 -u normal "  Swayidle Active"
+fi
+}
+
+case $1 in
+	toggle)
+		toggle
+		;;
+	*)
+		if pgrep "swayidle" > /dev/null
+		then
+			icon=""
+		else
+			icon=""
+		fi
+		printf "%s" "$icon "
+		;;
+esac
+
diff --git a/.config/qtile/statusbar/network.sh b/.config/qtile/statusbar/network.sh
new file mode 100755
index 0000000..eb98999
--- /dev/null
+++ b/.config/qtile/statusbar/network.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# This script requires dnsutils aka bind to fetch the WAN IP address
+
+# Shows the connections names
+# nmcli connection show --active | grep 'ethernet' | awk '{ print $1 }' FS='  '
+# nmcli connection show --active | grep 'wifi' | awk '{ print $1 }' FS='  '
+
+# Show ethernet interface name
+# nmcli connection show --active | grep 'ethernet' | awk '{ print $6 }' FS=' '
+
+# Show wifi interface name
+# nmcli connection show --active | grep 'wifi' | awk '{ print $4 }' FS=' '
+
+function ShowInfo {
+	if [ "$(nmcli connection show --active | grep -oh "\w*ethernet\w*")" == "ethernet" ]; then
+		wan="$(dig +short myip.opendns.com @resolver1.opendns.com)"
+		connection="$(nmcli connection show --active | grep 'ethernet' | awk '{ print $6 }' FS=' '): $(nmcli connection show --active | grep 'ethernet' | awk '{ print $1 }' FS='  ') - $(nmcli -t -f IP4.ADDRESS dev show $(nmcli connection show --active | grep 'ethernet' | awk '{ print $6 }' FS=' ') | awk '{print $2}' FS='[:/]')
+WAN IP: $wan"
+	elif [ "$(nmcli connection show --active | grep -oh "\w*wifi\w*")" == "wifi" ]; then
+		wan="$(dig +short myip.opendns.com @resolver1.opendns.com)"
+		connection="$(nmcli connection show --active | grep 'wifi' | awk '{ print $4 }' FS=' '): $(nmcli connection show --active | grep 'wifi' | awk '{ print $1 }' FS='  ') - $(nmcli -t -f IP4.ADDRESS dev show $(nmcli connection show --active | grep 'wifi' | awk '{ print $4 }' FS=' ') | awk '{print $2}' FS='[:/]')
+WAN IP: $wan"
+	else
+		connection="No active connection."
+	fi
+	notify-send -i "network-idle" "$connection" -r 123
+}
+
+function IconUpdate() {
+	if [ "$(nmcli connection show --active | grep -oh "\w*ethernet\w*")" == "ethernet" ]; then
+		icon="󰈀 "
+	elif [ "$(nmcli connection show --active | grep -oh "\w*wifi\w*")" == "wifi" ]; then
+		icon=" "
+	else
+		icon="󰲜 "
+	fi
+	printf "%s" "$icon"
+}
+
+if [ "$1" = "ShowInfo" ]; then
+	ShowInfo
+else
+	IconUpdate	
+fi
diff --git a/.config/qtile/statusbar/void-updates.sh b/.config/qtile/statusbar/void-updates.sh
new file mode 100755
index 0000000..94298ae
--- /dev/null
+++ b/.config/qtile/statusbar/void-updates.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+case $1 in
+  key-update)
+		foot bash -c "sudo xbps-install -Suv"
+		qtile cmd-obj -o widget checkupdates -f force_update
+    ;;
+  *)
+		# Supress error codes to stop issues with CheckUpdates widget
+		xbps-install -Mun 2> /dev/null
+		exit 0
+    ;;
+esac
diff --git a/.config/qtile/statusbar/volumecontrol b/.config/qtile/statusbar/volumecontrol
new file mode 100755
index 0000000..62c4cc2
--- /dev/null
+++ b/.config/qtile/statusbar/volumecontrol
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+# You can call this script like this:
+# volumecontrol up
+# volumecontrol down
+# volumecontrol mute
+
+function get_volume {
+    amixer get Master | grep '%' | head -n 1 | cut -d '[' -f 2 | cut -d '%' -f 1
+}
+
+function is_mute {
+    amixer get Master | grep '%' | grep -oE '[^ ]+$' | grep off > /dev/null
+}
+
+function send_notification {
+	volume=$(get_volume)
+    # Make the bar with the special character ─ (it's not dash -)
+    # https://en.wikipedia.org/wiki/Box-drawing_character
+if [ "$volume" = "0" ]; then
+        icon_name="/usr/share/icons/Adwaita/16x16/legacy/audio-volume-muted.png"
+notify-send -i "$icon_name" -r 5556 -u normal "$volume"
+    else
+	if [  "$volume" -lt "10" ]; then
+	     icon_name="/usr/share/icons/Adwaita/16x16/legacy/audio-volume-low.png"
+notify-send -i "$icon_name" -r 5556 -u normal "$volume"
+    else
+        if [ "$volume" -lt "30" ]; then
+            icon_name="/usr/share/icons/Adwaita/16x16/legacy/audio-volume-low.png"
+        else
+            if [ "$volume" -lt "70" ]; then
+                icon_name="/usr/share/icons/Adwaita/16x16/legacy/audio-volume-medium.png"
+            else
+                icon_name="/usr/share/icons/Adwaita/16x16/legacy/audio-volume-high.png"
+            fi
+        fi
+    fi
+fi
+bar=$(seq -s "─" $(($volume/5)) | sed 's/[0-9]//g')
+# Send the notification
+notify-send -i "$icon_name" -r 5556 -u normal "$bar  $volume"
+}
+
+case $1 in
+    up)
+	# Set the volume on (if it was muted)
+	amixer set Master on > /dev/null
+	# Up the volume (+ 2%)
+	amixer sset Master 2%+ > /dev/null
+	send_notification
+	canberra-gtk-play -i audio-volume-change
+	;;
+    down)
+	amixer set Master on > /dev/null
+	amixer sset Master 2%- > /dev/null
+	send_notification
+	canberra-gtk-play -i audio-volume-change
+	;;
+    mute)
+    	# Toggle mute
+	amixer set Master 1+ toggle > /dev/null
+	if is_mute ; then
+	notify-send -i "/usr/share/icons/Adwaita/16x16/legacy/audio-volume-muted.png" -r 5556 -u normal "$bar Audio Muted"
+	else
+	    send_notification
+	    canberra-gtk-play -i audio-volume-change
+	fi
+	;;
+    *)
+	volume="$(get_volume)"
+
+	if [[ $volume == "100" ]]; then
+		icon="󰕾"
+	elif [[ $volume -ge "89" && $volume -le "100" ]]; then
+		icon="󰕾"
+	elif [[ $volume -ge "79" && $volume -le "90" ]]; then
+		icon="󰕾"
+	elif [[ $volume -ge "69" && $volume -le "80" ]]; then
+		icon="󰖀"
+	elif [[ $volume -ge "59" && $volume -le "70" ]]; then
+		icon="󰖀"
+	elif [[ $volume -ge "49" && $volume -le "60" ]]; then
+		icon="󰖀"
+	elif [[ $volume -ge "39" && $volume -le "50" ]]; then
+		icon="󰖀"
+	elif [[ $volume -ge "29" && $volume -le "40" ]]; then
+		icon="󰕿"
+	elif [[ $volume -ge "19" && $volume -le "30" ]]; then
+		icon="󰕿"
+	elif [[ $volume -ge "9" && $volume -le "20" ]]; then
+		icon="󰕿"
+	elif [[ $volume -gt "0" && $volume -le "10" ]]; then
+		icon="󰕿"
+	elif [[ $volume -eq "0" ]]; then
+		icon="󰝟"
+		volume=" M "
+	fi
+
+
+if is_mute; then
+		icon="󰝟"
+		volume=" M "
+fi
+
+printf "%s" "$icon $volume%"
+  ;;
+esac
diff --git a/.config/qtile/widgets.py b/.config/qtile/widgets.py
new file mode 100644
index 0000000..1072c09
--- /dev/null
+++ b/.config/qtile/widgets.py
@@ -0,0 +1,106 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# Widgets setup
+# Get the icons at https://www.nerdfonts.com/cheat-sheet
+
+import os
+import subprocess
+from libqtile import qtile
+from libqtile import widget
+from colors import colors
+from ordinaldate import custom_date
+from keys import terminal
+
+
+widget_defaults = dict(
+    font='GoMono Nerd Font',
+    fontsize='12',
+    padding=2,
+    foreground=colors['light0']
+)
+extension_defaults = widget_defaults.copy()
+
+primary_widgets = [
+    widget.Spacer(length=10),
+    widget.GroupBox(
+        padding=0,
+        active=colors['light0'],
+        borderwidth=3,
+        inactive=colors['light4'],
+        this_current_screen_border=colors['neutral_green'],
+        this_screen_border=colors['neutral_green'],
+        other_screen_border='#00000000',
+        other_current_screen_border='#00000000',
+        font='GoMono Nerd Font',
+        fontsize=12,
+        highlight_method='line',
+        highlight_color=['00000000', '00000000']
+    ),
+    widget.CurrentLayoutIcon(scale=0.5, **widget_defaults),
+    widget.CurrentLayout(**widget_defaults),
+    widget.Spacer(),
+    widget.GenPollText(
+        func=custom_date,
+        update_interval=1,
+        **widget_defaults,
+        mouse_callbacks={
+            'Button1': lambda: qtile.cmd_spawn(os.path.expanduser("~/.config/qtile/statusbar/calendar.sh show"), shell=True),
+            'Button3': lambda: qtile.cmd_spawn(os.path.expanduser("~/.config/qtile/statusbar/calendar.sh edit"), shell=True)
+        }
+    ),
+    widget.Spacer(),
+    widget.CheckUpdates(
+        **widget_defaults,
+        update_interval=600,
+        distro='Arch_paru',
+        custom_command='~/.config/qtile/statusbar/void-updates.sh',
+        display_format=' {updates}',
+        colour_have_updates=colors['neutral_green'],
+        execute='foot bash -c "sudo xbps-install -Suv"'
+    ),
+    widget.Spacer(length=5),
+    widget.GenPollText(update_interval=1, **widget_defaults, func=lambda: subprocess.check_output(os.path.expanduser("~/.config/qtile/statusbar/idleinhibit")).decode(), mouse_callbacks={'Button1': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/idleinhibit toggle"), shell=True)}),
+    widget.Spacer(length=5),
+    widget.KeyboardLayout(configured_keyboards=['us', 'gb']),
+    widget.Spacer(length=5),
+    widget.GenPollText(update_interval=1, **widget_defaults, func=lambda: subprocess.check_output(os.path.expanduser("~/.config/qtile/statusbar/brightnesscontrol")).decode(), mouse_callbacks={'Button1': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/brightnesscontrol down"), shell=True), 'Button3': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/brightnesscontrol up"), shell=True)}),
+    widget.Spacer(length=5),
+    widget.GenPollText(update_interval=1, **widget_defaults, func=lambda: subprocess.check_output(os.path.expanduser("~/.config/qtile/statusbar/volumecontrol")).decode(), mouse_callbacks={'Button1': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/volumecontrol down"), shell=True), 'Button2': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/volumecontrol mute"), shell=True), 'Button3': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/volumecontrol up"), shell=True)}),
+    widget.Spacer(length=5),
+    widget.GenPollText(update_interval=1, **widget_defaults, func=lambda: subprocess.check_output(os.path.expanduser("~/.config/qtile/statusbar/battery.py")).decode(), mouse_callbacks={'Button1': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/battery.py --c left-click"), shell=True)}),
+    widget.Spacer(length=5),
+    widget.GenPollText(update_interval=1, **widget_defaults, func=lambda: subprocess.check_output(os.path.expanduser("~/.config/qtile/statusbar/network.sh")).decode(), mouse_callbacks={'Button1': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/network.sh ShowInfo"), shell=True), 'Button3': lambda: qtile.spawn(terminal + ' -e nmtui', shell=True)}),
+    widget.Spacer(length=10),
+]
+
+secondary_widgets = [
+    widget.Spacer(length=10),
+    widget.GroupBox(
+        padding=0,
+        active=colors['light0'],
+        borderwidth=3,
+        inactive=colors['light4'],
+        this_current_screen_border=colors['neutral_green'],
+        this_screen_border=colors['neutral_green'],
+        other_screen_border='#00000000',
+        other_current_screen_border='#00000000',
+        font='GoMono Nerd Font',
+        fontsize=12,
+        highlight_method='line',
+        highlight_color=['00000000', '00000000']
+    ),
+    widget.CurrentLayoutIcon(scale=0.6, **widget_defaults),
+    widget.CurrentLayout(**widget_defaults),
+    widget.Spacer(length=440),
+    widget.GenPollText(
+        func=custom_date,
+        update_interval=1,
+        **widget_defaults,
+        mouse_callbacks={
+            'Button1': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/calendar.sh show"), shell=True),
+            'Button3': lambda: qtile.spawn(os.path.expanduser("~/.config/qtile/statusbar/calendar.sh edit"), shell=True)
+        }
+    ),
+    widget.Spacer(),
+]
diff --git a/.config/qtile/workspaces.py b/.config/qtile/workspaces.py
new file mode 100644
index 0000000..42e203f
--- /dev/null
+++ b/.config/qtile/workspaces.py
@@ -0,0 +1,93 @@
+# Justine Smithies
+# https://github.com/justinesmithies/qtile-wayland-dotfiles
+
+# Workspace setup
+# Get the icons at https://www.nerdfonts.com/cheat-sheet
+
+from libqtile.config import Match
+
+workspaces = [
+    {
+        "name": " ₁",
+        "key": "1",
+        "matches": [
+            Match(wm_class='firefox'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+    {
+        "name": " ₂",
+        "key": "2",
+        "matches": [
+            Match(wm_class='foot'),
+            Match(wm_class='ranger'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+    {
+        "name": " ₃",
+        "key": "3",
+        "matches": [
+            Match(wm_class='nvim'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+    {
+        "name": " ₄",
+        "key": "4",
+        "matches": [
+            Match(wm_class='telegram-desktop'),
+            Match(wm_class='weechat'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+    {
+        "name": " ₅",
+        "key": "5",
+        "matches": [
+            Match(wm_class='gimp-2.99'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+    {
+        "name": "󰓇 ₆",
+        "key": "6",
+        "matches": [
+            Match(wm_class='Spotify'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+    {
+        "name": " ₇",
+        "key": "7",
+        "matches": [
+            Match(wm_class='soffice'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+    {
+        "name": "󰎕 ₈",
+        "key": "8",
+        "matches": [
+            Match(wm_class='newsboat'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+    {
+        "name": "󰇮 ₉",
+        "key": "9",
+        "matches": [
+            Match(wm_class='aerc'),
+        ],
+        "layout": "monadtall",
+        "spawn": [],
+    },
+]
-- 
cgit v1.2.3