aboutsummaryrefslogtreecommitdiff
path: root/.config/qtile
diff options
context:
space:
mode:
authorJustine Smithies <justine@smithies.me.uk>2023-08-22 19:46:15 +0100
committerJustine Smithies <justine@smithies.me.uk>2023-08-22 19:46:15 +0100
commit6aaea9cf4b283d41016e60735f52c8feb3cd0c9e (patch)
treeffc170bb7f267247b60908ac9125fcc02175afd9 /.config/qtile
parent86c0c3694c93025dfec23f27266905c12f446a4e (diff)
Initial commit
Diffstat (limited to '.config/qtile')
-rwxr-xr-x.config/qtile/autostart.sh41
-rw-r--r--.config/qtile/colors.py51
-rw-r--r--.config/qtile/config.py43
-rw-r--r--.config/qtile/groups.py58
-rw-r--r--.config/qtile/hooks.py82
-rw-r--r--.config/qtile/keys.py277
-rw-r--r--.config/qtile/layouts.py56
-rw-r--r--.config/qtile/mouse.py28
-rw-r--r--.config/qtile/ordinaldate.py20
-rw-r--r--.config/qtile/screens.py24
-rwxr-xr-x.config/qtile/statusbar/battery.py89
-rwxr-xr-x.config/qtile/statusbar/brightnesscontrol44
-rwxr-xr-x.config/qtile/statusbar/calendar.sh26
-rwxr-xr-x.config/qtile/statusbar/idleinhibit32
-rwxr-xr-x.config/qtile/statusbar/network.sh45
-rwxr-xr-x.config/qtile/statusbar/void-updates.sh13
-rwxr-xr-x.config/qtile/statusbar/volumecontrol107
-rw-r--r--.config/qtile/widgets.py106
-rw-r--r--.config/qtile/workspaces.py93
19 files changed, 1235 insertions, 0 deletions
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": [],
+ },
+]