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

Commit 908181fa2d2ff0760e821d7b4f07a1818c40d988
Parent: 040f497b4df8772ef83ed44d8b836e83fe650247
Author: mcol <mcol@posteo.net>
Date: 2022-08-24 00:14:42 +0100
Committer: mcol <mcol@posteo.net>
Committed: 2022-08-24 00:14:42 +0100

various updates from last few weeks/months

x11.py Modified

@@ -11,7 +11,8 @@
 mod = "mod1" if IS_XEPHYR else "mod4"
 
 
-term = "xterm" if IS_XEPHYR else "urxvt"  # urxvt doesn't like xephyr
+#term = "xterm" if IS_XEPHYR else "urxvt"  # urxvt doesn't like xephyr
+term = "xterm"
 wmname = "LG3D"
 keys_backend = []
 

wayland.py Modified

@@ -9,7 +9,8 @@
 from libqtile import hook, qtile
 from libqtile.backend import base
 from libqtile.backend.wayland import InputConfig
-from libqtile.backend.wayland.window import XdgWindow, XWindow
+from libqtile.backend.wayland.xdgwindow import XdgWindow
+from libqtile.backend.wayland.xwindow import XWindow
 from libqtile.lazy import lazy
 from libqtile.log_utils import logger
 
@@ -41,7 +42,10 @@
     "1267:12377:ELAN1300:00 04F3:3059 Touchpad": InputConfig(
         drag=True, tap=True, dwt=False, pointer_accel=0.3
     ),
-    "type:pointer": InputConfig(pointer_accel=-0.9),
+    "1e7d:2c38:ROCCAT ROCCAT Kiro Mouse": InputConfig(
+        left_handed=True, pointer_accel=-1.0,
+    ),
+    "type:pointer": InputConfig(pointer_accel=-1.0),
 }
 
 

startup.sh Modified

@@ -23,17 +23,26 @@
 run_if_new() { ps aux | grep -v grep | grep -q $1 || $@; }
 
 [[ -z "$QTILE_XEPHYR" ]] && {
-    kanshi &
-    wlsunset -t 2500 -T 5700 -l 55.7 -L -3.1 &
-    run_if_new firefox &
-    #run_if_new irc &
+    # Session setup
     systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
+    dbus-update-activation-environment --systemd \
+	WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=$XDG_CURRENT_DESKTOP
+
+    # Services
+    kanshi &
+    wlsunset -t 1000 -T 6700 -l 55.7 -L -3.1 &
     swayidle &
     mpDris2 &
     sway-mpris-idle-inhibit &
-    playerctld daemon
-    check_systemd
+    playerctld daemon &
+    kdeconnect-indicator &
+
+    # Startup programs
+    run_if_new firefox &
     run_if_new geary &
+
+    # Notify me if any systemd services failed
+    check_systemd &
 } &> /dev/null
 
 

scratchpad.py Modified

@@ -22,10 +22,7 @@
 if IS_WAYLAND:
     term = "foot "
 else:
