From f4c6430eb41db08622c98076d01bfca929e38be3 Mon Sep 17 00:00:00 2001
From: Kevin Alberts <kevin@kevinalberts.nl>
Date: Wed, 14 Oct 2020 18:59:03 +0200
Subject: [PATCH 1/7] Changes on Celestia

---
 kuro/config.py        |  4 +++-
 kuro/theme.py         | 11 ++++++++++-
 required_packages.txt |  3 ++-
 3 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/kuro/config.py b/kuro/config.py
index 05f2f87..aba2092 100644
--- a/kuro/config.py
+++ b/kuro/config.py
@@ -16,7 +16,9 @@ class Config(BaseConfig):
 
     # Default Applications
     app_terminal = "terminator"
-    app_launcher = "/home/kevin/bin/dmenu_wal.sh"
+    #app_launcher = "/home/kevin/bin/dmenu_wal.sh"
+    app_launcher = "dmenu_run -i -p '»' -nb '" + background + "' -fn 'Noto Sans-11' -nf '" + inactive_light + \
+                   "' -sb '" + highlight + "' -sf '" + foreground + "'"
     web_browser = "firefox-developer-edition"
     file_manager = "thunar"
     app_chat = "/home/kevin/bin/ramboxpro"
diff --git a/kuro/theme.py b/kuro/theme.py
index ac322df..30472b3 100644
--- a/kuro/theme.py
+++ b/kuro/theme.py
@@ -149,6 +149,9 @@ class Kuro(BaseTheme):
             # Floating toggle
             Key([self.mod, "shift"], 'f', lazy.window.toggle_floating()),
 
+            # Pinned toggle
+            Key([self.mod], 'p', lazy.function(self.toggle_pinned)),
+
             # Toggle between split and unsplit sides of stack.
             # Split = all windows displayed
             # Unsplit = 1 window displayed, like Max layout, but still with
@@ -180,7 +183,7 @@ class Kuro(BaseTheme):
             # Screenshot key
             Key([], "Print", lazy.spawn(Config.get('cmd_screenshot', 'xfce4-screenshooter'))),
 
-            # Alt Screenshot
+            # Alt screenshot
             Key([self.mod], "Print", lazy.spawn(Config.get('cmd_alt_screenshot', 'xfce4-screenshooter'))),
 
             # Toggle between different layouts as defined below
@@ -564,6 +567,12 @@ class Kuro(BaseTheme):
 
         window.floating = True
 
+    # Pinned toggle function
+    @staticmethod
+    def toggle_pinned(qtile):
+        windows = qtile.cmd_windows()
+        print(windows)
+
     # QTile base callbacks
     def callback_startup_once(self, *args, **kwargs):
         self.update_wallpaper(self.qtile)
diff --git a/required_packages.txt b/required_packages.txt
index 8b3391e..ff3bd88 100644
--- a/required_packages.txt
+++ b/required_packages.txt
@@ -8,4 +8,5 @@ python-osc
 playerctl
 
 xfce4-screenshooter
-xfce4-clipman-plugin
\ No newline at end of file
+xfce4-clipman-plugin
+wireless_tools

From a033bde573fee44f09fb9de5045b378e55406cdc Mon Sep 17 00:00:00 2001
From: Kevin Alberts <kevin@kevinalberts.nl>
Date: Thu, 15 Apr 2021 17:11:48 +0200
Subject: [PATCH 2/7] Changes to Celestia

---
 kuro/base.py          | 24 +++++-------
 kuro/config.py        | 13 +++++--
 kuro/theme.py         | 73 ++++++++++++++++++++-----------------
 kuro/utils/general.py |  9 ++++-
 kuro/utils/layouts.py | 85 ++++++++++++++++++-------------------------
 5 files changed, 101 insertions(+), 103 deletions(-)

diff --git a/kuro/base.py b/kuro/base.py
index fd7bc84..370c02f 100644
--- a/kuro/base.py
+++ b/kuro/base.py
@@ -1,6 +1,6 @@
 from libqtile import layout as libqtile_layout, layout, bar, widget
 from libqtile.command import lazy
-from libqtile.config import Key, Group, Screen, Drag, Click
+from libqtile.config import Key, Group, Screen, Drag, Click, Match
 
 
 class BaseConfig:
@@ -29,20 +29,14 @@ class BaseTheme:
     bring_front_click = False
     cursor_warp = False
     floating_layout = libqtile_layout.Floating(float_rules=[
-        {'wmclass': 'confirm'},
-        {'wmclass': 'dialog'},
-        {'wmclass': 'download'},
-        {'wmclass': 'error'},
-        {'wmclass': 'file_progress'},
-        {'wmclass': 'notification'},
-        {'wmclass': 'splash'},
-        {'wmclass': 'toolbar'},
-        {'wmclass': 'confirmreset'},  # gitk
-        {'wmclass': 'makebranch'},  # gitk
-        {'wmclass': 'maketag'},  # gitk
-        {'wname': 'branchdialog'},  # gitk
-        {'wname': 'pinentry'},  # GPG key password entry
-        {'wmclass': 'ssh-askpass'},  # ssh-askpass
+        # Run the utility of `xprop` to see the wm class and name of an X client.
+        *layout.Floating.default_float_rules,
+        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
     ])
     auto_fullscreen = True
     focus_on_window_activation = "smart"
diff --git a/kuro/config.py b/kuro/config.py
index aba2092..b986e53 100644
--- a/kuro/config.py
+++ b/kuro/config.py
@@ -122,7 +122,8 @@ class Config(BaseConfig):
     volume_font = "Noto Sans"
     volume_fontsize = 11
     volume_theme_path = "/home/kevin/.config/qtile/kuro/resources/volume"
-    volume_pulse_sink = "alsa_output.pci-0000_00_1f.3.analog-stereo"
+    volume_pulse_sink = "alsa_output.pci-0000_04_00.0.pro-output-0"
+    volume_pulse_sink2 = "alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.pro-output-0"
 
     volume_is_bluetooth_icon = False
     volume_update_interval = 0.2
@@ -151,12 +152,18 @@ class Config(BaseConfig):
     do_keyboard_updates = False
 
     # Show audio visualizer
-    show_audio_visualizer = True
+    show_audio_visualizer = False
     kill_unnecessary_glava_processes = True
 
     # Show thermal widget
     show_temperature = True
 
+    # Show GPU widget
+    show_gpu_widget = False
+
+    # Show battery widget
+    show_battery_widget = False
+
     # Audio control applications
     # apps_audio = ["pavucontrol"]