-    if IS_XEPHYR:
-        term = "xterm -e "
-    else:
-        term = "urxvt -e "
+    term = "xterm -e "
 
 
 conf = {

mindfulness.py Added

@@ -0,0 +1,101 @@
+# Copyright (c) 2022 Matt Colligan
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import os
+from urllib import request
+from random import randint
+
+import gi
+gi.require_version('Gst', '1.0')
+from gi.repository import Gst
+
+from libqtile.log_utils import logger
+from libqtile.widget import base
+from libqtile.utils import get_cache_dir
+
+Gst.init(None)
+
+
+class ReMindfulness(base.ThreadPoolText):
+    """
+    Mindfulness reminder widget (Proof of concept / work in progress)
+
+    Inspired by Kyle MacLeod's mindfulnotifier Android app
+    (https://github.com/kmac/mindfulnotifier). The widget will periodically play a
+    calming tone at semi-random intervals.
+
+    Note, if the specified audio_file does not exist (i.e. the first time the widget is
+    configured, leaving the default value for ``audio_file``), one will be downloaded.
+    It is fetched via the mindfulnotifier app's github repository.
+    """
+
+    default_path = os.path.join(get_cache_dir(), "tibetan_bell_ding_b.mp3")
+    default_url = "https://github.com/kmac/mindfulnotifier/raw/master/media/tibetan_bell_ding_b.mp3"
+
+    defaults = [
+        ("audio_file", default_path, "Lower bound for the interval between reminders"),
+        ("interval_minimum", 60 * 60, "Lower bound for the interval between reminders"),
+        ("interval_maximum", 90 * 60, "Upper bound for the interval between reminders"),
+        ("reminder_background", "ff0000", "Background colour when reminding"),
+        ("reminder_foreground", "ff0000", "Foreground colour when reminding"),
+        ("reminder_duration", 5, "Duration of reminders (i.e. for appearance change)"),
+        ("reminder_text", None, "Text"),
+    ]
+
+    def __init__(self, text="", **config):
+        base.ThreadPoolText.__init__(self, text, **config)
+        self.add_defaults(ReMindfulness.defaults)
+        self._just_started = True
+        self._original_foreground = self.foreground
+        self._original_background = self.background
+
+        self.add_callbacks(
+            {
+                "Button1": self._reset_colours,
+            }
+        )
+
+    def _configure(self, qtile, bar):
+        self._just_started = not self.configured
+        base.ThreadPoolText._configure(self, qtile, bar)
+
+    def poll(self):
+        self.update_interval = randint(self.interval_minimum, self.interval_maximum)
+
+        if self._just_started:
+            return self.text
+
+        if not os.path.exists(self.audio_file):
+            logger.info("Downloading default tone for ReMindfulness widget")
+            request.urlretrieve(self.default_url, self.audio_file)
+
+        playbin = Gst.ElementFactory.make('playbin', 'playbin')
+        playbin.props.uri = "file://" + self.audio_file
+        playbin.set_state(Gst.State.PLAYING)
+
+        self.foreground = self.reminder_foreground
+        self.background = self.reminder_background
+        self.timeout_add(self.reminder_duration, self._reset_colours)
+
+        return self.text
+
+    def _reset_colours(self):
+        self.foreground = self._original_foreground
+        self.background = self._original_background

groups.py Modified

@@ -32,10 +32,10 @@
     Group("2", label="term"),
     Group("3", label="term"),
     Group("4", label="term"),
-    Group("q", label="surf", matches=[Match(wm_class="firefox")]),
-    Group("w", label="mail", matches=[Match(wm_class="geary"), Match(title="Geary")]),
+    Group("q", label="html", matches=[Match(wm_class="firefox")]),
+    Group("w", label="imap", matches=[Match(wm_class="geary"), Match(title="Geary")]),
     Group("e", label="work"),
-    Group("r", label="chat", matches=[Match(title="irc")]),
+    Group("r", label="spot", matches=[Match(title="spotify")]),
 ]
 
 

config.py Modified

@@ -10,6 +10,7 @@
 import os
 import subprocess
 import sys
+import re
 from typing import TYPE_CHECKING
 
 from libqtile import bar, hook, layout, qtile, widget
@@ -48,12 +49,15 @@
 reload("power_menu")
 from power_menu import keys_power_menu
 
+#reload("mindfulness")
+#from mindfulness import ReMindfulness
+
 IS_WAYLAND: bool = qtile.core.name == "wayland"
 IS_XEPHYR: bool = int(os.environ.get("QTILE_XEPHYR", 0)) > 0
 
 reload(qtile.core.name)
 if IS_WAYLAND:
-    from wayland import keys_backend, term, wl_input_rules  # noqa: F401
+    from wayland import keys_backend, term, wl_input_rules, wl_shadows  # noqa: F401
 else:
     from x11 import keys_backend, term, wmname  # noqa: F401
 
@@ -124,6 +128,7 @@
         ([mod], "f", lazy.window.toggle_fullscreen(), "Toggle fullscreen"),
         # ([mod], "g", lazy.function(toggle_fullscreen_state), "Toggle fullscreen state"),
         ([mod, "shift"], "space", lazy.window.toggle_floating(), "Toggle floating"),
+        ([mod, "shift"], "s", lazy.window.static(), "Make window static"),
         (
             [mod],
             "space",
@@ -195,15 +200,18 @@
             lazy.spawn("playerctl next"),
             "Next song",
         ),
+        ([], "XF86AudioNext", lazy.spawn("playerctl next"), "Next song"),
         (
             [mod],
             "bracketleft",
             lazy.spawn("playerctl previous"),
             "Previous song",
         ),
+        ([], "XF86AudioPrev", lazy.spawn("playerctl previous"), "Previous song"),
         ([], "Pause", lazy.spawn("playerctl play-pause"), "Play/unpause music"),
+        ([], "XF86AudioPlay", lazy.spawn("playerctl play-pause"), "Play/unpause music"),
         # Launchers