-    apps_audio_afterstart = "/home/kevin/bin/start_jack_audio_chain.sh"
+    apps_audio_afterstart = []
diff --git a/kuro/theme.py b/kuro/theme.py
index 30472b3..27012d6 100644
--- a/kuro/theme.py
+++ b/kuro/theme.py
@@ -7,7 +7,7 @@ from libqtile.log_utils import logger
 
 logger.error("Importing qtile theme requirements...")
 
-from libqtile.config import Key, Screen, Group, Drag, Click
+from libqtile.config import Key, Screen, Group, Drag, Click, Match
 from libqtile.command import lazy
 from libqtile import layout, bar, widget
 
@@ -75,20 +75,14 @@ class Kuro(BaseTheme):
 
     # Floating layout override
     floating_layout = kuro_layouts.KuroFloating(float_rules=[
-        {'wmclass': 'confirm'},
-        {'wmclass': 'dialog'},
-        {'wmclass': 'download'},
-        {'wmclass': 'error'},
-        {'wmclass': 'file_progress'},
-        {'wmclass': 'notification'},
-        {'wmclass': 'splash'},
-        {'wmclass': 'toolbar'},
-        {'wmclass': 'confirmreset'},  # gitk
-        {'wmclass': 'makebranch'},  # gitk
-        {'wmclass': 'maketag'},  # gitk
-        {'wname': 'branchdialog'},  # gitk
-        {'wname': 'pinentry'},  # GPG key password entry
-        {'wmclass': 'ssh-askpass'},  # ssh-askpass
+        # Run the utility of `xprop` to see the wm class and name of an X client.
+        *layout.Floating.default_float_rules,
+        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
     ])
 
     def set_debug_text(self, text):
@@ -245,14 +239,7 @@ class Kuro(BaseTheme):
         groups.append(Group(""))
         groups.append(Group(""))
         groups.append(Group(""))