-        ([mod], "d", lazy.spawn("rofi -show run"), "Spawn with rofi"),
+        ([mod], "d", lazy.spawn("rofi -show run -theme ~/.config/rofi/common.rasi"), "Spawn with rofi"),
         ([mod], "Return", lazy.spawn(term), "Spawn terminal"),
         ([mod, "shift"], "f", lazy.spawn("firefox"), "Spawn Firefox"),
         (
@@ -216,6 +224,8 @@
         (["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"),
+        ([mod, "shift"], "i", lazy.spawn("gtklock & systemctl suspend -i", shell=True), "Suspend and lock"),
+        ([mod, "shift"], "p", lazy.widget["pomodoro"].toggle_active(), "Toggle Pomodoro"),
     ]
 )
 
@@ -253,7 +263,7 @@
         wrap_focus_columns=False,
         wrap_focus_rows=False,
         margin=0,
-        margin_on_single=4,
+        #margin_on_single=30,  # 4
     ),
     # layout.Stack(
     #    border_width=5,
@@ -269,9 +279,12 @@
     border_normal="00000000",
     fullscreen_border_width=0,
     float_rules=[
+        Match(title="Bluetooth Devices"),
         Match(title="Open File"),
         Match(title="Unlock Database - KeePassXC"),
         Match(title="File Operation Progress", wm_class="thunar"),
+        Match(title="File Operation Progress", wm_class="Thunar"),
+        Match(title=re.compile("Presenting: .*"), wm_class="libreoffice-impress"),
         Match(wm_class="thunar"),
         Match(title="Firefox — Sharing Indicator"),
         Match(wm_class="Arandr"),
@@ -299,6 +312,7 @@
         Match(wm_class="wlroots"),
         Match(wm_class="wdisplays"),
         Match(wm_class="Xephyr"),
+        Match(wm_class="zoom"),
         Match(wm_type="dialog"),
         Match(role="gimp-file-export"),
         Match(func=base.Window.has_fixed_size),
@@ -341,11 +355,11 @@
 
 mpd2 = widget.Mpris2(
     name="mpris",
+    width=1000,
     objname=None,
     display_metadata=["xesam:title", "xesam:artist"],
     foreground=colours[12],
     font="TamzenForPowerline Bold",
-    scroll_interval=0,
 )
 
 
@@ -406,6 +420,11 @@
     foreground=colours[3],
 )
 
+pomodoro = widget.Pomodoro(
+    prefix_inactive="",
+    foreground=colours[6],
+)
+
 volume = MyVolume(
     fontsize=18,
     channel="PCM",
@@ -428,6 +447,13 @@
     update_interval=5,
 )
 
+#mind = ReMindfulness(
+#    "",
+#    reminder_text="MIND!",
+#    reminder_background=colours[2],
+#    foreground=colours[2],
+#)
+
 
 class MyBattery(Battery):
     """
@@ -498,14 +524,14 @@
     fontsize=20,
     font="TamzenForPowerline Medium",
     update_interval=60,
+    foreground=colours[7],
     name="time",
 )
 
 groupboxes = [
     widget.GroupBox(**groupbox_config),
     widget.GroupBox(**groupbox_config, visible_groups=["q", "w", "e", "r"]),
-]
-
+    ]
 
 @hook.subscribe.startup
 def _():
@@ -538,7 +564,9 @@
                 widget.Spacer(name="s1"),
                 mpd2,
                 widget.Spacer(name="s2"),
+                pomodoro,
                 systray,
+                #mind,
                 volume,
                 bklight,
                 wlan,
@@ -552,7 +580,7 @@
         top=bar.Gap(outer_gaps),
         left=bar.Gap(outer_gaps),
         right=bar.Gap(outer_gaps),
-        wallpaper="~/pictures/Wallpapers/q_triangles2.png",
+        wallpaper="~/pictures/Wallpapers/akira.png",
         wallpaper_mode="fill",
     ),
     Screen(
@@ -562,6 +590,7 @@
                 widget.Spacer(name="s3"),
                 mpd2,
                 widget.Spacer(name="s4"),
+                pomodoro,
                 volume,
                 bklight,
                 wlan,
@@ -572,7 +601,7 @@
             28,
             background=background + "00",
         ),
-        wallpaper="~/pictures/Wallpapers/q_triangles2.png",
+        wallpaper="~/pictures/Wallpapers/akira.png",
         wallpaper_mode="fill",
         top=bar.Gap(outer_gaps),
         left=bar.Gap(outer_gaps),