-        groups.append(Group("", spawn=Config.get('apps_audio', "true"), layout='verticaltile', layouts=[
-            layout.VerticalTile(
-                border_focus=Config.get('colour_border_focus', "#ffffff"),
-                border_normal=Config.get('colour_border_normal', "#777777"),
-                border_width=Config.get('width_border', "1"),
-                margin=Config.get('margin_layout', "0"),
-            )
-        ]))
+        groups.append(Group("", spawn=Config.get('apps_audio', "true")))
         groups.append(Group("", layout='floating', layouts=[
             layout.Floating(
                 border_focus="#990000",
@@ -360,19 +347,34 @@ class Kuro(BaseTheme):
                 kuro.utils.widgets.CPUInfoWidget(fontsize_left=16, fontsize_right=11),
                 kuro.utils.widgets.MemoryInfoWidget(fontsize_left=18, fontsize_right=11),
                 kuro.utils.widgets.DiskIOInfoWidget(fontsize_left=16, fontsize_right=11),
-                kuro.utils.widgets.BatteryInfoWidget(fontsize_left=16, fontsize_right=11),
+            ])
+            if Config.get('show_battery_widget', False):
+                widgets.extend([
+                    kuro.utils.widgets.BatteryInfoWidget(fontsize_left=16, fontsize_right=11),
+                ])
+            widgets.extend([
                 kuro.utils.widgets.VolumeInfoWidget(
                     pulse_sink=Config.get('volume_pulse_sink', None),
                     fontsize_left=18,
                     fontsize_right=11,
                 ),
+                kuro.utils.widgets.VolumeInfoWidget(
+                    pulse_sink=Config.get('volume_pulse_sink2', None),
+                    fontsize_left=18,
+                    fontsize_right=11,
+                ),
                 kuro.utils.widgets.TextSpacerWidget(fontsize=14),
                 kuro.utils.widgets.NetworkInfoWidget(fontsize_left=16, fontsize_right=14),
-                kuro.utils.widgets.GPUStatusWidget(
-                    theme_path=Config.get('gpu_theme_path', '/home/docs/checkouts/readthedocs.org/user_builds/qtile'
-                                                            '/checkouts/latest/libqtile/resources/battery-icons'),
-                    padding=0,
-                ),
+            ])
+            if Config.get('show_gpu_widget', False):
+                widgets.extend([
+                    kuro.utils.widgets.GPUStatusWidget(
+                        theme_path=Config.get('gpu_theme_path', '/home/docs/checkouts/readthedocs.org/user_builds/qtile'
+                                                                '/checkouts/latest/libqtile/resources/battery-icons'),
+                        padding=0,
+                    )
+                ])
+            widgets.extend([
                 kuro.utils.widgets.TextSpacerWidget(fontsize=14),
             ])
 
@@ -388,6 +390,7 @@ class Kuro(BaseTheme):
                     colour_have_updates=Config.get('updates_colour_available', '#ff0000'),
                     display_format=Config.get('updates_display_format', 'Updates: {updates}'),
                     execute=Config.get('updates_execute_command', None),
+                    update_interval=Config.get('updates_interval', 600),
                     **self.widget_defaults
                 ),
                 widget.TextBox("#{}".format(x), name="default", **self.widget_defaults),
@@ -481,7 +484,6 @@ class Kuro(BaseTheme):
     @staticmethod
     def update_screens(qtile):
         out = utils.call_process(["xrandr", "--current"])
-        #mode_out = utils.call_process(["optimus-manager", "--print-mode"])
         video_mode = "nvidia"
         #if "nvidia" in mode_out:
         #    video_mode = "nvidia"
@@ -600,11 +602,14 @@ class Kuro(BaseTheme):
                 p.wait()
 
         self.log_info("Starting compositor...")
-        utils.execute_once("compton -b")
+        utils.execute_once("picom -b")
 
         self.log_info("Starting clipboard manager...")
         utils.execute_once("xfce4-clipman")
 
+        self.log_info("Starting notification daemon...")
+        utils.execute_once("dunst")
+
         # Update color scheme
         self.initialize_colorscheme()
 
@@ -636,9 +641,9 @@ class Kuro(BaseTheme):
             for r in dg.rules:
                 pid = -1
                 # noinspection PyProtectedMember
-                for r2 in r.match._rules:
-                    if r2[0] == "net_wm_pid":
-                        pid = r2[1]
+                for m in r.matchlist:
+                    if m._rules.get('net_wm_pid', None) is not None:
+                        pid = m._rules.get('net_wm_pid')
                         break
                 if pid != -1:
                     self.initial_windows.append((pid, r.group))
diff --git a/kuro/utils/general.py b/kuro/utils/general.py
index da8d96b..bfa32fc 100644
--- a/kuro/utils/general.py
+++ b/kuro/utils/general.py
@@ -12,7 +12,10 @@ from libqtile.bar import Bar
 from notify2 import Notification, URGENCY_NORMAL
 from libqtile.log_utils import logger
 
-notify2.init("QTileWM")
+try:
+    notify2.init("QTileWM")
+except DBusException as e:
+    logger.error("Could not initialize notify2: {}".format(e))
 
 BUTTON_LEFT = 1
 BUTTON_MIDDLE = 2
@@ -100,6 +103,10 @@ def notify(title, content, urgency=URGENCY_NORMAL, timeout=5000, image=None):
 
     try:
         return notification.show()
+    except notify2.UninittedError:
+        logger.warning("Notify2 was uninitialized, initializing...")
+        notify2.init("qtile")
+        return notification.show()
     except DBusException as e:
         logger.warning("Showing notification failed: {}".format(e))
         logger.warning(traceback.format_exc())
diff --git a/kuro/utils/layouts.py b/kuro/utils/layouts.py
index c12007c..e74dee6 100644
--- a/kuro/utils/layouts.py
+++ b/kuro/utils/layouts.py
@@ -15,68 +15,53 @@ class KuroFloating(Floating):
         super(KuroFloating, self).__init__(*args, **kwargs)
         self.add_defaults(KuroFloating.defaults)
     
-    def configure(self, client, screen):
-        # 'sun-awt-X11-XWindowPeer' is a dropdown used in Java application,
-        # don't reposition it anywhere, let Java app to control it
-        cls = client.window.get_wm_class() or ''
-        is_java_dropdown = 'sun-awt-X11-XWindowPeer' in cls
-        if is_java_dropdown:
-            return
-
+    def configure(self, client, screen_rect):
         if hasattr(client, "is_static_window") and client.is_static_window:
-            bc = client.group.qtile.color_pixel(self.border_static)
+            bc = self.border_static
         elif client.has_focus:
-            bc = client.group.qtile.color_pixel(self.border_focus)
+            bc = self.border_focus
         else:
-            bc = client.group.qtile.color_pixel(self.border_normal)
+            bc = self.border_normal
+
         if client.maximized:
             bw = self.max_border_width
         elif client.fullscreen:
             bw = self.fullscreen_border_width
         else:
             bw = self.border_width
-        above = False
 
-        # We definitely have a screen here, so let's be sure we'll float on screen
-        try:
-            client.float_x
-            client.float_y
-        except AttributeError:
-            # this window hasn't been placed before, let's put it in a sensible spot
-            transient_for = client.window.get_wm_transient_for()
-            win = client.group.qtile.windows_map.get(transient_for)
-            if win is not None:
-                # if transient for a window, place in the center of the window
-                center_x = win.x + win.width / 2
-                center_y = win.y + win.height / 2
-            else:
-                center_x = screen.x + screen.width / 2
-                center_y = screen.y + screen.height / 2
-                above = True
+        # 'sun-awt-X11-XWindowPeer' is a dropdown used in Java application,
+        # don't reposition it anywhere, let Java app to control it
+        cls = client.window.get_wm_class() or ''
+        is_java_dropdown = 'sun-awt-X11-XWindowPeer' in cls
+        if is_java_dropdown:
+            client.paint_borders(bc, bw)
+            client.cmd_bring_to_front()
 
-            x = center_x - client.width / 2
-            y = center_y - client.height / 2
+        # alternatively, users may have asked us explicitly to leave the client alone
+        elif any(m.compare(client) for m in self.no_reposition_rules):
+            client.paint_borders(bc, bw)
+            client.cmd_bring_to_front()
 
-            # don't go off the right...
-            x = min(x, screen.x + screen.width)
-            # or left...
-            x = max(x, screen.x)
-            # or bottom...
-            y = min(y, screen.y + screen.height)
-            # or top
-            y = max(y, screen.y)
+        else:
+            above = False
 
-            if not (self.no_reposition_match and self.no_reposition_match.compare(client)):
-                client.x = int(round(x))
-                client.y = int(round(y))
+            # We definitely have a screen here, so let's be sure we'll float on screen
+            try:
+                client.float_x
+                client.float_y
+            except AttributeError:
+                # this window hasn't been placed before, let's put it in a sensible spot
+                above = self.compute_client_position(client, screen_rect)
 
-        client.place(
-            client.x,
-            client.y,
-            client.width,
-            client.height,
-            bw,
-            bc,
-            above,
-        )
+
+            client.place(
+                client.x,
+                client.y,
+                client.width,
+                client.height,
+                bw,
+                bc,
+                above,
+            )
         client.unhide()

From 6a265ea3b617aa9dc9be34aa302862b1130732fb Mon Sep 17 00:00:00 2001
From: Kevin Alberts <kevin@kevinalberts.nl>
Date: Mon, 22 Nov 2021 19:42:03 +0100
Subject: [PATCH 3/7] Compatibility with latest qtile, fixes to widgets, etc...

---
 config.py             |  3 ---
 kuro/config.py        | 16 +++++------
 kuro/theme.py         | 63 ++++++++++++++++++++++++++-----------------
 kuro/utils/general.py |  2 +-
 kuro/utils/widgets.py | 41 ++++++++++++++++++++--------
 kuro/utils/windows.py |  3 +--
 6 files changed, 78 insertions(+), 50 deletions(-)

diff --git a/config.py b/config.py
index c3abfcd..7d5f49f 100644
--- a/config.py
+++ b/config.py
@@ -121,8 +121,5 @@ def main(qtile):
     else:
         qtile.cmd_warning()
 
-    # Save qtile instance in theme
-    Theme.qtile = qtile
-
     # Save theme instance in qtile
     qtile.theme_instance = Theme
diff --git a/kuro/config.py b/kuro/config.py
index b986e53..480de2c 100644
--- a/kuro/config.py
+++ b/kuro/config.py
@@ -16,12 +16,10 @@ class Config(BaseConfig):
 
     # Default Applications
     app_terminal = "terminator"
-    #app_launcher = "/home/kevin/bin/dmenu_wal.sh"
-    app_launcher = "dmenu_run -i -p '»' -nb '" + background + "' -fn 'Noto Sans-11' -nf '" + inactive_light + \
-                   "' -sb '" + highlight + "' -sf '" + foreground + "'"
+    app_launcher = "/home/kevin/bin/dmenu_wal.sh"
     web_browser = "firefox-developer-edition"
     file_manager = "thunar"
-    app_chat = "/home/kevin/bin/ramboxpro"
+    app_chat = "/usr/bin/ramboxpro"
     app_irc = "quasselclient"
     app_mail = "thunderbird"
     cmd_brightness_up = "sudo /usr/bin/xbacklight -inc 10"
@@ -94,8 +92,8 @@ class Config(BaseConfig):
 
     # Thermal indicator variables
     thermal_threshold = 75
-    thermal_sensor = "Package id 0"
-    thermal_chip = "coretemp-isa-0000"
+    thermal_sensor = "Tdie"
+    thermal_chip = "zenpower-pci-00c3"
 
     # CPU graph variables
     cpu_graph_colour = '#ff0000'
@@ -122,8 +120,8 @@ class Config(BaseConfig):
     volume_font = "Noto Sans"
     volume_fontsize = 11
     volume_theme_path = "/home/kevin/.config/qtile/kuro/resources/volume"
-    volume_pulse_sink = "alsa_output.pci-0000_04_00.0.pro-output-0"
-    volume_pulse_sink2 = "alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.pro-output-0"
+    volume_pulse_sink = "alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo"
+    volume_pulse_sink2 = "alsa_output.pci-0000_0e_00.4.analog-stereo"
 
     volume_is_bluetooth_icon = False
     volume_update_interval = 0.2
@@ -152,7 +150,7 @@ class Config(BaseConfig):
     do_keyboard_updates = False
 
     # Show audio visualizer
-    show_audio_visualizer = False
+    show_audio_visualizer = True
     kill_unnecessary_glava_processes = True
 
     # Show thermal widget
diff --git a/kuro/theme.py b/kuro/theme.py
index 27012d6..aebeb85 100644
--- a/kuro/theme.py
+++ b/kuro/theme.py
@@ -9,7 +9,7 @@ logger.error("Importing qtile theme requirements...")
 
 from libqtile.config import Key, Screen, Group, Drag, Click, Match
 from libqtile.command import lazy
-from libqtile import layout, bar, widget
+from libqtile import layout, bar, widget, qtile
 
 logger.error("Importing theme util functions...")
 
@@ -83,13 +83,14 @@ class Kuro(BaseTheme):
         Match(wm_class='ssh-askpass'),  # ssh-askpass
         Match(title='branchdialog'),  # gitk
         Match(title='pinentry'),  # GPG key password entry
+        Match(title='origin.exe', wm_class='Wine'),  # Wine Origin game launcher
     ])
 
     def set_debug_text(self, text):
         for field in self.debug_textfields:
             field.text = text
         for bar in self.debug_bars:
-            if self.qtile is not None:
+            if qtile is not None:
                 bar.draw()
 
     def log_debug(self, text):
@@ -144,7 +145,7 @@ class Kuro(BaseTheme):
             Key([self.mod, "shift"], 'f', lazy.window.toggle_floating()),
 
             # Pinned toggle
-            Key([self.mod], 'p', lazy.function(self.toggle_pinned)),
+            Key([self.mod, "shift"], 'p', lazy.function(self.toggle_pinned)),
 
             # Toggle between split and unsplit sides of stack.
             # Split = all windows displayed
@@ -513,22 +514,22 @@ class Kuro(BaseTheme):
         else:
             utils.execute("arandr")
 
-    def reinitialize_visualizers(self, qtile=None):
+    def reinitialize_visualizers(self):
         if Config.get("show_audio_visualizer", False):
             logger.warning("Reinitializing visualizers...")
-            for screen in self.qtile.screens:
+            for screen in qtile.screens:
                 for widget in screen.top.widgets:
                     if isinstance(widget, kuro.utils.widgets.AudioVisualizerWidget):
                         if widget.client is not None:
                             widget.client.kill()
                             widget.client = None
                             widget.screen = None
-            self.update_visualizers(qtile=qtile)
+            self.update_visualizers()
 
-    def update_visualizers(self, qtile=None):
+    def update_visualizers(self):
         if Config.get("show_audio_visualizer", False):
             logger.warning("Updating visualizers..")
-            for screen in self.qtile.screens:
+            for screen in qtile.screens:
                 for widget in screen.top.widgets:
                     if isinstance(widget, kuro.utils.widgets.AudioVisualizerWidget):
                         if widget.client is None:
@@ -577,13 +578,20 @@ class Kuro(BaseTheme):
 
     # QTile base callbacks
     def callback_startup_once(self, *args, **kwargs):
-        self.update_wallpaper(self.qtile)
+        if not hasattr(qtile, 'theme_instance'):
+            # Save theme instance in qtile
+            qtile.theme_instance = self
+        self.update_wallpaper(qtile)
 
         # Setup audio
         # p = utils.execute_once(["qjackctl"])
         # p.wait()
 
     def callback_startup(self):
+        if not hasattr(qtile, 'theme_instance'):
+            # Save theme instance in qtile
+            qtile.theme_instance = self
+
         if self.current_wallpaper:
             p = utils.execute_once(["wal", "-n", "-i", "{}".format(self.current_wallpaper)])
             p.wait()
@@ -596,7 +604,7 @@ class Kuro(BaseTheme):
                     except KeyError:
                         wallpaper = None
             if wallpaper:
-                Kuro.set_wallpaper(self.qtile, wallpaper)
+                Kuro.set_wallpaper(qtile, wallpaper)
             else:
                 p = utils.execute_once("nitrogen --restore")
                 p.wait()
@@ -610,6 +618,9 @@ class Kuro(BaseTheme):
         self.log_info("Starting notification daemon...")
         utils.execute_once("dunst")
 
+        self.log_info("Starting xiccd color profile manager...")
+        utils.execute_once("xiccd")
+
         # Update color scheme
         self.initialize_colorscheme()
 
@@ -635,9 +646,13 @@ class Kuro(BaseTheme):
     initial_windows = []
 
     def callback_startup_complete(self, *args, **kwargs):
+        if not hasattr(qtile, 'theme_instance'):
+            # Save theme instance in qtile
+            qtile.theme_instance = self
+            
         # Only run on first startup
-        if not self.qtile.no_spawn:
-            dg = self.qtile.dgroups
+        if not qtile.no_spawn:
+            dg = qtile.dgroups
             for r in dg.rules:
                 pid = -1
                 # noinspection PyProtectedMember
@@ -651,12 +666,12 @@ class Kuro(BaseTheme):
             self.callback_client_new()
 
         # After first startup is complete, start the audio apps that can only be started after boot is complete
-        if not self.qtile.no_spawn:
+        if not qtile.no_spawn:
             for app in Config.get("apps_audio_afterstart", []):
                 utils.execute_once(app)
 
         # Update color scheme
-        Kuro.update_colorscheme(self.qtile)
+        Kuro.update_colorscheme(qtile)
 
     def callback_client_new(self, *args, **kwargs):
         client = args[0] if len(args) > 0 else None
@@ -664,27 +679,27 @@ class Kuro(BaseTheme):
         if len(self.initial_windows) > 0:
             init = self.initial_windows.copy()
             for pid, gname in init:
-                for group in self.qtile.groups:
+                for group in qtile.groups:
                     if len(group.windows) > 0:
                         for window in group.windows:
                             w_pid = window.window.get_net_wm_pid()
                             self.log_info("Comparing pid {} with window PID {}, window {}".format(pid, w_pid,
                                                                                                   window.name))
                             if pid == w_pid:
-                                c = self.qtile.dgroups.rules_map.copy()
+                                c = qtile.dgroups.rules_map.copy()
                                 for rid, r in c.items():
                                     if r.matches(window):
-                                        self.qtile.dgroups.remove_rule(rid)
+                                        qtile.dgroups.remove_rule(rid)
                                         self.initial_windows.remove((pid, gname))
                                         self.log_info("Removed group rule for PID {}, window {}".format(pid,
                                                                                                         window.name))
-            self.log_info(str(self.qtile.dgroups.rules_map))
+            self.log_info(str(qtile.dgroups.rules_map))
 
         # Check if it is a visualizer
         if Config.get("show_audio_visualizer", False):
             if client is not None and client.window.get_name() == "GLava":
                 placed = False
-                for screen in self.qtile.screens:
+                for screen in qtile.screens:
                     for widget in screen.top.widgets:
                         if not placed and isinstance(widget, kuro.utils.widgets.AudioVisualizerWidget):
                             if widget.client is None:
@@ -693,10 +708,10 @@ class Kuro(BaseTheme):
                                 pos_y = 0 + widget.margin_y
                                 width = viz_info['width'] - (2 * widget.margin_x)
                                 height = viz_info['height'] - (2 * widget.margin_y)
-                                screen_index = self.qtile.screens.index(screen)
+                                screen_index = qtile.screens.index(screen)
                                 logger.warning("Attaching {} {} to {} on screen {}".format(client, client.window.wid, type(widget).__name__, screen_index))
                                 c = KuroStatic.create(client, screen, x=pos_x, y=pos_y, width=width, height=height)
-                                c.set_opacity(Config.get("bar_opacity", 1.0))
+                                c.opacity = Config.get("bar_opacity", 1.0)
                                 widget.set_client(c, screen)
                                 placed = True
                 if not placed:
@@ -722,11 +737,11 @@ class Kuro(BaseTheme):
 
         # Detach visualizer from widget if it was a visualizer window
         if isinstance(client, KuroStatic):
-            for screen in self.qtile.screens:
+            for screen in qtile.screens:
                 for widget in screen.top.widgets:
                     if isinstance(widget, kuro.utils.widgets.AudioVisualizerWidget):
                         if widget.client == client:
-                            screen_index = self.qtile.screens.index(screen)
+                            screen_index = qtile.screens.index(screen)
                             logger.warning("Detaching {} {} from widget {} on screen {}".format(client, client.window.wid, type(widget).__name__, screen_index))
                             widget.client = None
                             widget.screen = None
@@ -876,7 +891,7 @@ class Kuro(BaseTheme):
 
             # Update colors in visualizers and restart visualizers
             with open(Config.get("glava_color_file_path", "~/.config/glava/kurobars_color.glsl"), 'w') as f:
-                f.write("#define COLOR {}\n#request setbg {}".format(colors['color15'], colors['color1'][1:]))
+                f.write("#define COLOR {}\n#request setbg {}00".format(colors['color15'], colors['color1'][1:]))
             qtile.theme_instance.reinitialize_visualizers()
 
             utils.notify(
diff --git a/kuro/utils/general.py b/kuro/utils/general.py
index bfa32fc..92551f7 100644
--- a/kuro/utils/general.py
+++ b/kuro/utils/general.py
@@ -7,7 +7,7 @@ import notify2
 import six
 from dbus import DBusException
 from libqtile import widget
-from libqtile.window import Internal
+from libqtile.backend.x11.window import Internal
 from libqtile.bar import Bar
 from notify2 import Notification, URGENCY_NORMAL
 from libqtile.log_utils import logger
diff --git a/kuro/utils/widgets.py b/kuro/utils/widgets.py
index a122a50..7620d1a 100644
--- a/kuro/utils/widgets.py
+++ b/kuro/utils/widgets.py
@@ -18,7 +18,7 @@ from libqtile.widget.currentlayout import CurrentLayoutIcon
 from libqtile.widget.graph import _Graph
 from libqtile.widget.tasklist import TaskList
 from libqtile.widget.wlan import get_status
-from libqtile.window import Window
+from libqtile.backend.x11.window import Window
 
 from kuro.utils.general import notify, BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT, BUTTON_DOWN, BUTTON_UP, BUTTON_MUTE, \
     call_process
@@ -260,6 +260,7 @@ class MediaWidget(base.InLoopPollText):
         ('on_text_pause', ' {}', 'The pattern for the text if music is paused.'),
         ('on_text_stop', ' {}', 'The pattern for the text if music is stopped.'),
         ('update_interval', 1, 'The update interval.'),
+        ('max_chars_per_player', 50, 'Maximum characters of text per player.'),
     ]
 
     player_icons = {
@@ -333,7 +334,7 @@ class MediaWidget(base.InLoopPollText):
 
     def cmd_update_custom_player(self, player_name, data):
         # Update firefox player
-        if player_name == "firefox":
+        if player_name.startswith("firefox"):
             if data['playing'] and data['muted']:
                 self.custom_player_data['firefox']['showing'] = True
                 self.custom_player_data['firefox']['state'] = MediaWidget.Status.PAUSED
@@ -355,8 +356,11 @@ class MediaWidget(base.InLoopPollText):
         players = []
 
         # Playerctl players
-        command = ["playerctl", "-l"]
-        result = self.call_process(command)
+        try:
+            result = self.call_process(["playerctl", "-l"])
+        except subprocess.CalledProcessError:
+            result = None
+
         if result:
             players.extend([x for x in result.split("\n") if x])
 
@@ -398,8 +402,14 @@ class MediaWidget(base.InLoopPollText):
 
                     text = "Unknown"
                     if cmd_result in ["Playing", "Paused"]:
-                        artist = self.call_process(['playerctl', '-p', player, 'metadata', 'artist']).strip()
-                        title = self.call_process(['playerctl', '-p', player, 'metadata', 'title']).strip()
+                        try:
+                            artist = self.call_process(['playerctl', '-p', player, 'metadata', 'artist']).strip()
+                        except subprocess.CalledProcessError:
+                            artist = None
+                        try:
+                            title = self.call_process(['playerctl', '-p', player, 'metadata', 'title']).strip()
+                        except subprocess.CalledProcessError:
+                            title = None
 
                         if artist and title:
                             text = "{} - {}".format(artist, title)
@@ -419,13 +429,17 @@ class MediaWidget(base.InLoopPollText):
 
     def _get_formatted_text(self, status):
         if status[0] == MediaWidget.Status.PLAYING:
-            return self.on_text_play.format(status[1])
+            res = self.on_text_play.format(status[1])
         elif status[0] == MediaWidget.Status.PAUSED:
-            return self.on_text_pause.format(status[1])
+            res = self.on_text_pause.format(status[1])
         elif status[0] == MediaWidget.Status.STOPPED:
-            return self.on_text_stop.format(status[1])
+            res = self.on_text_stop.format(status[1])
         else:
-            return "Unknown"
+            res = "Unknown"
+        res = pangocffi.markup_escape_text(res)
+        if len(res) > self.max_chars_per_player:
+            res = res[:self.max_chars_per_player] + "..."
+        return res
 
     def draw(self):
         super(MediaWidget, self).draw()
@@ -437,7 +451,12 @@ class MediaWidget(base.InLoopPollText):
             return self.off_text
         else:
             for player in status.keys():
-                icon = self.player_icons.get(player, player)
+                # Shorten firefox.instance[0-9]+ to just firefox for icon finding
+                if player.startswith("firefox"):
+                    player_icon = "firefox"
+                else:
+                    player_icon = player
+                icon = self.player_icons.get(player_icon, player_icon)
                 text.append("{} {}".format(icon, self._get_formatted_text(status[player])))
 
         return " | ".join(text) if text else self.off_text
diff --git a/kuro/utils/windows.py b/kuro/utils/windows.py
index edaf7ac..6bfd997 100644
--- a/kuro/utils/windows.py
+++ b/kuro/utils/windows.py
@@ -1,7 +1,6 @@
 from cairocffi.test_xcb import xcffib
 from libqtile import hook
-from libqtile.window import Window, Static
-
+from libqtile.backend.x11.window import Window, Static
 
 class KuroStatic(Static):
 

From cdf5996c4031fef4897366838d9ee7886f902d91 Mon Sep 17 00:00:00 2001
From: Kevin Alberts <kevin@kevinalberts.nl>
Date: Sat, 28 May 2022 13:57:05 +0200
Subject: [PATCH 4/7] Updates over time

---
 kuro/config.py        | 2 +-
 kuro/theme.py         | 6 ++++++
 kuro/utils/general.py | 2 +-
 kuro/utils/widgets.py | 2 ++
 4 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/kuro/config.py b/kuro/config.py
index 480de2c..3e05df9 100644
--- a/kuro/config.py
+++ b/kuro/config.py
@@ -19,7 +19,7 @@ class Config(BaseConfig):
     app_launcher = "/home/kevin/bin/dmenu_wal.sh"
     web_browser = "firefox-developer-edition"
     file_manager = "thunar"
-    app_chat = "/usr/bin/ramboxpro"
+    app_chat = "/usr/bin/rambox"
     app_irc = "quasselclient"
     app_mail = "thunderbird"
     cmd_brightness_up = "sudo /usr/bin/xbacklight -inc 10"
diff --git a/kuro/theme.py b/kuro/theme.py
index aebeb85..40915d4 100644
--- a/kuro/theme.py
+++ b/kuro/theme.py
@@ -621,6 +621,12 @@ class Kuro(BaseTheme):
         self.log_info("Starting xiccd color profile manager...")
         utils.execute_once("xiccd")
 
+        self.log_info("Starting KDE connect daemon...")
+        utils.execute_once("/usr/lib/kdeconnectd")
+
+        self.log_info("Starting KDE connect indicator...")
+        utils.execute_once("/usr/bin/kdeconnect-indicator")
+
         # Update color scheme
         self.initialize_colorscheme()
 
diff --git a/kuro/utils/general.py b/kuro/utils/general.py
index 92551f7..76a1f0a 100644
--- a/kuro/utils/general.py
+++ b/kuro/utils/general.py
@@ -196,7 +196,7 @@ class KuroTopBar(Bar):
         self.theme = theme
         super(KuroTopBar, self).__init__(widgets, size, **config)
 
-    def _configure(self, qtile, screen):
+    def _configure(self, qtile, screen, *args, **kwargs):
         super(KuroTopBar, self)._configure(qtile, screen)
         self.window.handle_EnterNotify = self.handle_enter_notify
         self.window.handle_LeaveNotify = self.handle_leave_notify
diff --git a/kuro/utils/widgets.py b/kuro/utils/widgets.py
index 7620d1a..b665507 100644
--- a/kuro/utils/widgets.py
+++ b/kuro/utils/widgets.py
@@ -8,6 +8,7 @@ import iwlib
 import netifaces
 import psutil
 import six
+import unicodedata
 from libqtile import bar, pangocffi
 from libqtile.log_utils import logger
 from libqtile.widget import base
@@ -437,6 +438,7 @@ class MediaWidget(base.InLoopPollText):
         else:
             res = "Unknown"
         res = pangocffi.markup_escape_text(res)
+        res = unicodedata.normalize('NFKD', res)
         if len(res) > self.max_chars_per_player:
             res = res[:self.max_chars_per_player] + "..."
         return res

From 9976e7f14423f35aaf63c7969b48937eda3e1825 Mon Sep 17 00:00:00 2001
From: Kevin Alberts <kevin@kevinalberts.nl>
Date: Mon, 15 May 2023 11:41:58 +0200
Subject: [PATCH 5/7] various small changes

---
 kuro/config.py        |  8 ++++++--
 kuro/theme.py         |  4 ++--
 kuro/utils/widgets.py | 13 +++++++------
 3 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/kuro/config.py b/kuro/config.py
index 3e05df9..e82690b 100644
--- a/kuro/config.py
+++ b/kuro/config.py
@@ -17,11 +17,12 @@ class Config(BaseConfig):
     # Default Applications
     app_terminal = "terminator"
     app_launcher = "/home/kevin/bin/dmenu_wal.sh"
-    web_browser = "firefox-developer-edition"
+    web_browser = "firefox"
     file_manager = "thunar"
     app_chat = "/usr/bin/rambox"
     app_irc = "quasselclient"
     app_mail = "thunderbird"
+    app_music = "spotify"
     cmd_brightness_up = "sudo /usr/bin/xbacklight -inc 10"
     cmd_brightness_down = "sudo /usr/bin/xbacklight -dec 10"
     lock_command = "bash /home/kevin/bin/lock.sh"
@@ -150,7 +151,7 @@ class Config(BaseConfig):
     do_keyboard_updates = False
 
     # Show audio visualizer
-    show_audio_visualizer = True
+    show_audio_visualizer = False
     kill_unnecessary_glava_processes = True
 
     # Show thermal widget
@@ -165,3 +166,6 @@ class Config(BaseConfig):
     # Audio control applications
     # apps_audio = ["pavucontrol"]
     apps_audio_afterstart = []
+
+    # Comma-separated list of ignored players in the media widget
+    media_ignore_players = "kdeconnect"
diff --git a/kuro/theme.py b/kuro/theme.py
index 40915d4..f3d44d6 100644
--- a/kuro/theme.py
+++ b/kuro/theme.py
@@ -238,7 +238,7 @@ class Kuro(BaseTheme):
         groups.append(Group("", spawn=Config.get('file_manager', "true")))
         groups.append(Group("", spawn=Config.get('app_mail', "true")))
         groups.append(Group(""))
-        groups.append(Group(""))
+        groups.append(Group("", spawn=Config.get('app_music', "true")))
         groups.append(Group(""))
         groups.append(Group("", spawn=Config.get('apps_audio', "true")))
         groups.append(Group("", layout='floating', layouts=[
@@ -325,7 +325,7 @@ class Kuro(BaseTheme):
                 widgets.append(kuro.utils.widgets.AudioVisualizerWidget(margin_x=0, margin_y=0))
 
             widgets.extend([
-                kuro.utils.widgets.MediaWidget(),
+                kuro.utils.widgets.MediaWidget(ignore_players=Config.get('media_ignore_players', '')),
                 kuro.utils.widgets.TextSpacerWidget(fontsize=14),
             ])
 
diff --git a/kuro/utils/widgets.py b/kuro/utils/widgets.py
index b665507..43b31ed 100644
--- a/kuro/utils/widgets.py
+++ b/kuro/utils/widgets.py
@@ -262,6 +262,7 @@ class MediaWidget(base.InLoopPollText):
         ('on_text_stop', ' {}', 'The pattern for the text if music is stopped.'),
         ('update_interval', 1, 'The update interval.'),
         ('max_chars_per_player', 50, 'Maximum characters of text per player.'),
+        ('ignore_players', '', 'Comma-separated list of players to ignore.')
     ]
 
     player_icons = {
@@ -319,13 +320,13 @@ class MediaWidget(base.InLoopPollText):
         if button == BUTTON_LEFT:
             player = self._player_to_control()
             if player is not None:
-                command = ["playerctl", "-p", player, "play-pause"]
+                command = ["playerctl", "-i", self.ignore_players, "-p", player, "play-pause"]
                 _ = self.call_process(command)
                 notify("MediaWidget", "Toggled {}".format(player))
         if button == BUTTON_RIGHT:
             player = self._player_to_control()
             if player is not None:
-                command = ["playerctl", "-p", player, "next"]
+                command = ["playerctl", "-i", self.ignore_players, "-p", player, "next"]
                 _ = self.call_process(command)
         if button == BUTTON_MIDDLE:
             # Jump to the screen that the player is on
@@ -358,7 +359,7 @@ class MediaWidget(base.InLoopPollText):
 
         # Playerctl players
         try:
-            result = self.call_process(["playerctl", "-l"])
+            result = self.call_process(["playerctl", "-i", self.ignore_players, "-l"])
         except subprocess.CalledProcessError:
             result = None
 
@@ -398,17 +399,17 @@ class MediaWidget(base.InLoopPollText):
 
                 else:
                     # PlayerCtl player
-                    command = ["playerctl", "-p", player, "status"]
+                    command = ["playerctl", "-i", self.ignore_players, "-p", player, "status"]
                     cmd_result = self.call_process(command).strip()
 
                     text = "Unknown"
                     if cmd_result in ["Playing", "Paused"]:
                         try:
-                            artist = self.call_process(['playerctl', '-p', player, 'metadata', 'artist']).strip()
+                            artist = self.call_process(['playerctl', "-i", self.ignore_players, '-p', player, 'metadata', 'artist']).strip()
                         except subprocess.CalledProcessError:
                             artist = None
                         try:
-                            title = self.call_process(['playerctl', '-p', player, 'metadata', 'title']).strip()
+                            title = self.call_process(['playerctl', "-i", self.ignore_players, '-p', player, 'metadata', 'title']).strip()
                         except subprocess.CalledProcessError:
                             title = None
 

From b279b258462f92ce7dcd45c9e281597f78a81f1f Mon Sep 17 00:00:00 2001
From: Kevin Alberts <kevin@kevinalberts.nl>
Date: Sat, 28 Oct 2023 10:16:10 +0200
Subject: [PATCH 6/7] Updates on Violet

---
 kuro/config.py        | 18 ++++++++++++++++--
 kuro/theme.py         | 19 +++++++++++++++++++
 kuro/utils/general.py | 18 ++++++++++--------
 kuro/utils/widgets.py | 22 +++++++++++++++++++++-
 4 files changed, 66 insertions(+), 11 deletions(-)

diff --git a/kuro/config.py b/kuro/config.py
index e82690b..6761564 100644
--- a/kuro/config.py
+++ b/kuro/config.py
@@ -30,6 +30,20 @@ class Config(BaseConfig):
     cmd_screenshot = "xfce4-screenshooter -r -c -d 1"
     cmd_alt_screenshot = "xfce4-screenshooter -w -c -d 0"
 
+    # Keyboard commands
+    cmd_media_play = "playerctl -i kdeconnect play-pause"
+    cmd_media_next = "playerctl -i kdeconnect next"
+    cmd_media_mute = "pamixer -t"
+    cmd_media_volume_down = "pamixer -i 2"
+    cmd_media_volume_up = "pamixer -d 2"
+
+    # Display mode commands
+    cmd_monitor_mode_3s144 = "bash /home/kevin/.screenlayout/3scrns_144_rrot.sh"
+    cmd_monitor_mode_3s60 = "bash /home/kevin/.screenlayout/3scrns_60_rrot.sh"
+    cmd_monitor_mode_day = "bash /home/kevin/bin/monitor_day.sh"
+    cmd_monitor_mode_night = "bash /home/kevin/bin/monitor_night.sh"
+    cmd_monitor_mode_alt = "bash /home/kevin/bin/monitor_gamenight.sh"
+
     # Commands
     wallpaper_config_command = "/home/kevin/bin/wal-nitrogen-noupdate"
 
@@ -121,8 +135,8 @@ class Config(BaseConfig):
     volume_font = "Noto Sans"
     volume_fontsize = 11
     volume_theme_path = "/home/kevin/.config/qtile/kuro/resources/volume"
-    volume_pulse_sink = "alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo"
-    volume_pulse_sink2 = "alsa_output.pci-0000_0e_00.4.analog-stereo"
+    volume_pulse_sink = "alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo-output"
+    volume_pulse_sink2 = "alsa_output.pci-0000_0d_00.4.analog-stereo"
 
     volume_is_bluetooth_icon = False
     volume_update_interval = 0.2
diff --git a/kuro/theme.py b/kuro/theme.py
index f3d44d6..f6f0946 100644
--- a/kuro/theme.py
+++ b/kuro/theme.py
@@ -175,6 +175,23 @@ class Kuro(BaseTheme):
             Key([], "XF86MonBrightnessUp", lazy.spawn(Config.get('cmd_brightness_up', 'xbacklight -inc 10'))),
             Key([], "XF86MonBrightnessDown", lazy.spawn(Config.get('cmd_brightness_down', 'xbacklight -dec 10'))),
 
+            # Display modes
+            Key([self.mod], "Prior", lazy.spawn(Config.get('cmd_monitor_mode_3s144', 'true'))),
+            Key([self.mod], "Next", lazy.spawn(Config.get('cmd_monitor_mode_3s60', 'true'))),
+            Key([self.mod], "Home", lazy.spawn(Config.get('cmd_monitor_mode_day', 'true'))),
+            Key([self.mod], "End", lazy.spawn(Config.get('cmd_monitor_mode_night', 'true'))),
+            Key([self.mod], "Insert", lazy.spawn(Config.get('cmd_monitor_mode_alt', 'true'))),
+
+            # Media keys
+            Key([], "XF86AudioPlay", lazy.spawn(Config.get('cmd_media_play', 'true'))),
+            Key([], "XF86AudioNext", lazy.spawn(Config.get('cmd_media_next', 'true'))),
+            Key([], "XF86AudioMute", lazy.spawn(Config.get('cmd_media_mute', 'true'))),
+            Key([], "XF86AudioRaiseVolume", lazy.spawn(Config.get('cmd_media_volume_up', 'true'))),
+            Key([], "XF86AudioLowerVolume", lazy.spawn(Config.get('cmd_media_volume_down', 'true'))),
+
+            # Sleep key
+            Key([], "XF86Sleep", lazy.spawn(Config.get('cmd_sleep', 'true'))),
+
             # Screenshot key
             Key([], "Print", lazy.spawn(Config.get('cmd_screenshot', 'xfce4-screenshooter'))),
 
@@ -358,11 +375,13 @@ class Kuro(BaseTheme):
                     pulse_sink=Config.get('volume_pulse_sink', None),
                     fontsize_left=18,
                     fontsize_right=11,
+                    font_left=Config.get('font_groupbox', None),
                 ),
                 kuro.utils.widgets.VolumeInfoWidget(
                     pulse_sink=Config.get('volume_pulse_sink2', None),
                     fontsize_left=18,
                     fontsize_right=11,
+                    font_left=Config.get('font_groupbox', None),
                 ),
                 kuro.utils.widgets.TextSpacerWidget(fontsize=14),
                 kuro.utils.widgets.NetworkInfoWidget(fontsize_left=16, fontsize_right=14),
diff --git a/kuro/utils/general.py b/kuro/utils/general.py
index 76a1f0a..f63e1ad 100644
--- a/kuro/utils/general.py
+++ b/kuro/utils/general.py
@@ -204,22 +204,24 @@ class KuroTopBar(Bar):
         self.window.update_name()
 
     def draw(self):
-        if self.queued_draws == 0:
-            self.qtile.call_soon(self._actual_draw)
-        self.queued_draws += 1
+        if not self.widgets:
+            return
+        if not self._draw_queued:
+            self.future = self.qtile.call_soon(self._actual_draw)
+            self._draw_queued = True
 
     def _actual_draw(self):
-        self.queued_draws = 0
-        self._resize(self.length, self.widgets)
+        self._draw_queued = False
+        self._resize(self._length, self.widgets)
         for i in self.widgets:
             i.draw()
         if self.widgets:
             end = i.offset + i.length
-            if end < self.length:
+            if end < self._length:
                 if self.horizontal:
-                    self.drawer.draw(offsetx=end, width=self.length - end)
+                    self.drawer.draw(offsetx=end, width=self._length - end)
                 else:
-                    self.drawer.draw(offsety=end, height=self.length - end)
+                    self.drawer.draw(offsety=end, height=self._length - end)
 
                 self.theme.update_visualizers()
 
diff --git a/kuro/utils/widgets.py b/kuro/utils/widgets.py
index 43b31ed..d03c0da 100644
--- a/kuro/utils/widgets.py
+++ b/kuro/utils/widgets.py
@@ -139,6 +139,26 @@ class DualPaneTextboxBase(base._Widget):
         if self.layout_right:
             self.layout_right.font = value
 
+    @property
+    def font_left(self):
+        return self._font_left
+
+    @font_left.setter
+    def font_left(self, value):
+        self._font_left = value
+        if self.layout_left:
+            self.layout_left.font = value
+
+    @property
+    def font_right(self):
+        return self._font_right
+
+    @font_right.setter
+    def font_right(self, value):
+        self._font_right = value
+        if self.layout_right:
+            self.layout_right.font = value
+
     @property
     def fontshadow(self):
         return self._fontshadow
@@ -1197,7 +1217,7 @@ class VolumeInfoWidget(DualPaneTextboxBase):
     """Displays information about the volume"""
     orientations = base.ORIENTATION_HORIZONTAL
     defaults = [
-        ('update_interval', 10, 'The update interval in seconds.'),
+        ('update_interval', 5, 'The update interval in seconds.'),
         ('text_pattern', "{percentage}%", 'The pattern for the text that is displayed.'),
         ('charging_color', "#ffffff", "Color when battery is charging"),
         ('normal_color', "#ffffff", "Color when value is normal"),

From 1e650250450bfae59b049b655a24ea5a27fe9bd3 Mon Sep 17 00:00:00 2001
From: Kevin Alberts <kevin@kevinalberts.nl>
Date: Mon, 5 Feb 2024 20:19:39 +0100
Subject: [PATCH 7/7] Filter vertical walls, add vorta

---
 kuro/theme.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/kuro/theme.py b/kuro/theme.py
index f6f0946..f85eb9e 100644
--- a/kuro/theme.py
+++ b/kuro/theme.py
@@ -646,6 +646,9 @@ class Kuro(BaseTheme):
         self.log_info("Starting KDE connect indicator...")
         utils.execute_once("/usr/bin/kdeconnect-indicator")
 
+        self.log_info("Starting automatic backup scheduler...")
+        utils.execute_once("/usr/bin/vorta")
+
         # Update color scheme
         self.initialize_colorscheme()
 
@@ -782,7 +785,7 @@ class Kuro(BaseTheme):
         wallpapers = []
         wallpaper_dir = Config.get("desktop_bg_folder", "")
         try:
-            wallpapers = os.listdir(wallpaper_dir)
+            wallpapers = [x for x in os.listdir(wallpaper_dir) if ".vertical." not in x]
         except os.error as e:
             logger.warning("Could not load wallpapers from directory: {}".format(e))