diff --git a/config.py b/config.py
index c530902..7d5f49f 100644
--- a/config.py
+++ b/config.py
@@ -27,36 +27,38 @@
 # Import Theme
 from libqtile import hook
 from libqtile.log_utils import logger
-from kuro.utils import load_config_class
-import traceback
 
 try:
     from kuro.theme import Kuro
     Theme = Kuro()
 except ImportError as e:
-    logger.error(traceback.format_exc())
     logger.error("Could not load Kuro Theme. Trying to load BaseTheme. Error: {}".format(e))
     try:
         from kuro.base import BaseTheme as Kuro
         Theme = Kuro()
     except ImportError as e:
         Kuro = None
-        logger.error(traceback.format_exc())
         raise ImportError("Could not load theme Config or BaseTheme! Error: {}".format(e))
 
 # Import theme configuration
-Config = load_config_class()
-if Config is None:
-    raise ImportError("Could not load theme Config or BaseConfig! Error: {}".format(e))
+try:
+    from kuro.config import Config
+except ImportError as e:
+    logger.error("Could not load Kuro Config. Trying to load BaseConfig. Error: {}".format(e))
+    try:
+        from kuro.base import BaseConfig as Config
+    except ImportError as e:
+        Config = None
+        raise ImportError("Could not load theme Config or BaseConfig! Error: {}".format(e))
+
 
 try:
-    logger.warning("Initializing theme...")
-    logger.warning(f"Using config variables for '{Config.get('config_name', '????')}'")
+    logger.info("Initializing theme...")
     # Initialize the Theme
     Theme.initialize()
-    logger.warning("Initialize done")
+    logger.info("Initialize done")
 
-    logger.warning("Hooking theme into callbacks...")
+    logger.info("Hooking theme into callbacks...")
     # Hook theme into all hooks we know of
     hook.subscribe.startup_once(Theme.callback_startup_once)
     hook.subscribe.startup(Theme.callback_startup)
@@ -80,22 +82,17 @@ try:
     hook.subscribe.selection_notify(Theme.callback_selection_notify)
     hook.subscribe.selection_change(Theme.callback_selection_change)
     hook.subscribe.screen_change(Theme.callback_screen_change)
-    hook.subscribe.screens_reconfigured(Theme.callback_screens_reconfigured)
     hook.subscribe.current_screen_change(Theme.callback_current_screen_change)
-    logger.warning("Hooking done")
+    logger.info("Hooking done")
 
-    logger.warning("Initializing theme variables")
+    logger.info("Initializing theme variables")
     # Initialize variables from theme
     keys = Theme.keys
     mouse = Theme.mouse
     groups = Theme.groups
     layouts = Theme.layouts
     widget_defaults = Theme.widget_defaults
-    if Config.get("use_fake_screens", False):
-        logger.warning(f"This host uses fake screens!")
-        fake_screens = Theme.screens
-    else:
-        screens = Theme.screens
+    screens = Theme.screens
     dgroups_key_binder = Theme.dgroups_key_binder
     dgroups_app_rules = Theme.dgroups_app_rules
     main = Theme.main
@@ -107,18 +104,14 @@ try:
     focus_on_window_activation = Theme.focus_on_window_activation
     extensions = Theme.extensions
     wmname = Theme.wmname
-    reconfigure_screens = Theme.reconfigure_screens
-    logger.warning("Variable initialization done")
+    logger.info("Variable initialization done")
 except Exception as e:
     Theme = None
     Config = None
-    logger.error(traceback.format_exc())
     raise AttributeError("Could not configure theme! Error: {}".format(e))
 
 
 def main(qtile):
-    Config.initialize(qtile)
-
     # set logging level
     if Config.get('debug', False):
         if Config.get('verbose', False):
diff --git a/kuro/base.py b/kuro/base.py
index 8f02d65..221d1fd 100644
--- a/kuro/base.py
+++ b/kuro/base.py
@@ -1,38 +1,24 @@
-import time
-
 from libqtile import layout as libqtile_layout, layout, bar, widget
 from libqtile.lazy import lazy
 from libqtile.config import Key, Group, Screen, Drag, Click, Match
 
-# Initialize logging
-from libqtile.log_utils import logger
-
 
 class BaseConfig:
-    config_name = "KuroBase"
-
     @classmethod
     def get(cls, key, default):
         if hasattr(cls, key):
-            return getattr(cls, key)
-            #return cls.__dict__[key]
+            return cls.__dict__[key]
         else:
             return default
 
-    @classmethod
-    def initialize(cls, qtile):
-        # Can do extra initialization based on qtile instance here
-        pass
-
 
 class BaseTheme:
     # Changing variables initialized by function
     keys = None
-    mouse = None
     groups = None
     layouts = None
     widget_defaults = None
-    screens = []
+    screens = None
     qtile = None
 
     # 'Static' variables
@@ -55,7 +41,6 @@ class BaseTheme:
     auto_fullscreen = True
     focus_on_window_activation = "smart"
     extensions = []
-    reconfigure_screens = False
 
     # 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
@@ -70,21 +55,12 @@ class BaseTheme:
     # 'export _JAVA_AWT_WM_NONREPARENTING=1'
     wmname = "LG3D"
 
-    def __init__(self):
-        self.startup_time = time.time()
-
     def initialize(self):
-        logger.info("Initializing widget defaults...")
         self.widget_defaults = self.init_widget_defaults()
-        logger.info("Initializing keys...")
         self.keys = self.init_keys()
-        logger.info("Initializing groups...")
         self.groups = self.init_groups()
-        logger.info("Initializing layouts...")
         self.layouts = self.init_layouts()
-        logger.info("Initializing screens...")
         self.screens = self.init_screens()
-        logger.info("Initializing mouse...")
         self.mouse = self.init_mouse()
 
     def init_keys(self):
@@ -119,15 +95,6 @@ class BaseTheme:
             Key(["mod4"], "r", lazy.spawncmd()),
         ]
 
-    def init_mouse(self):
-        return [
-            Drag(["mod4"], "Button1", lazy.window.set_position_floating(),
-                 start=lazy.window.get_position()),
-            Drag(["mod4"], "Button3", lazy.window.set_size_floating(),
-                 start=lazy.window.get_size()),
-            Click(["mod4"], "Button2", lazy.window.bring_to_front())
-        ]
-
     def init_groups(self):
         groups = [Group(i) for i in "asdfuiop"]
         for i in groups:
@@ -171,6 +138,15 @@ class BaseTheme:
             ),
         ]
 
+    def init_mouse(self):
+        return [
+            Drag(["mod4"], "Button1", lazy.window.set_position_floating(),
+                 start=lazy.window.get_position()),
+            Drag(["mod4"], "Button3", lazy.window.set_size_floating(),
+                 start=lazy.window.get_size()),
+            Click(["mod4"], "Button2", lazy.window.bring_to_front())
+        ]
+
     # Callbacks
     def callback_startup_once(self, *args, **kwargs):
         pass
@@ -244,8 +220,5 @@ class BaseTheme:
     def callback_screen_change(self, *args, **kwargs):
         pass
 
-    def callback_screens_reconfigured(self, *args, **kwargs):
-        pass
-
     def callback_current_screen_change(self, *args, **kwargs):
         pass
diff --git a/kuro/config/__init__.py b/kuro/config.py
similarity index 51%
rename from kuro/config/__init__.py
rename to kuro/config.py
index 85c1bff..5bc2ce7 100644
--- a/kuro/config/__init__.py
+++ b/kuro/config.py
@@ -1,23 +1,12 @@
 from kuro.base import BaseConfig
-from libqtile.log_utils import logger
-from libqtile.lazy import lazy
 
 
 # Config variables used in the main configuration
 class Config(BaseConfig):
-    config_name = "KuroGeneral"
-
     # Show debug bar and messages
     debug = False
     verbose = False
 
-    # General variables
-    homedir = "/home/kevin"
-    use_fake_screens = False
-
-    # Screen configs (empty = autoconfig, otherwise list of Screen() kwarg dicts)
-    screen_kwargs = []
-
     # Colors
     foreground = "#ffffff"
     background = "#000000"
@@ -25,88 +14,22 @@ class Config(BaseConfig):
     inactive_light = "#777777"
     inactive_dark = "#333333"
 
-    # Predefined commands
-    cmd_screenshot = "/home/kevin/bin/screenshot.sh"
-    cmd_alt_screenshot = "/home/kevin/bin/screenshot.sh"
-    lock_command = "bash /home/kevin/bin/lock.sh"
-    cliphistory_command = "/home/kevin/bin/cliphistory.sh"
-
     # Default Applications
-    app_terminal = "terminator"
-    app_launcher = "wofi --show run,drun"
-    app_launcer_x11 = "/home/kevin/bin/dmenu_wal.sh"
-    file_manager = "thunar"
-    visualizer_app = "glava"
+    app_terminal = "ghostty"
+    app_terminal_init = "ghostty --gtk-single-instance=true --quit-after-last-window-close=false --initial-window=true"
+    app_launcher = "/home/kevin/bin/dmenu_wal.sh"
     web_browser = "firefox"
-    network_config = None
-
-    # Group definitions
-    groups = [
-            {'name': ''},
-            {'name': ''},
-            {'name': ''},
-            {'name': ''},
-            {'name': ''},
-            {'name': ''},
-            {'name': ''},
-            {'name': ''},
-            {'name': ''},
-            {'name': '', 'layout': 'floating', 'options': {'border_focus': '#990000', 'border_normal': '#440000'}}
-    ]
-
-    # Extra keybind definitions
-    extra_keys = [
-        # Display modes
-        {'modifiers': ['mod'], 'key': "Prior", 'action': lazy.spawn("bash /home/kevin/.screenlayout/3scrns_144_rrot.sh")},
-        {'modifiers': ['mod'], 'key': "Next", 'action': lazy.spawn("bash /home/kevin/.screenlayout/3scrns_60_rrot.sh")},
-        {'modifiers': ['mod'], 'key': "Home", 'action': lazy.spawn("bash /home/kevin/bin/monitor_day.sh")},
-        {'modifiers': ['mod'], 'key': "End", 'action': lazy.spawn("bash /home/kevin/bin/monitor_night.sh")},
-        {'modifiers': ['mod'], 'key': "Insert", 'action': lazy.spawn("bash /home/kevin/bin/monitor_gamenight.sh")},
-
-        # Backlight keys
-        {'modifiers': [], 'key': "XF86MonBrightnessUp", 'action': lazy.spawn("sudo /usr/bin/xbacklight -inc 10")},
-        {'modifiers': [], 'key': "XF86MonBrightnessDown", 'action': lazy.spawn("sudo /usr/bin/xbacklight -dec 10")},
-    ]
-
-    # Extra floating window rules
-    extra_float_rules = [
-        # Wine Origin game launcher
-        {'title': 'origin.exe', 'wm_class': 'Wine'},
-        # Homebank popups
-        {'title': 'Add transaction', 'wm_class': 'homebank'},
-        {'title': 'Edit transaction', 'wm_class': 'homebank'},
-        {'title': 'Inherit transaction', 'wm_class': 'homebank'},
-        {'title': 'Multiple edit transactions', 'wm_class': 'homebank'},
-        {'title': 'Transaction splits', 'wm_class': 'homebank'},
-    ]
-
-    # Autostart applications
-    apps_autostart_group = [
-        {'group': "", 'command': ["firefox"]},
-        {'group': "", 'command': ["terminator"]},
-        {'group': "", 'command': ["/usr/bin/rambox"]},
-        {'group': "", 'command': ["thunar"]},
-        {'group': "", 'command': ["thunderbird"]},
-        {'group': "", 'command': ["spotify"]},
-    ]
-    apps_autostart = {
-        'common': [
-            ["/usr/lib/kdeconnectd"],  # KDE Connect daemon
-            ["kdeconnect-indicator"],  # KDE Connect tray
-            ["vorta"],                 # Vorta backup scheduler
-        ],
-        'x11': [
-            ["dunst"],          # Notification daemon
-            ["picom", "-b"],    # Compositor
-            ["xfce4-clipman"],  # Clipboard manager
-            ["xiccd"],          # Color profile manager
-        ],
-        'wayland': [
-            ["mako"],    # Notification daemon
-            ["wl-paste", "--watch", "cliphist", "store"],  # Clipboard manager
-            ["kanshi"],  # Display hotplugging
-        ]
-    }
+    file_manager = "thunar"
+    app_chat = "/usr/bin/rambox"
+    app_irc = ""
+    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"
+    visualizer_app = "glava"
+    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"
@@ -116,11 +39,14 @@ class Config(BaseConfig):
     cmd_media_volume_up = "pamixer -d 2"
 
     # Display mode commands
-    cmd_reconfigure_screens = "kanshictl reload"
-    cmd_wal = ["wallust", "run"]
+    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
-    x11_wallpaper_config_command = "/home/kevin/bin/wal-nitrogen-noupdate"  # TODO: Remove
+    wallpaper_config_command = "/home/kevin/bin/wal-nitrogen-noupdate"
 
     # Images
     desktop_bg = "/home/kevin/Pictures/wallpapers/desktop.png"
@@ -159,8 +85,7 @@ class Config(BaseConfig):
 
     # Bar variables
     bar_background = background
-    bar_rgba_opacity = "AA"
-    bar_opacity = 1.0
+    bar_opacity = 0.8
     bar_hover_opacity = 1
 
     # Groupbox variables
@@ -181,12 +106,11 @@ class Config(BaseConfig):
     tasklist_urgent_border = highlight
     tasklist_font = "Noto Sans"
     tasklist_fontsize = 11
-    tasklist_rounded = False
 
     # 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'
@@ -201,11 +125,10 @@ class Config(BaseConfig):
     battery_theme_path = "/home/kevin/.config/qtile/kuro/resources/battery"
     battery_update_delay = 5
 
-    # Network variables
+    # Wifi variables
     wifi_interface = "wifi0"
     wifi_theme_path = "/home/kevin/.config/qtile/kuro/resources/wifi"
     wifi_update_interval = 5
-    wired_interface = "eth0"
 
     # GPU variables
     gpu_theme_path = "/home/kevin/.config/qtile/kuro/resources/gpu"
@@ -214,7 +137,9 @@ class Config(BaseConfig):
     volume_font = "Noto Sans"
     volume_fontsize = 11
     volume_theme_path = "/home/kevin/.config/qtile/kuro/resources/volume"
-    volume_pulse_sinks = []
+    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
 
@@ -238,6 +163,9 @@ class Config(BaseConfig):
     laptop_screen_nvidia = "eDP-1-1"
     laptop_screen_intel = "eDP1"
 
+    # Keyboard colors
+    do_keyboard_updates = False
+
     # Show audio visualizer
     show_audio_visualizer = False
     kill_unnecessary_glava_processes = True
@@ -251,18 +179,9 @@ class Config(BaseConfig):
     # Show battery widget
     show_battery_widget = False
 
-    # Show media widget
-    show_media_widget = True
+    # Audio control applications
+    # apps_audio = ["pavucontrol"]
+    apps_audio_afterstart = []
 
     # Comma-separated list of ignored players in the media widget
     media_ignore_players = "kdeconnect"
-
-    @classmethod
-    def initialize(cls, qtile):
-        # Can do extra initialization based on qtile instance here
-        super(Config, cls).initialize(qtile=qtile)
-
-        # Replace some apps if launched in X11 mode
-        if qtile.core.name == "x11":
-            logger.warning("Launched in X11 mode, overriding some apps in Config to xorg-variants.")
-            cls.app_launcher = cls.app_launcer_x11
diff --git a/kuro/config/aria.py b/kuro/config/aria.py
deleted file mode 100644
index ebb87bf..0000000
--- a/kuro/config/aria.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from kuro.config import Config as GeneralConfig
-
-
-class Config(GeneralConfig):
-    """
-    Kuro QTile configuration overrides for Aria
-    """
-    config_name = "Aria"
-
-    # Default Applications
-    app_terminal = "terminator"
-    app_launcher = "/home/kevin/bin/dmenu_wal.sh"
-    cmd_brightness_up = "true"
-    cmd_brightness_down = "true"
-    cmd_screenshot = "xfce4-screenshooter -r -c -d 1"
-    cmd_alt_screenshot = "xfce4-screenshooter -w -c -d 0"
-    lock_command = "bash /home/kevin/bin/lock.sh"
-    cliphistory_command = "true"
-
-    # Autostart applications
-    apps_autostart_group = [
-        {'group': "", 'command': ["firefox"]},
-        {'group': "", 'command': ["terminator"]},
-        {'group': "", 'command': ["/usr/bin/rambox"]},
-        {'group': "", 'command': ["thunar"]},
-        {'group': "", 'command': ["thunderbird"]},
-        {'group': "", 'command': ["spotify"]},
-    ]
-
-    # Thermal indicator variables
-    thermal_sensor = "Package id 0"
-    thermal_chip = "coretemp-isa-0000"
-
-    # Network variables
-    wifi_interface = "wifi0"
-    wired_interface = "enp7s0"
-
-    # Volume widget variables
-    volume_pulse_sinks = [
-        "alsa_output.pci-0000_00_1f.3.analog-stereo",
-    ]
-
-    # Screen organization
-    laptop_screen_nvidia = "eDP-1-1"
-    laptop_screen_intel = "eDP1"
diff --git a/kuro/config/meconopsis.py b/kuro/config/meconopsis.py
deleted file mode 100644
index e580c92..0000000
--- a/kuro/config/meconopsis.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from kuro.config import Config as GeneralConfig
-
-
-class Config(GeneralConfig):
-    """
-    Kuro QTile configuration overrides for Meconopsis
-    """
-    config_name = "Meconopsis"
-
-    # Thermal indicator variables
-    thermal_sensor = "Package id 0"
-    thermal_chip = "coretemp-isa-0000"
-
-    # Network variables
-    wifi_interface = "wlp3s0"
-    wired_interface = "enp4s0"
-
-    # Volume widget variables
-    volume_pulse_sinks = [
-        # Analog jack
-        "alsa_output.usb-CSCTEK_USB_Audio_and_HID_A34004801402-00.analog-stereo",
-    ]
diff --git a/kuro/config/ppc1006083.py b/kuro/config/ppc1006083.py
deleted file mode 100644
index f3f6939..0000000
--- a/kuro/config/ppc1006083.py
+++ /dev/null
@@ -1,134 +0,0 @@
-from kuro.config import Config as GeneralConfig
-import os
-from libqtile.log_utils import logger
-
-
-class Config(GeneralConfig):
-    """
-    Kuro QTile configuration overrides for Work DBO laptop
-    """
-    config_name = "DBO Power"
-    homedir = "/home/albek00"
-    #modifier = "mod1"  # Non-existing F13 key used by AutoHotKey script
-    use_fake_screens = True
-    fake_screen_count = 2
-
-    # Default Applications
-    app_terminal = "terminator"
-    app_launcher = "/home/albek00/bin/dmenu_wal.sh"
-    app_launcer_x11 = "/home/albek00/bin/dmenu_wal.sh"
-    cmd_brightness_up = "true"
-    cmd_brightness_down = "true"
-    cmd_screenshot = "xfce4-screenshooter -r -c -d 1"
-    cmd_alt_screenshot = "xfce4-screenshooter -w -c -d 0"
-    lock_command = "true"
-    cliphistory_command = "true"
-    cmd_wal = ["wal", "-n", "-s", "-t", "-i"]
-
-    # Screen configs (empty = autoconfig, otherwise list of Screen() kwarg dicts)
-    screen_kwargs = [
-        {'x': 0, 'y': 0, 'width': int(os.getenv("QTILE_WIDTH", "3840"))//2, 'height': int(os.getenv("QTILE_HEIGHT", "1080"))},
-        {'x': int(os.getenv("QTILE_WIDTH", "3840"))//2, 'y': 0, 'width': int(os.getenv("QTILE_WIDTH", "3840"))//2, 'height': int(os.getenv("QTILE_HEIGHT", "1080"))},
-    ]
-    margin_layout = [8, 8, 2, 8]
-
-    # Group definitions
-    groups = [
-        {'name': ''},
-        {'name': ''},
-        {'name': ''},
-        {'name': ''},
-        {'name': ''},
-        {'name': ''},
-        {'name': ''},
-        {'name': ''},
-        {'name': ''},
-    ]
-
-    # Extra keybind definitions
-    extra_keys = []
-
-    # Extra floating window rules
-    extra_float_rules = []
-
-    # Autostart applications
-    apps_autostart_group = [
-        {'group': "", 'command': ["firefox"]},
-        {'group': "", 'command': ["terminator"]},
-        {'group': "", 'command': ["subl"]},
-        {'group': "", 'command': ["smerge"]},
-        {'group': "", 'command': ["thunar"]},
-    ]
-
-    # Hide unnecessary widgets
-    show_temperature = False
-    show_media_widget = False
-
-    # Network variables
-    wifi_interface = None
-    wired_interface = "enP20755p0s0"
-
-    # Volume widget variables
-    volume_pulse_sinks = [
-        "RDPSink",
-    ]
-
-    # Override directories to proper homedir. Half of these are probably not even used but meh
-    x11_wallpaper_config_command = "/home/albek00/bin/wal-nitrogen-noupdate"
-    desktop_bg = "/home/albek00/Pictures/wallpapers/desktop.png"
-    desktop_bg_folder = "/home/albek00/Pictures/wallpapers/desktop_rotation/day"
-    desktop_bg_night_folder = "/home/albek00/Pictures/wallpapers/desktop_rotation/night"
-    # desktop_bg_override = "/home/albek00/Pictures/safe_wallpaper.jpg"
-    applauncher_image = "/home/albek00/.config/qtile/kuro/resources/arch.png"
-    custom_layout_icon_paths = ['/home/albek00/.config/qtile/kuro/resources/layout_icons/']
-    glava_color_file_path = "/home/albek00/.config/glava/kurobars_color.glsl"
-    battery_theme_path = "/home/albek00/.config/qtile/kuro/resources/battery"
-    wifi_theme_path = "/home/albek00/.config/qtile/kuro/resources/wifi"
-    gpu_theme_path = "/home/albek00/.config/qtile/kuro/resources/gpu"
-    volume_theme_path = "/home/albek00/.config/qtile/kuro/resources/volume"
-    bluevol_theme_path = "/home/albek00/.config/qtile/kuro/resources/bluetooth_volume"
-
-    @classmethod
-    def initialize(cls, qtile):
-        super(Config, cls).initialize(qtile=qtile)
-        # Add keyboard remapping to autostart apps
-        cls.apps_autostart['common'].append(["xmodmap", "-e", "keycode 191 = Super_L"])
-
-        # Determine screens programatically
-        qtile_width = int(os.getenv("QTILE_WIDTH", "3840"))
-        qtile_height = int(os.getenv("QTILE_HEIGHT", "1080"))
-        logger.warning(f"Determining screens for size {qtile_width}x{qtile_height}...")
-
-        # Home office, 1920x1080 horizontal right and 1080x1920 vertical left
-        if qtile_width == 3000 and (qtile_height > 1912 and qtile_height <= 1920):
-            cls.screen_kwargs = [
-                {'x': 0, 'y': 839, 'width': 1920, 'height': qtile_height-839},
-                {'x': 1920, 'y': 0, 'width': 1080, 'height': qtile_height},
-            ]
-        # Dual 1680x1050
-        elif qtile_width == 3360 and (qtile_height > 1040 and qtile_height <= 1050):
-            cls.screen_kwargs = [
-                {'x': 0, 'y': 0, 'width': 1680, 'height': 1050},
-                {'x': 1680, 'y': 0, 'width': 1680, 'height': 1050},
-            ]
-        # Dual 1920x1080
-        elif qtile_width == 3840 and (qtile_height > 1070 and qtile_height <= 1080):
-            cls.screen_kwargs = [
-                {'x': 0, 'y': 0, 'width': 1920, 'height': 1080},
-                {'x': 1920, 'y': 0, 'width': 1920, 'height': 1080},
-            ]
-        # Single 1920x1080
-        elif qtile_width == 1920 and (qtile_height > 1070 and qtile_height <= 1080):
-            cls.screen_kwargs = [{'x': 0, 'y': 0, 'width': 1920, 'height': 1080}]
-            cls.fake_screen_count = 1
-        # Single 1680x1050
-        elif qtile_width == 1680 and (qtile_height > 1040 and qtile_height <= 1050):
-            cls.screen_kwargs = [{'x': 0, 'y': 0, 'width': 1680, 'height': 1050}]
-            cls.fake_screen_count = 1
-        # Else, configure for 1 large screen
-        else:
-            cls.screen_kwargs = [{'x': 0, 'y': 0, 'width': qtile_width, 'height': qtile_height}]
-            cls.fake_screen_count = 1
-
-        logger.warning(f"Kwargs: {cls.screen_kwargs}")
-
diff --git a/kuro/config/temari.py b/kuro/config/temari.py
deleted file mode 100644
index 59f4dff..0000000
--- a/kuro/config/temari.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from kuro.config import Config as GeneralConfig
-
-
-class Config(GeneralConfig):
-    """
-    Kuro QTile configuration overrides for Meconopsis
-    """
-    config_name = "Temari"
-
-    # Default Applications
-    #app_terminal = "terminator"
-    cmd_brightness_up = "brightnessctl -d intel_backlight set +5%"
-    cmd_brightness_down = "brightnessctl -d intel_backlight set 5%-"
-    network_config = "nm-connection-editor"
-    #lock_command = "bash /home/kevin/bin/lock.sh"
-
-    # Thermal indicator variables
-    thermal_sensor = "Package id 0"
-    thermal_chip = "coretemp-isa-0000"
-
-    # Network variables
-    wifi_interface = "wlp0s20f3"
-    wired_interface = "enp7s0"
-
-    # Volume widget variables
-    volume_pulse_sinks = [
-        # Analog jack
-        "alsa_output.pci-0000_00_1f.3.analog-stereo",
-    ]
-
-    # Screen organization
-    laptop_screen_nvidia = "eDP-1-1"
-    laptop_screen_intel = "eDP1"
-
-    # Show battery widget
-    show_battery_widget = True
diff --git a/kuro/config/violet.py b/kuro/config/violet.py
deleted file mode 100644
index e6dd5a6..0000000
--- a/kuro/config/violet.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from kuro.config import Config as GeneralConfig
-
-
-class Config(GeneralConfig):
-    """
-    Kuro QTile configuration overrides for Violet
-    """
-    config_name = "Violet"
-
-    # Thermal indicator variables
-    thermal_sensor = "Tdie"
-    thermal_chip = "zenpower-pci-00c3"
-
-    # Network variables
-    wifi_interface = None
-    wired_interface = "br1"
-
-    # Volume widget variables
-    volume_pulse_sinks = [
-        # Behringer USB mixer
-        "alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo-output",
-        # Motherboard output (Starship/Matisse)
-        "alsa_output.pci-0000_0e_00.4.iec958-stereo",
-        # PCIe card output (CMI8738/CMI8768 PCI Audio)
-        "alsa_output.pci-0000_08_00.0.analog-stereo",
-    ]
diff --git a/kuro/theme.py b/kuro/theme.py
index 3f554a6..b83e0d6 100644
--- a/kuro/theme.py
+++ b/kuro/theme.py
@@ -1,106 +1,131 @@
 import json
 import os
 import random
-import time
 import datetime
-import socket
-import subprocess
-from typing import Optional
 
-from libqtile.backend.base import Window
-from libqtile.backend.wayland.layer import LayerStatic
-from libqtile.backend.wayland.xwindow import XWindow as WaylandXWindow, XStatic as WaylandXStatic
-from libqtile.backend.x11.window import XWindow as XorgXWindow
 # Initialize logging
 from libqtile.log_utils import logger
 
-logger.warning("Importing qtile theme requirements...")
+logger.error("Importing qtile theme requirements...")
 
 from libqtile.config import Key, Screen, Group, Drag, Click, Match
+from libqtile.command.base import expose_command
 from libqtile.lazy import lazy
 from libqtile import layout, bar, widget, qtile
-from qtile_extras import widget as extra_widget
 
-logger.warning("Importing theme util functions...")
+logger.error("Importing theme util functions...")
 
 # Import theme util functions
 from xcffib.xproto import WindowError
 
-logger.warning("Importing kuro utils...")
+logger.error("Importing kuro utils...")
 
 import kuro.utils.widgets
 from kuro.utils import general as utils
 
-logger.warning("Importing variables and other utils...")
+logger.error("Importing variables and other utils...")
 
 # Import variables
 from kuro.base import BaseTheme
 from kuro.utils.general import display_wm_class, test_popups
+from kuro.utils.kb_backlight import handle_focus_change as kb_handle_focus_change
 from kuro.utils import layouts as kuro_layouts
+from kuro.utils.windows import KuroStatic
 
-logger.warning("Importing configuration...")
+logger.error("Importing configuration...")
 
-from kuro.utils import load_config_class
-Config = load_config_class()
-if Config is None:
-    raise ImportError("Could not load theme Config or BaseConfig! Error: {}".format(e))
-Config.initialize(qtile)
-
-logger.warning("Imports done")
+try:
+    from kuro.config import Config
+except ImportError:
+    try:
+        from kuro.baseconfig import BaseConfig as Config
+    except ImportError:
+        Config = None
+        raise ImportError("Could not load theme Config or BaseConfig!")
 
+logger.error("Imports done")
 
 class Kuro(BaseTheme):
     # Shorthand for modifier key
     mod = Config.get("modifier", "mod4")
 
+    # Show debug messages
+    debug = Config.get('debug', False)
+    debug_textfields = []
+    debug_bars = []
+
     # Screen count
     num_screens = 0
 
+    # Top bars
+    topbars = []
+
+    # Visualizers
+    audio_visualizers = []
+
     # Static windows
     static_windows = []
 
     # Current wallpaper path
     current_wallpaper = None
 
+    # Whether or not to perform keyboard backlight updates
+    do_keyboard_updates = True
+
     # Window manager name
     wmname = "qtile"
 
-    startup_completed = False
-    autostart_app_rules = {}
-
     # Floating layout override
-    floating_layout = kuro_layouts.KuroFloating(
-        border_width=0,
-        border_focus="#000000",
-        border_normal="#000000",
-        float_rules=[
-            # 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
-            # Extra rules from host-specific Config
-            *[Match(**rule) for rule in Config.get('extra_float_rules', [])]
-        ]
-    )
+    floating_layout = kuro_layouts.KuroFloating(float_rules=[
+        # 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
+        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 qtile is not None:
+                bar.draw()
+
+    def log_debug(self, text):
+        if Config.get('verbose', False):
+            self.set_debug_text(text)
+        logger.debug(text)
+
+    def log_info(self, text):
+        self.set_debug_text(text)
+        logger.info(text)
 
     def initialize(self):
+        logger.error("Initializing Kuro theme...")
+        self.log_debug("Initializing Kuro Theme...")
+
         # Update color scheme
-        logger.warning("Initializing colorscheme...")
         self.initialize_colorscheme()
 
-        logger.warning("Initializing superclass...")
+        # Set settings
+        self.do_keyboard_updates = Config.get("do_keyboard_updates", True)
+
         super(Kuro, self).initialize()
 
-        logger.warning("Updating keys for groups and layouts...")
+        self.update()
+
+    def update(self):
+        # Update keys with keys for groups and layouts
         self.update_keys()
 
     def init_keys(self):
-        logger.warning("Initializing keys")
-        keys = [
+        self.log_debug("Initializing keys")
+
+        return [
             # Switch between windows in current stack pane
             Key([self.mod], "k", lazy.layout.down()),
             Key([self.mod], "j", lazy.layout.up()),
@@ -121,6 +146,9 @@ class Kuro(BaseTheme):
             # Floating toggle
             Key([self.mod, "shift"], 'f', lazy.window.toggle_floating()),
 
+            # Pinned toggle
+            Key([self.mod, "shift"], '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
@@ -136,8 +164,8 @@ class Kuro(BaseTheme):
             # Super-B to start webbrowser
             Key([self.mod], "b", lazy.spawn(Config.get('web_browser', "xterm links"))),
 
-            # Super-T to start file manager
-            Key([self.mod], "t", lazy.spawn(Config.get('file_manager', "thunar"))),
+            # Super-F to start file manager
+#            Key([self.mod], "f", lazy.spawn(Config.get('file_m4anager', "thunar"))),
 
             # Super-Shift-R to start spawncmd
             Key([self.mod, "shift"], "r", lazy.spawncmd()),
@@ -145,6 +173,17 @@ class Kuro(BaseTheme):
             # Lock shortcut
             Key([self.mod], "l", lazy.spawn(Config.get('lock_command', "i3lock"))),
 
+            # Backlight keys
+            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'))),
@@ -161,31 +200,27 @@ class Kuro(BaseTheme):
             # Alt screenshot
             Key([self.mod], "Print", lazy.spawn(Config.get('cmd_alt_screenshot', 'xfce4-screenshooter'))),
 
-            # Copy from clipboard history
-            Key([self.mod, "control"], "c", lazy.spawn(Config.get('cliphistory_command', 'true'))),
-
             # Toggle between different layouts as defined below
             Key([self.mod], "Tab", lazy.next_layout()),
 
             # Kill the current window
             Key([self.mod], "w", lazy.window.kill()),
 
-            # Restart/reload QTile (Restart only available in X11 backend)
-            Key([self.mod, "control"], "r", lazy.restart() if qtile.core.name == "x11" else lazy.reload_config()),
+            # Restart QTile
+            Key([self.mod, "control"], "r", lazy.restart()),
 
             # Shutdown QTile
             Key([self.mod, "control"], "q", lazy.shutdown()),
 
             # Update wallpaper
-            Key([self.mod, "control"], "w",
-                lazy.function(self.set_random_wallpaper), lazy.function(self.update_colorscheme)),
-
-            # Reload screen configuration
-            Key([self.mod, "control"], "s", lazy.spawn(Config.get('cmd_reconfigure_screens', 'true'))),
+            Key([self.mod, "control"], "w", lazy.function(self.update_wallpaper)),
 
             # Reload colorscheme
             Key([self.mod, "control"], "t", lazy.function(self.update_colorscheme)),
 
+            # Reorganize screens
+            Key([self.mod, "control"], "s", lazy.function(self.update_screens)),
+
             # Toggle static windows
             Key([self.mod], "p", lazy.function(self.toggle_window_static)),
 
@@ -193,7 +228,13 @@ class Kuro(BaseTheme):
             ##
             # Debug keyboard shortcuts
             ##
-            Key([self.mod, "shift", "control"], "w", lazy.function(display_wm_class)),
+            Key([self.mod, "control"], "c", lazy.function(display_wm_class)),
+
+            # Redraw the top bar
+            Key([self.mod, "shift", "control"], "r", lazy.function(self.redraw_bar)),
+
+            # Update visualizer widgets
+            Key([self.mod, "shift", "control"], "v", lazy.function(self.reinitialize_visualizers)),
 
             # Show extensive window info
             Key([self.mod, "shift", "control"], "i", lazy.function(self.show_window_info)),
@@ -201,36 +242,36 @@ class Kuro(BaseTheme):
             # Spawn a popup, and despawn it after 3 seconds
             Key([self.mod, "control"], "p", lazy.function(test_popups)),
         ]
-        # Extra keybinds from host-specific Config
-        keys.extend([
-            Key([(self.mod if m == 'mod' else m) for m in key['modifiers']], key['key'], key['action'])
-            for key in Config.get('extra_keys', [])
-        ])
-        return keys
 
     def init_groups(self):
-        logger.warning("Initializing groups")
-        # http://fontawesome.io/cheatsheet
+        self.log_debug("Initializing groups")
+        
         groups = []
-
-        for group in Config.get('groups', [{'name': '1'}]):
-            if 'layout' in group:
-                if group['layout'] == "floating":
-                    groups.append(Group(
-                        group['name'],
-                        layout="floating",
-                        layouts=[
-                            layout.Floating(**group.get('options', {}))
-                        ]
-                    ))
-                else:
-                    logger.warning(f"Unknown group layout for group '{group['name']}': {group['layout']}")
-            else:
-                groups.append(Group(group['name']))
+        
+        # http://fontawesome.io/cheatsheet
+        groups.append(Group("", spawn=Config.get('web_browser', "true")))
+        groups.append(Group("", spawn=Config.get('app_terminal_init', "true")))
+        groups.append(Group(""))
+        groups.append(Group("", spawn=Config.get('app_chat', "true")))
+        groups.append(Group("", spawn=Config.get('app_irc', "true")))
+        groups.append(Group("", spawn=Config.get('file_manager', "true")))
+        groups.append(Group("", spawn=Config.get('app_mail', "true")))
+        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=[
+            layout.Floating(
+                border_focus="#990000",
+                border_normal="#440000"
+            )
+        ]))
+        
         return groups
 
     def init_layouts(self):
-        logger.warning("Initializing layouts")
+        self.log_debug("Initializing layouts")
+
         return [
             kuro_layouts.KuroWmii(
                 theme=self,
@@ -250,7 +291,8 @@ class Kuro(BaseTheme):
         ]
 
     def init_widget_defaults(self):
-        logger.warning("Initializing widget_defaults")
+        self.log_debug("Initializing widget_defaults")
+
         return {
             "font": Config.get('font_topbar', "Sans"),
             "fontsize": Config.get('fontsize_topbar', 16),
@@ -258,12 +300,158 @@ class Kuro(BaseTheme):
         }
 
     def init_screens(self):
-        logger.warning("Initializing screens")
-        self.reinit_screens()
-        return self.screens
+        self.log_debug("Initializing screens")
+
+        self.num_screens = utils.get_screen_count()
+        if self.num_screens <= 0:
+            self.num_screens = 1
+
+        screens = []
+        for x in range(self.num_screens):
+            self.log_info("Initializing bars for screen {}".format(x))
+            widgets = []
+            widgets.extend([
+                widget.GroupBox(
+                    active=Config.get('colour_groupbox_icon_active', '#ffffff'),
+                    borderwidth=Config.get('width_groupbox_border', 1),
+                    disable_drag=Config.get('bool_groupbox_disable_drag', False),
+                    font=Config.get('font_groupbox', 'Arial'),
+                    fontsize=Config.get('fontsize_groupbox', 15),
+                    highlight_color=Config.get("colour_groupbox_border_normal", '#444444'),
+                    inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
+                    rounded=Config.get('bool_groupbox_rounded_borders', True),
+                    this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
+                    this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
+                    margin=Config.get('margin_groupbox', 0)
+                ),
+                widget.Prompt(**self.widget_defaults),
+
+                kuro.utils.widgets.KuroTaskList(
+                    border=Config.get('tasklist_border', '#ffffff'),
+                    borderwidth=Config.get('tasklist_borderwidth', 1),
+                    font=Config.get('tasklist_font', 'Arial'),
+                    fontsize=Config.get('tasklist_fontsize', 15),
+                    highlight_method=Config.get('tasklist_highlight_method', 'border'),
+                    max_title_width=Config.get('tasklist_max_title_width', 200),
+                    rounded=Config.get('tasklist_rounded', True),
+                    urgent_alert_method=Config.get('tasklist_urgent_alert_method', 'border'),
+                    urgent_border=Config.get('tasklist_urgent_border', '#ff0000'),
+                    margin=Config.get('margin_groupbox', 0)
+                )
+            ])
+
+            if Config.get('show_audio_visualizer', False):
+                widgets.append(kuro.utils.widgets.AudioVisualizerWidget(margin_x=0, margin_y=0))
+
+            widgets.extend([
+                kuro.utils.widgets.MediaWidget(ignore_players=Config.get('media_ignore_players', '')),
+                kuro.utils.widgets.TextSpacerWidget(fontsize=14),
+            ])
+
+            if Config.get('show_temperature', False):
+                widgets.extend([
+                    kuro.utils.widgets.ThermalSensorWidget(
+                        font=Config.get('font_topbar', 'Arial'),
+                        fontsize=Config.get('fontsize_topbar', 16),
+                        foreground=Config.get('thermal_colour', '#ffffff'),
+                        foreground_alert=Config.get('thermal_colour_alert', '#ff0000'),
+                        tag_sensor=Config.get('thermal_sensor', 'temp1'),
+                        chip=Config.get('thermal_chip', None),
+                        threshold=Config.get('thermal_threshold', 70),
+                        update_interval=5,
+                        fontsize_left=18, fontsize_right=11
+                    ),
+                ])
+
+            widgets.extend([
+                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),
+            ])
+            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,
+                    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),
+            ])
+            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),
+            ])
+
+            # Systray can only be on one screen, so put it on the first
+            if x == 0:
+                widgets.append(widget.Systray(**self.widget_defaults))
+
+            widgets.extend([
+                kuro.utils.widgets.KuroCurrentLayoutIcon(custom_icon_paths=Config.get('custom_layout_icon_paths', [])),
+                widget.Clock(format="%a %d %b, %H:%M", **self.widget_defaults),
+                kuro.utils.widgets.CheckUpdatesYay(
+                    colour_no_updates=Config.get('updates_colour_none', '#ffffff'),
+                    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),
+            ])
+
+            topbar = utils.KuroTopBar(
+                theme=self,
+                background=Config.get('bar_background', '#000000'),
+                opacity=Config.get('bar_opacity', 1.0),
+                widgets=widgets,
+                size=Config.get('height_groupbox', 30)
+            )
+
+            self.topbars.append(topbar)
+
+            screens.append(Screen(top=topbar))
+
+        # Add debug bars on each window if debugging is enabled
+        if Config.get('debug', False):
+            self.debug_textfields = []
+            for x in range(self.num_screens):
+                textfield = widget.TextBox("...", name="debugtext", **self.widget_defaults)
+                self.debug_textfields.append(textfield)
+                widgets = []
+                widgets.extend([
+                    widget.TextBox("  Debugging bar   ", name="default", **self.widget_defaults),
+                    textfield,
+                ])
+                screens[x].bottom = bar.Bar(
+                    widgets=widgets,
+                    size=Config.get('height_debugbar', 30)
+                )
+                self.debug_bars.append(screens[x].bottom)
+
+        return screens
 
     def init_mouse(self):
-        logger.warning("Initializing mouse")
+        self.log_debug("Initializing mouse")
+
         # Drag floating layouts.
         mouse = [
             Drag([self.mod], "Button1", lazy.window.set_position_floating(),
@@ -272,58 +460,12 @@ class Kuro(BaseTheme):
                  start=lazy.window.get_size()),
             Click([self.mod], "Button2", lazy.window.bring_to_front())
         ]
+
         return mouse
 
-    def initialize_colorscheme(self):
-        colors = None
-        if os.path.isfile(f"{Config.get('homedir', '~')}/.cache/wal/colors.json"):
-            with open(f"{Config.get('homedir', '~')}/.cache/wal/colors.json", 'r') as f:
-                try:
-                    colors = json.load(f)['colors']
-                except KeyError:
-                    colors = None
-
-        if colors:
-            # Update Config
-            Config.foreground = colors['color15']
-            Config.background = colors['color0']
-            Config.highlight = colors['color3']
-            Config.inactive_light = colors['color4']
-            Config.inactive_dark = colors['color5']
-            Config.bar_background = colors['color1']
-
-    def reinit_screens(self):
-        if Config.get("use_fake_screens", False):
-            logger.warning(f"Using fake screens!")
-            self.num_screens = Config.get("fake_screen_count", 1)
-        else:
-            self.num_screens = max(1, utils.get_screen_count())
-        logger.warning(f"Detected {self.num_screens} screens.")
-
-        screen_kwargs = Config.get("screen_kwargs", [])
-
-        for x in range(self.num_screens):
-            logger.warning(f"Reconfiguring bars for screen {x}")
-
-            try:
-                screen = self.screens[x]
-            except IndexError:
-                try:
-                    kwargs = screen_kwargs[x]
-                    logger.warning(f"Config for screen {x}: {kwargs}")
-                except IndexError:
-                    logger.warning(f"No kwarg config for screen {x}")
-                    kwargs = {}
-                screen = Screen(**kwargs)
-            
-            if screen.top is None:
-                screen.top = self.build_bar_for_screen(x)
-            topbar = screen.top
-
-            self.screens.append(screen)
-
     def update_keys(self):
-        logger.warning("Updating keys")
+        self.log_debug("Updating keys")
+
         for i, g in enumerate(self.groups):
             if i == 9:
                 i = -1
@@ -355,328 +497,167 @@ class Kuro(BaseTheme):
             Key([self.mod], "n", lazy.layout.normalize()),
         ])
 
-    def build_bar_for_screen(self, screen_num):
-        widgets = [
-            # Workspaces
-            kuro.utils.widgets.KuroGroupBox(
-                active=Config.get('colour_groupbox_icon_active', '#ffffff'),
-                borderwidth=Config.get('width_groupbox_border', 1),
-                disable_drag=Config.get('bool_groupbox_disable_drag', False),
-                font=Config.get('font_groupbox', 'Arial'),
-                fontsize=Config.get('fontsize_groupbox', 15),
-                highlight_color=Config.get("colour_groupbox_border_normal", '#444444'),
-                inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
-                rounded=Config.get('bool_groupbox_rounded_borders', True),
-                this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
-                this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
-                margin=Config.get('margin_groupbox', 0)
-            ),
+    # Util functions
+    @staticmethod
+    def redraw_bar(qtile):
+        for s in qtile.screens:
+            s.top.draw()
 
-            # Spawn prompt (only shown if activated)
-            widget.Prompt(**self.widget_defaults),
+    @staticmethod
+    def update_screens(qtile):
+        out = utils.call_process(["xrandr", "--current"])
+        video_mode = "nvidia"
+        #if "nvidia" in mode_out:
+        #    video_mode = "nvidia"
+        #elif "intel" in mode_out:
+        #    video_mode = "intel"
+        laptop_screen = None
+        screens = []
+        for x in out.split("\n"):
+            if " connected " in x:
+                if Config.get("laptop_screen_nvidia", None) is not None \
+                        and Config.get("laptop_screen_intel", None) is not None:
+                    if video_mode == "nvidia" and Config.get("laptop_screen_nvidia", None) in x:
+                        laptop_screen = x
+                    elif video_mode == "intel" and Config.get("laptop_screen_intel", None) in x:
+                        laptop_screen = x
+                    else:
+                        screens.append(x)
+                else:
+                    screens.append(x)
 
-            # Open window list
-            widget.TaskList(
-                border=Config.get('tasklist_border', '#ffffff'),
-                borderwidth=Config.get('tasklist_borderwidth', 1),
-                font=Config.get('tasklist_font', 'Arial'),
-                fontsize=Config.get('tasklist_fontsize', 15),
-                highlight_method=Config.get('tasklist_highlight_method', 'border'),
-                max_title_width=Config.get('tasklist_max_title_width', 200),
-                rounded=Config.get('tasklist_rounded', True),
-                urgent_alert_method=Config.get('tasklist_urgent_alert_method', 'border'),
-                urgent_border=Config.get('tasklist_urgent_border', '#ff0000'),
-                margin=Config.get('margin_groupbox', 0),
-                icon_size=Config.get('tasklist_iconsize', 17),
-                theme_mode=Config.get('tasklist_thememode', 'preferred'),
-                theme_path=Config.get('tasklist_themepath', '/usr/share/icons/Papirus-Dark'),
-                txt_floating=" ", txt_maximized=" ", txt_minimized=" ",
-            )
-        ]
+        # Only configure two screens. Open arandr if more screens are present.
+        if laptop_screen is not None and len(screens) == 1:
+            laptop = laptop_screen.split()[0]
+            other = screens[0].split()[0]
+            utils.call_process(["xrandr", "--output", laptop, "--below", other])
+            qtile.cmd_restart()
+        else:
+            utils.execute("arandr")
 
-        # Media widget(s)
-        if Config.get('show_media_widget', False):
-            widgets.extend([
-                # An MPRIS widget that shows the media play status as an icon.
-                widget.Mpris2(
-                    font=Config.get('font_groupbox', 'Arial'),
-                    fontsize=Config.get('fontsize_groupbox', 15),
-                    format="",
-                    scroll=False,
-                    playing_text="",
-                    paused_text="",
-                    stopped_text="",
-                    no_metadata_text="",
-                    name=f"media_icon{screen_num}",
-                    mouse_callbacks={
-                        "Button1": lazy.widget[f"media_icon{screen_num}"].play_pause(),
-                        "Button3": lazy.widget[f"media_icon{screen_num}"].next(),
-                        "Button4": lambda: None,
-                        "Button5": lambda: None,
-                    }
-                ),
-                # An MPRIS widget that shows the currently playing song information in a nice format.
-                widget.Mpris2(
-                    font=Config.get('font_topbar', 'Arial'),
-                    fontsize=Config.get('fontsize_topbar', 15),
-                    format="{xesam:title} - {xesam:artist} - {xesam:album}",
-                    scroll=True,
-                    width=300,  # Maximum width before widget starts scrolling
-                    playing_text="{track}",
-                    paused_text="{track}",
-                    stopped_text="",
-                    no_metadata_text="No metadata available",
-                    name=f"media_text{screen_num}",
-                    mouse_callbacks={
-                        "Button1": lazy.widget[f"media_icon{screen_num}"].play_pause(),
-                        "Button3": lazy.widget[f"media_icon{screen_num}"].next(),
-                        "Button4": lambda: None,
-                        "Button5": lambda: None,
-                    }
-                ),
-                # An MPRIS widget masquerading as a text widget, that only shows "|" when media is playing or paused.
-                widget.Mpris2(
-                    fontsize=14,
-                    format="",
-                    scroll=False,
-                    playing_text="|",
-                    paused_text="|",
-                    stopped_text="",
-                    no_metadata_text="",
-                    mouse_callbacks={
-                        "Button1": lambda: None,
-                        "Button4": lambda: None,
-                        "Button5": lambda: None,
-                    }
-                )
-            ])
+    def reinitialize_visualizers(self):
+        if Config.get("show_audio_visualizer", False):
+            logger.warning("Reinitializing visualizers...")
+            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()
 
-        # Sensor widgets
-        sensor_widgets = []
+    def update_visualizers(self):
+        if Config.get("show_audio_visualizer", False):
+            logger.warning("Updating visualizers..")
+            for screen in qtile.screens:
+                for widget in screen.top.widgets:
+                    if isinstance(widget, kuro.utils.widgets.AudioVisualizerWidget):
+                        if widget.client is None:
+                            logger.warning("Spawning for screen {}".format(screen))
+                            utils.execute(Config.get('visualizer_app', "glava"))
+                        else:
+                            widget.update_graph()
 
-        # Temperature sensor
-        if Config.get('show_temperature', False):
-            sensor_widgets.append(kuro.utils.widgets.ThermalSensorWidget(
-                font=Config.get('font_topbar', 'Arial'),
-                fontsize=Config.get('fontsize_topbar', 16),
-                foreground=Config.get('thermal_colour', '#ffffff'),
-                foreground_alert=Config.get('thermal_colour_alert', '#ff0000'),
-                tag_sensor=Config.get('thermal_sensor', 'temp1'),
-                chip=Config.get('thermal_chip', None),
-                threshold=Config.get('thermal_threshold', 70),
-                update_interval=5,
-                fontsize_left=18, fontsize_right=11
-            ))
+    def show_window_info(self, qtile):
+        window = qtile.current_window if qtile else None
 
-        # CPU/Memory/Disk sensors
-        sensor_widgets.extend([
-            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),
-        ])
+        import pprint
+        if window:
+            info = window.cmd_inspect() or None
+            name = window.name
 
-        widgets.extend([
-            widget.WidgetBox(
-                font=Config.get('font_groupbox', 'Arial'),
-                fontsize=Config.get('fontsize_groupbox', 15),
-                text_open="[>]",
-                text_closed="[]",
-                widgets=sensor_widgets
-            ),
-            widget.TextBox(fontsize=14, text="|")
-        ])
+            utils.notify(title="Window properties {}".format(name),
+                         content="{}".format(pprint.pformat(vars(window))))
+            logger.warning("{}".format(pprint.pformat(vars(window))))
 
-        # Battery level
-        if Config.get('show_battery_widget', False):
-            widgets.extend([
-                kuro.utils.widgets.BatteryInfoWidget(fontsize_left=16, fontsize_right=11),
-            ])
+            if info:
+                info = pprint.pformat(info)
+                utils.notify(title="Window info of {}".format(name),
+                             content="{}".format(info))
+                logger.warning("{}".format(info))
 
-        # Volume widget(s)
-        for sink_name in Config.get('volume_pulse_sinks', []):
-            widgets.append(
-                kuro.utils.widgets.VolumeInfoWidget(
-                    pulse_sink=sink_name,
-                    fontsize_left=18,
-                    fontsize_right=11,
-                    font_left=Config.get('font_groupbox', None),
-                )
-            )
+    # @staticmethod
+    def toggle_window_static(self, qtile):
+        window = qtile.current_window
+        if window in self.static_windows:
+            utils.notify("Unpinned {}".format(window.name), "{} has been unpinned".format(window.name))
+            self.static_windows.remove(window)
+            del window.is_static_window
+        else:
+            utils.notify("Pinned {}".format(window.name), "{} has been pinned".format(window.name))
+            self.static_windows.append(window)
+            window.is_static_window = True
 
-        # Network information
-        widgets.extend([
-            widget.TextBox(fontsize=14, text="|"),
-            kuro.utils.widgets.NetworkInfoWidget(
-                fontsize_left=16, fontsize_right=14,
-                wireless_interface=Config.get('wifi_interface', None),
-                wired_interface=Config.get('wired_interface', None),
-                config_application=Config.get('network_config', None),
-            ),
-        ])
+        window.floating = True
 
-        # GPU widget
-        if Config.get('show_gpu_widget', False):
-            widgets.extend([
-                kuro.utils.widgets.GPUStatusWidget(
-                    theme_path=Config.get(
-                        'gpu_theme_path',
-                        '/usr/lib/python3.11/site-packages/libqtile/resources/battery-icons'),
-                    padding=0,
-                )
-            ])
-        widgets.append(widget.TextBox(fontsize=14, text="|"))
-
-        # Determine systray to use (X or Wayland)
-        # X system tray can only be on one screen, so only put it on the first
-        if qtile.core.name == "x11" and screen_num == 0:
-            widgets.append(widget.Systray(**self.widget_defaults))
-        elif qtile.core.name != "x11":
-            widgets.append(extra_widget.StatusNotifier(**self.widget_defaults))
-
-        # Layout switcher, clock and Screen ID
-        widgets.extend([
-            widget.CurrentLayout(mode="icon", custom_icon_paths=Config.get('custom_layout_icon_paths', [])),
-            widget.Clock(format="%a %d %b ", **self.widget_defaults),
-            widget.Clock(
-                format="%H:%M",
-                font=Config.get('font_topbar', "Sans"),
-                fontsize=16,
-                padding=0,
-            ),
-            widget.Clock(
-                format=":%S ",
-                font=Config.get('font_topbar', "Sans"),
-                fontsize=11,
-                padding=0,
-            ),
-            widget.CurrentScreen(
-                active_color="66FF66", inactive_color="FFFFFF",
-                active_text=f"#{screen_num}", inactive_text=f"#{screen_num}",
-                **self.widget_defaults
-            )
-        ])
-
-        # Build the bar
-        return bar.Bar(
-            background=f"{Config.get('bar_background', '#000000')}{Config.get('bar_rgba_opacity', 'AA')}",
-            opacity=Config.get('bar_opacity', 1.0),
-            widgets=widgets,
-            size=Config.get('height_groupbox', 30)
-        )
+    # Pinned toggle function
+    @staticmethod
+    def toggle_pinned(qtile):
+        windows = qtile.cmd_windows()
+        print(windows)
 
     # QTile base callbacks
     def callback_startup_once(self, *args, **kwargs):
-        logger.warning("Callback Startup Once")
         if not hasattr(qtile, 'theme_instance'):
             # Save theme instance in qtile
             qtile.theme_instance = self
-        self.set_random_wallpaper()
+        self.update_wallpaper(qtile)
+
+        # Setup audio
+        # p = utils.execute_once(["qjackctl"])
+        # p.wait()
 
     def callback_startup(self):
-        logger.warning("Callback Startup")
         if not hasattr(qtile, 'theme_instance'):
             # Save theme instance in qtile
             qtile.theme_instance = self
 
-        logger.warning("Restoring wallpaper...")
         if self.current_wallpaper:
-            p = utils.execute_once([*Config.get('cmd_wal', ['wallust', 'run']), "{}".format(self.current_wallpaper)])
-            if p:
-                p.wait()
+            #p = utils.execute_once(["wal", "-n", "-i", "{}".format(self.current_wallpaper)])
+            p = utils.execute_once(["wallust", "run", "{}".format(self.current_wallpaper)])
+            p.wait()
         else:
             wallpaper = None
-            if os.path.isfile(f"{Config.get('homedir', '~')}/.cache/wal/colors.json"):
-                with open(f"{Config.get('homedir', '~')}/.cache/wal/colors.json", 'r') as f:
+            if os.path.isfile("/home/kevin/.cache/wal/colors.json"):
+                with open("/home/kevin/.cache/wal/colors.json", 'r') as f:
                     try:
                         wallpaper = json.load(f)['wallpaper']
                     except KeyError:
                         wallpaper = None
             if wallpaper:
-                self.set_wallpaper(wallpaper)
+                Kuro.set_wallpaper(qtile, wallpaper)
             else:
-                logger.warning("No wallpaper to restore.")
+                p = utils.execute_once("nitrogen --restore")
+                p.wait()
+
+        self.log_info("Starting compositor...")
+        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")
+
+        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")
+
+        self.log_info("Starting automatic backup scheduler...")
+        utils.execute_once("/usr/bin/vorta")
 
         # Update color scheme
-        self.update_colorscheme()
+        self.initialize_colorscheme()
 
-    def callback_startup_complete(self, *args, **kwargs):
-        logger.warning("Callback Startup Complete")
-        if not hasattr(qtile, 'theme_instance'):
-            # Save theme instance in qtile
-            qtile.theme_instance = self
-
-        # Update color scheme
-        self.update_colorscheme()
-
-        # Setup XDG Desktop Portal on Wayland
-        if qtile.core.name == "wayland":
-            self.setup_xdg_desktop_portal()
-
-        # After first startup is complete, autostart configured apps
-        logger.warning("Autostarting apps...")
-        for category in Config.get("apps_autostart", {}).keys():
-            if qtile.core.name == category or category == "common":
-                logger.warning(f"Autostarting apps for {category}...")
-                for app in Config.get("apps_autostart", {}).get(category, []):
-                    logger.warning(f"Starting '{app}'...")
-                    utils.execute_once(app)
-            else:
-                logger.warning(f"Skipping autostart apps for {category}, because core is {qtile.core.name}...")
-
-        for app in Config.get("apps_autostart_group", []):
-            if all(x in app.keys() for x in ["group", "command"]):
-                logger.warning(f"Starting '{app['command']}' in group {app['group']}...")
-                utils.start_in_group_once(theme=self, qtile=qtile, **app)
-            else:
-                logger.warning(f"Invalid app in 'apps_autostart_group', "
-                               f"must have 'group' and 'command' keys: {app}...")
-        logger.warning("Autostart complete")
-        cur_time = time.time()
-        logger.warning(f"QTile startup completed! Started up in {(cur_time - self.startup_time):.1f} seconds!")
-        self.startup_completed = True
-
-    def callback_client_managed(self, *args, **kwargs):
-        client: Optional[Window] = args[0] if len(args) > 0 else None
-
-        # TODO: Move get_pid to an utility function
-        w_pid = None
-        try:
-            w_pid = client.get_pid()
-        except AttributeError:  # Some windows might not have this .get_pid method. Try other ways
-            if isinstance(client, WaylandXWindow) or isinstance(client, WaylandXStatic):
-                w_pid = client.surface.pid
-            elif isinstance(client, XorgXWindow):
-                w_pid = client.get_net_wm_pid()
-            elif isinstance(client, LayerStatic):
-                pass  # Wayland background layer 'window'
-            else:
-                logger.error(f"Unknown window type {client.__class__.__name__}")
-
-        if w_pid is not None and w_pid in self.autostart_app_rules.keys():
-            rule_id = self.autostart_app_rules[w_pid]
-            logger.warning(f"Removing rule {rule_id} for PID {w_pid}, client {client.name}")
-            lazy.remove_rule(rule_id)
-
-    def callback_client_killed(self, *args, **kwargs):
-        client = args[0]
-        logger.warning("Client {} Killed".format(client))
-
-        # If this window was static, remove it from the static window list
-        if hasattr(client, "is_static_window") and client.is_static_window:
-            logger.warning("Removing static window {}".format(client.name))
-            del client.is_static_window
-            self.static_windows.remove(client)
-
-    def callback_screen_change(self, *args, **kwargs):
-        logger.warning(f"Screen configuration changed, reinitializing screens")
-        self.reinit_screens()
-        qtile.reconfigure_screens()
-        #qtile.reconfigure_screens()  # Twice, see: https://github.com/qtile/qtile/issues/4673#issuecomment-2196459114
-
-    #def callback_screens_reconfigured(self, *args, **kwargs):
-        logger.warning(f"Screens were reconfgured, updating wallpapers and color scheme")
-        self.set_wallpaper(self.current_wallpaper)
-        self.update_colorscheme()
+    # def callback_screen_change(self, *args, **kwargs):
+    #     for window in self.static_windows:
+    #         window.togroup()
 
     def callback_setgroup(self, *args, **kwargs):
         for window in self.static_windows:
@@ -689,28 +670,121 @@ class Kuro(BaseTheme):
                     del window.is_static_window
                     self.static_windows.remove(window)
 
-    def show_window_info(self, *args, **kwargs):
-        import pprint
-        window = qtile.current_window
-        if window:
-            logger.warning(f"Window properties {window.name}\n{pprint.pformat(vars(window))}")
-            if window.info():
-                logger.warning(f"Window info of {window.name}\n{pprint.pformat(window.info())}")
+    def callback_focus_change(self, *args, **kwargs):
+        if self.do_keyboard_updates:
+            kb_handle_focus_change(self)
 
-    def toggle_window_static(self, *args, **kwargs):
-        window = qtile.current_window
-        if window in self.static_windows:
-            utils.notify(qtile, "Unpinned {}".format(window.name), "{} has been unpinned".format(window.name))
-            self.static_windows.remove(window)
-            del window.is_static_window
-        else:
-            utils.notify(qtile, "Pinned {}".format(window.name), "{} has been pinned".format(window.name))
-            self.static_windows.append(window)
-            window.is_static_window = True
+    initial_windows = []
 
-        window.floating = True
+    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 qtile.no_spawn:
+            dg = qtile.dgroups
+            for r in dg.rules:
+                pid = -1
+                # noinspection PyProtectedMember
+                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))
 
-    def set_random_wallpaper(self, *args, **kwargs):
+            self.callback_client_new()
+
+        # After first startup is complete, start the audio apps that can only be started after boot is complete
+        if not qtile.no_spawn:
+            for app in Config.get("apps_audio_afterstart", []):
+                utils.execute_once(app)
+
+        # Update color scheme
+        Kuro.update_colorscheme(qtile)
+
+    def callback_client_new(self, *args, **kwargs):
+        client = args[0] if len(args) > 0 else None
+
+        if len(self.initial_windows) > 0:
+            init = self.initial_windows.copy()
+            for pid, gname in init:
+                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 = qtile.dgroups.rules_map.copy()
+                                for rid, r in c.items():
+                                    if r.matches(window):
+                                        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(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 qtile.screens:
+                    for widget in screen.top.widgets:
+                        if not placed and isinstance(widget, kuro.utils.widgets.AudioVisualizerWidget):
+                            if widget.client is None:
+                                viz_info = widget.info()
+                                pos_x = viz_info['offset'] + widget.margin_x
+                                pos_y = 0 + widget.margin_y
+                                width = viz_info['width'] - (2 * widget.margin_x)
+                                height = viz_info['height'] - (2 * widget.margin_y)
+                                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.opacity = Config.get("bar_opacity", 1.0)
+                                widget.set_client(c, screen)
+                                placed = True
+                if not placed:
+                    if Config.get("kill_unnecessary_glava_processes", False):
+                        logger.warning("Killing GLava {} because there is no widget where it can fit".format(client))
+                        utils.notify("Glava", "Killing new GLava process because there is no screen without a visualizer")
+                        client.kill()
+                    else:
+                        logger.warning("Not repositioning GLava {} because there is no widget where it can fit".format(client))
+                        utils.notify("Glava", "Not repisitioning new GLava process because there is no screen without a visualizer")
+
+        # If this is Non-Mixer, move it to the audio group
+        logger.warning("Processing window {}".format(client))
+        if client is not None and client.window.get_wm_class() == ('Non-Mixer', 'Non-Mixer'):
+            logger.warning("Moving to correct group!")
+            client.window.togroup("")
+
+
+
+    def callback_client_killed(self, *args, **kwargs):
+        client = args[0]
+        logger.warning("Client {} Killed".format(client))
+
+        # Detach visualizer from widget if it was a visualizer window
+        if isinstance(client, KuroStatic):
+            for screen in qtile.screens:
+                for widget in screen.top.widgets:
+                    if isinstance(widget, kuro.utils.widgets.AudioVisualizerWidget):
+                        if widget.client == client:
+                            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
+
+        # If this window was static, remove it from the static window list
+        if hasattr(client, "is_static_window") and client.is_static_window:
+            logger.warning("Removing static window {}".format(client.name))
+            del client.is_static_window
+            self.static_windows.remove(client)
+
+    @staticmethod
+    def update_wallpaper(qtile):
         wallpapers = []
         wallpaper_dir = Config.get("desktop_bg_folder", "")
 
@@ -726,46 +800,64 @@ class Kuro(BaseTheme):
         except os.error as e:
             logger.warning("Could not load wallpapers from directory: {}".format(e))
 
+
         if wallpapers:
             if Config.get("desktop_bg_override", False):
-                wallpaper_file = Config.get("desktop_bg_override", "")
+                qtile.theme_instance.current_wallpaper = Config.get("desktop_bg_override", "")
             else:
-                wallpaper_file = os.path.join(wallpaper_dir, random.choice(wallpapers))
-                logger.warning(f"Selected new wallpaper: {wallpaper_file}")
-            self.set_wallpaper(wallpaper_file)
+                qtile.theme_instance.current_wallpaper = os.path.join(wallpaper_dir, random.choice(wallpapers))
+                logger.warning("Selected new wallpaper: {}".format(qtile.theme_instance.current_wallpaper))
+            Kuro.set_wallpaper(qtile, qtile.theme_instance.current_wallpaper)
         else:
-            logger.warning("Random wallpaper requested but no wallpapers are available.")
+            utils.execute_once("nitrogen --restore")
 
-    def set_wallpaper(self, filename):
-        if qtile.core.name == "x11":
-            p = utils.execute_once(f"{Config.get('x11_wallpaper_config_command', 'wal-nitrogen-noupdate')} {filename}")
-            if p:
-                p.wait()
-        else:
-            # Wayland can set wallpaper in qtile directly per screen
-            for screen_i, screen in enumerate(qtile.screens):
-                sinfo = screen.info()
-                sfilename = filename
-                if sinfo.get('width', 100) < sinfo.get('height', 10):
-                    # Vertical screen, see if there is a vertical alt wallpaper
-                    basename, ext = os.path.splitext(filename)
-                    new_filename = f"{basename}.vertical{ext}"
-                    if os.path.isfile(new_filename):
-                        sfilename = new_filename
-                logger.warning(f"Setting Screen#{screen_i} wallpaper to {sfilename}.")
-                screen.set_wallpaper(sfilename, "fill")
-        self.current_wallpaper = filename
 
-    def update_colorscheme(self, *args, **kwargs):
-        if self.current_wallpaper:
-            logger.warning(f"Updating wal colors for wallpaper {self.current_wallpaper}")
-            p = utils.execute_once([*Config.get('cmd_wal', ['wallust', 'run']), "{}".format(self.current_wallpaper)])
-            if p:
-                p.wait()
+    @staticmethod
+    def set_wallpaper(qtile, filename):
+        p = utils.execute_once("{} {}".format(Config.get('wallpaper_config_command', 'wal-nitrogen-noupdate'),
+                                              filename))
+        p.wait()
+        qtile.theme_instance.current_wallpaper = filename
+        Kuro.update_colorscheme(qtile)
+
+
+    @staticmethod
+    def update_mediaclients(*args, **kwargs):
+        return str(str(args) + " " + str(kwargs))
+
+
+    @staticmethod
+    def initialize_colorscheme():
+        colors = None
+        if os.path.isfile("/home/kevin/.cache/wal/colors.json"):
+            with open("/home/kevin/.cache/wal/colors.json", 'r') as f:
+                try:
+                    colors = json.load(f)['colors']
+                except KeyError:
+                    colors = None
+
+        if colors:
+            # Update Config
+            Config.foreground = colors['color15']
+            Config.background = colors['color0']
+            Config.highlight = colors['color3']
+            Config.inactive_light = colors['color4']
+            Config.inactive_dark = colors['color5']
+            Config.bar_background = colors['color1']
+
+    @staticmethod
+    def update_colorscheme(qtile):
+        """
+        :type qtile: libqtile.manager.Qtile
+        """
+        if qtile.theme_instance.current_wallpaper:
+            #p = utils.execute(["wal", "-n", "-i", "{}".format(qtile.theme_instance.current_wallpaper)])
+            p = utils.execute(["wallust", "run", "{}".format(qtile.theme_instance.current_wallpaper)])
+            p.wait()
 
         colors = None
-        if os.path.isfile(f"{Config.get('homedir', '~')}/.cache/wal/colors.json"):
-            with open(f"{Config.get('homedir', '~')}/.cache/wal/colors.json", 'r') as f:
+        if os.path.isfile("/home/kevin/.cache/wal/colors.json"):
+            with open("/home/kevin/.cache/wal/colors.json", 'r') as f:
                 try:
                     colors = json.load(f)['colors']
                 except KeyError:
@@ -789,82 +881,62 @@ class Kuro(BaseTheme):
                         layout.border_normal = colors['color1']
                         layout.border_normal_stack = colors['color1']
 
-            for screen_i, screen in enumerate(qtile.screens):
+            for screen in qtile.screens:
                 bar = screen.top
-                logger.warning(f"Updating colorscheme for screen {screen_i}")
-                if bar:
-                    bar.background = f"{colors['color1']}{Config.get('bar_rgba_opacity', 'AA')}"
-                    bar.drawer.clear(bar.background)
+                bar.background = colors['color1']
+                bar.drawer.clear(bar.background)
+                for w in bar.widgets:
+                    if hasattr(w, '_update_drawer'):
+                        try:
+                            w._update_drawer()
+                        except Exception as e:
+                            logger.error("Error while updating drawer for widget {}: {}".format(w, e))
 
-                    def update_widget(w):
-                        if hasattr(w, '_update_drawer'):
-                            try:
-                                w._update_drawer()
-                            except Exception as e:
-                                logger.error("Error while updating drawer for widget {}: {}".format(w, e))
+                    if hasattr(w, 'foreground'):
+                        w.foreground = colors['color15']
 
-                        if hasattr(w, 'foreground'):
-                            w.foreground = colors['color15']
+                    if hasattr(w, 'foreground_normal'):
+                        w.foreground_normal = colors['color15']
 
-                        if hasattr(w, 'foreground_normal'):
-                            w.foreground_normal = colors['color15']
+                    if hasattr(w, 'foreground_alert'):
+                        w.foreground_alert = colors['color3']
 
-                        if hasattr(w, 'foreground_alert'):
-                            w.foreground_alert = colors['color3']
+                    if hasattr(w, 'border'):
+                        w.border = colors['color15']
 
-                        if hasattr(w, 'border'):
-                            w.border = colors['color15']
+                    if hasattr(w, 'active'):
+                        w.active = colors['color15']
 
-                        if hasattr(w, 'active'):
-                            w.active = colors['color15']
+                    if hasattr(w, 'highlight_color'):
+                        w.highlight_color = colors['color3']
 
-                        if hasattr(w, 'highlight_color'):
-                            w.highlight_color = colors['color3']
+                    if hasattr(w, 'inactive'):
+                        w.inactive = colors['color8']
 
-                        if hasattr(w, 'inactive'):
-                            w.inactive = colors['color8']
+                    if hasattr(w, 'this_current_screen_border'):
+                        w.this_current_screen_border = colors['color15']
 
-                        if hasattr(w, 'this_current_screen_border'):
-                            w.this_current_screen_border = colors['color15']
+                    if hasattr(w, 'this_screen_border'):
+                        w.this_screen_border = colors['color15']
 
-                        if hasattr(w, 'this_screen_border'):
-                            w.this_screen_border = colors['color15']
+                    if hasattr(w, 'other_current_screen_border'):
+                        w.other_current_screen_border = colors['color8']
 
-                        if hasattr(w, 'other_current_screen_border'):
-                            w.other_current_screen_border = colors['color8']
+                    if hasattr(w, 'other_screen_border'):
+                        w.other_screen_border = colors['color8']
 
-                        if hasattr(w, 'other_screen_border'):
-                            w.other_screen_border = colors['color8']
+                    if isinstance(w, kuro.utils.widgets.AudioVisualizerWidget):
+                        w.graph_color = colors['color15']
+                        w.fill_color = colors['color8']
 
-                        if isinstance(w, widget.WidgetBox):
-                            for subw in w.widgets:
-                                update_widget(subw)
+                bar.draw()
 
-                    for w in bar.widgets:
-                        update_widget(w)
+            # 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 {}00".format(colors['color15'], colors['color1'][1:]))
+            qtile.theme_instance.reinitialize_visualizers()
 
-                    bar.draw()
-                else:
-                    logger.warning(f"Screen {screen_i} has no bar?")
-
-            # Attempt to call pywalfox to update firefox/thunderbird colors
-            try:
-                logger.warning(f"Calling 'pywalfox update'...")
-                p = utils.execute(["pywalfox", "update"])
-                if p:
-                    p.wait()
-            except subprocess.SubprocessError as e:
-                logger.error(f"Error running 'pywalfox update': {e}")
-
-            utils.notify(qtile,
-                         "Updated colorscheme!",
-                         f"active: {colors['color15']}, inactive: {colors['color1']}")
-
-    def setup_xdg_desktop_portal(self):
-        # XDG Desktop portal is used for screensharing, screenshots and filepickers in wayland.
-        # To work correctly, it needs to have two env variables set in the systemd user session
-        logger.warning(f"Setting XDG_CURRENT_DESKTOP env and updating XDG Desktop Portal configuration...")
-        os.environ["XDG_CURRENT_DESKTOP"] = "qtile"
-        subprocess.Popen(["systemctl", "--user", "import-environment", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP"])
-        subprocess.Popen(["dbus-update-activation-environment", "--systemd", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP=qtile"])
-        subprocess.Popen(["systemctl", "--user", "restart", "xdg-desktop-portal"])
+            utils.notify(
+                "Updated colorscheme!",
+                "active: {}, inactive: {}".format(colors['color15'], colors['color1'])
+            )
diff --git a/kuro/utils/__init__.py b/kuro/utils/__init__.py
index 7d8a838..e69de29 100644
--- a/kuro/utils/__init__.py
+++ b/kuro/utils/__init__.py
@@ -1,31 +0,0 @@
-import importlib
-import socket
-import traceback
-from libqtile.log_utils import logger
-
-def load_config_class():
-    # Try to import host-specific configuration first
-    hostname = socket.gethostname().lower()
-    if hostname:
-        try:
-            host_module = importlib.import_module(f"kuro.config.{hostname}")
-            return getattr(host_module, "Config")
-        except ImportError:
-            pass
-    logger.warning(f"No host-specific configuration available for {hostname}. Loading general config...")
-
-    # If no config yet, load general Kuro Config object
-    try:
-        conf_module = importlib.import_module("kuro.config")
-        return getattr(conf_module, "Config")
-    except ImportError as e:
-        logger.error(traceback.format_exc())
-    logger.error("Could not load Kuro Config. Trying to load BaseConfig. Error: {}".format(e))
-
-    # If no config yet, load fallback BaseConfig
-    try:
-        base_module = importlib.import_module("kuro.base")
-        return getattr(base_module, "BaseConfig")
-    except ImportError as e:
-        logger.error(traceback.format_exc())
-    return None
diff --git a/kuro/utils/general.py b/kuro/utils/general.py
index 35944f2..f63e1ad 100644
--- a/kuro/utils/general.py
+++ b/kuro/utils/general.py
@@ -2,14 +2,20 @@ import re
 import subprocess
 import traceback
 from time import sleep
-from typing import List
 
 import notify2
+import six
 from dbus import DBusException
 from libqtile import widget
+from libqtile.backend.x11.window import Internal
+from libqtile.bar import Bar
 from notify2 import Notification, URGENCY_NORMAL
 from libqtile.log_utils import logger
-from libqtile import qtile
+
+try:
+    notify2.init("QTileWM")
+except DBusException as e:
+    logger.error("Could not initialize notify2: {}".format(e))
 
 BUTTON_LEFT = 1
 BUTTON_MIDDLE = 2
@@ -32,70 +38,47 @@ def is_running(process):
 
 
 def execute(process):
-    try:
+    if isinstance(process, list):
+        return subprocess.Popen(process)
+    elif isinstance(process, str):
+        return subprocess.Popen(process.split())
+    else:
+        pass
+
+
+def execute_once(process):
+    if not is_running(process):
         if isinstance(process, list):
             return subprocess.Popen(process)
         elif isinstance(process, str):
             return subprocess.Popen(process.split())
         else:
-            logger.info(f"Failed to execute_once")
-    except FileNotFoundError as e:
-        logger.error(f"Could not execute {process}, FileNotFoundError - {e}")
-
-
-def execute_once(process):
-    logger.info(f"Attempting to execute_once: {process}")
-    if not is_running(process):
-        return execute(process)
-    logger.info(f"Process was already running: {process}")
-
-
-def start_in_group(theme, qtile, group: str, command: List[str], floating: bool = False,
-                   intrusive: bool = False, dont_break: bool = False):
-    try:
-        proc = subprocess.Popen(command)
-        match_args = {"net_wm_pid": proc.pid}
-        rule_args = {
-            "float": floating,
-            "intrusive": intrusive,
-            "group": group,
-            "break_on_match": not dont_break,
-        }
-        rule_id = qtile.add_rule(match_args, rule_args)
-        theme.autostart_app_rules[proc.pid] = rule_id
-        return proc
-    except FileNotFoundError as e:
-        logger.error(f"Could not execute {command}, FileNotFoundError - {e}")
-
-
-def start_in_group_once(theme, qtile, group: str, command: List[str], floating: bool = False,
-                        intrusive: bool = False, dont_break: bool = False):
-    logger.info(f"Attempting to start_in_group_once: {command}")
-    if not is_running(command):
-        return start_in_group(theme=theme, qtile=qtile, group=group, command=command,
-                              floating=floating, intrusive=intrusive, dont_break=dont_break)
-    logger.info(f"Process was already running: {command}")
+            pass
 
 
 def call_process(command, **kwargs):
     """
-    Run the given command and return the string from stdout.
+        This method uses `subprocess.check_output` to run the given command
+        and return the string from stdout, which is decoded when using
+        Python 3.
     """
-    return subprocess.check_output(command, **kwargs).decode()
+    output = subprocess.check_output(command, **kwargs)
+    if six.PY3:
+        output = output.decode()
+    return output
 
 
 def get_screen_count():
     try:
-        if qtile.core.name == "x11":
-            logger.info("Using xrandr to detect screen count")
-            output = subprocess.check_output("xrandr -q".split()).decode('utf-8')
-            output = [x for x in output.split("\n") if " connected" in x]
-            return max(1, len(output))
-        else:
-            return max(1, len(qtile.core.get_screen_info()))
+        output = subprocess.check_output("xrandr -q".split()).decode('utf-8')
+        output = [x for x in output.split("\n") if " connected" in x]
     except subprocess.CalledProcessError:
-        pass
-    return 1
+        return 1
+
+    if output:
+        return len(output)
+    else:
+        return 1
 
 
 def bar_separator(config):
@@ -104,21 +87,8 @@ def bar_separator(config):
                       padding=config.get('padding_spacer', 4),
                       )
 
-def init_notify(qtile):
-    #if qtile and qtile.theme_instance and qtile.theme_instance.startup_completed:
-    try:
-        if not notify2.is_initted():
-            logger.warning("Initializing Notify2")
-            notify2.init("QTileWM")
-    except DBusException:
-        logger.error(f"Failed to initialize Notify2 (DBus error), retrying later.")
-    except Exception:
-        logger.error(f"Failed to initialize Notify2 (Generic error), retrying later.")
-    #else:
-    #    logger.warning(f"Not initializing Notify2 yet, QTile startup not completed.")
 
-
-def notify(qtile, title, content, urgency=URGENCY_NORMAL, timeout=5000, image=None):
+def notify(title, content, urgency=URGENCY_NORMAL, timeout=5000, image=None):
     if image is not None:
         notification = Notification(
             summary=title, message=content,
@@ -131,14 +101,13 @@ def notify(qtile, title, content, urgency=URGENCY_NORMAL, timeout=5000, image=No
     notification.set_timeout(timeout)
     notification.set_urgency(urgency)
 
-    init_notify(qtile)
-
     try:
-        try:
-            return notification.show()
-        except notify2.UninittedError:
-            logger.warning("Notify2 is not initialized")
-    except Exception as e:
+        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())
 
@@ -156,10 +125,6 @@ def spawn_popup(qtile, x, y, text):
     :return: The popup instance
     :rtype: Internal
     """
-    if qtile.core.name == "x11":
-        from libqtile.backend.x11.window import Internal
-    else:
-        from libqtile.backend.wayland.window import Internal
     popup = Internal.create(
         qtile, x, y, 100, 100, opacity=1
     )
@@ -190,14 +155,14 @@ def test_popups(qtile):
 
 
 def display_wm_class(qtile):
-    window = qtile.current_window if qtile else None
+    window = qtile.currentWindow if qtile else None
 
     if window:
-        wm_class = window.get_wm_class() or None
+        wm_class = window.window.get_wm_class() or None
         name = window.name
 
         if wm_class:
-            notify(qtile=qtile, title="WM_Class of {}".format(name),
+            notify(title="WM_Class of {}".format(name),
                    content="{}".format(wm_class),
                    urgency=notify2.URGENCY_CRITICAL)
 
@@ -224,3 +189,76 @@ def bluetooth_audio_sink():
 
 def bluetooth_audio_connected():
     return bluetooth_audio_sink() != -1
+
+
+class KuroTopBar(Bar):
+    def __init__(self, theme, widgets, size, **config):
+        self.theme = theme
+        super(KuroTopBar, self).__init__(widgets, size, **config)
+
+    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
+        self.window.window.set_property("_NET_WM_NAME", "KuroTopBar")
+        self.window.update_name()
+
+    def draw(self):
+        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._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 self.horizontal:
+                    self.drawer.draw(offsetx=end, width=self._length - end)
+                else:
+                    self.drawer.draw(offsety=end, height=self._length - end)
+
+                self.theme.update_visualizers()
+
+    def handle_enter_notify(self, e):
+        # self.theme.log_debug("Bar HandleEnterNotify")
+        #
+        # self.window.opacity = Config.get('bar_hover_opacity', 1.0)
+        # print("Bar Hover Enter")
+        #
+        # try:
+        #     hovered_widget = [x for x in self.widgets if (x.offsetx + x.width) >= e.event_x][0]
+        # except IndexError:
+        #     hovered_widget = None
+        #
+        # self.theme.log_debug("Hovered over {}".format(hovered_widget))
+        #
+        # if hasattr(hovered_widget, "handle_hover_enter"):
+        #     hovered_widget.handle_hover_enter(e)
+
+        self.draw()
+
+    def handle_leave_notify(self, e):
+        # self.theme.log_debug("Bar HandleLeaveNotify")
+        #
+        # self.window.opacity = Config.get('bar_opacity', 1.0)
+        # print("Bar Hover Leave")
+        #
+        # try:
+        #     hovered_widget = [x for x in self.widgets if (x.offsetx + x.width) >= e.event_x][0]
+        # except IndexError:
+        #     hovered_widget = None
+        #
+        # self.theme.log_debug("Hovered over {}".format(hovered_widget))
+        #
+        # if hasattr(hovered_widget, "handle_hover_leave"):
+        #     hovered_widget.handle_hover_leave(e)
+
+        self.draw()
+
+
diff --git a/kuro/utils/kb_backlight.py b/kuro/utils/kb_backlight.py
index e684735..b818ec4 100644
--- a/kuro/utils/kb_backlight.py
+++ b/kuro/utils/kb_backlight.py
@@ -55,7 +55,7 @@ def handle_focus_change(theme):
     window = qtile.currentWindow if qtile else None
 
     if window:
-        wm_class = window.get_wm_class() or None
+        wm_class = window.window.get_wm_class() or None
         name = window.name
 
         if wm_class:
diff --git a/kuro/utils/layouts.py b/kuro/utils/layouts.py
index 719b526..e74dee6 100644
--- a/kuro/utils/layouts.py
+++ b/kuro/utils/layouts.py
@@ -32,25 +32,29 @@ class KuroFloating(Floating):
 
         # 'sun-awt-X11-XWindowPeer' is a dropdown used in Java application,
         # don't reposition it anywhere, let Java app to control it
-        cls = client.get_wm_class() or ""
-        is_java_dropdown = "sun-awt-X11-XWindowPeer" in cls
+        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.bring_to_front()
+            client.cmd_bring_to_front()
 
         # 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.bring_to_front()
+            client.cmd_bring_to_front()
 
         else:
             above = False
 
             # We definitely have a screen here, so let's be sure we'll float on screen
-            if client.float_x is None or client.float_y is None:
+            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,
@@ -59,6 +63,5 @@ class KuroFloating(Floating):
                 bw,
                 bc,
                 above,
-                respect_hints=True,
             )
         client.unhide()
diff --git a/kuro/utils/widgets.py b/kuro/utils/widgets.py
index 3a30e23..442595c 100644
--- a/kuro/utils/widgets.py
+++ b/kuro/utils/widgets.py
@@ -7,17 +7,60 @@ import cairocffi
 import iwlib
 import netifaces
 import psutil
-from libqtile import bar, qtile
+import six
+import unicodedata
+from libqtile import bar, pangocffi
 from libqtile.log_utils import logger
+from libqtile.command.base import expose_command
 from libqtile.widget import base
 from libqtile.widget.base import ORIENTATION_HORIZONTAL
 from libqtile.widget.battery import default_icon_path, load_battery, BatteryState
+from libqtile.widget.check_updates import CheckUpdates
+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.widget.groupbox import GroupBox
-from libqtile.command.base import expose_command
+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, execute
+    call_process
+
+
+class CheckUpdatesYay(CheckUpdates):
+    def __init__(self, **config):
+        super(CheckUpdatesYay, self).__init__(**config)
+        # Override command and output with yay command
+        self.cmd = "yay -Qu".split()
+        self.status_cmd = "yay -Qu --color never".split()
+        self.update_cmd = "sudo yay".split()
+        self.subtr = 0
+
+    def _check_updates(self):
+        #subprocess.check_output(self.update_cmd)
+        res = super(CheckUpdatesYay, self)._check_updates()
+        return res
+
+    def button_press(self, x, y, button):
+        if button == BUTTON_LEFT:
+            output = subprocess.check_output(self.status_cmd).decode('utf-8').split('\n')
+
+            num_updates = len(output)-1
+            msg = "{} updates available.".format(num_updates)
+
+            if num_updates > 0:
+                msg += "\n\n"
+                for x in range(min(num_updates, 9)):
+                    msg += output[x] + "\n"
+                if num_updates > 9:
+                    msg += "and {} more...".format(num_updates-9)
+
+            notify(
+                "System updates",
+                msg
+            )
+
+        elif button == BUTTON_MIDDLE and self.execute is not None:
+            subprocess.Popen(self.execute, shell=True)
 
 
 class DualPaneTextboxBase(base._Widget):
@@ -224,6 +267,364 @@ class DualPaneTextboxBase(base._Widget):
         return d
 
 
+class MediaWidget(base.InLoopPollText):
+    """Media Status Widget"""
+
+    class Status:
+        OFFLINE = 0
+        PLAYING = 1
+        PAUSED = 2
+        STOPPED = 3
+
+    orientations = base.ORIENTATION_HORIZONTAL
+    defaults = [
+        ('off_text', '', 'The pattern for the text if no players are found.'),
+        ('on_text_play', ' {}', 'The pattern for the text if music is playing.'),
+        ('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.'),
+        ('ignore_players', '', 'Comma-separated list of players to ignore.')
+    ]
+
+    player_icons = {
+        'spotify': '',
+        'vlc': '',
+        'firefox': '',
+        'mpv': '',
+    }
+
+    custom_player_data = {
+        'firefox': {
+            'showing': False,
+            'title': '',
+            'state': Status.STOPPED,
+        }
+    }
+
+    image_urls = {}
+    current_image_url = None
+    player_to_control = None
+
+    def __init__(self, **config):
+        super(MediaWidget, self).__init__(**config)
+        self.add_defaults(MediaWidget.defaults)
+        self.surfaces = {}
+        self.player_to_control = None
+
+    def _player_to_control(self):
+            info = self._get_info()
+            players = {}
+            for player in info.keys():
+                if player not in self.custom_player_data.keys():
+                    if info[player][0] in [MediaWidget.Status.PLAYING, MediaWidget.Status.PAUSED]:
+                        players[player] = info[player]
+
+            if self.player_to_control is not None and self.player_to_control not in players.keys():
+                self.player_to_control = None
+
+            if self.player_to_control is not None:
+                players = {self.player_to_control: players[self.player_to_control]}
+
+            if len(players.keys()) == 1:
+                player = list(players.keys())[0]
+                self.player_to_control = player
+                return player
+
+            elif len(players) == 0:
+                notify("MediaWidget", "Nothing to control!")
+            else:
+                notify("MediaWidget", "Multiple players to control, I don't know what you want to do!")
+
+            return None
+
+    def button_press(self, x, y, button):
+        if button == BUTTON_LEFT:
+            player = self._player_to_control()
+            if player is not None:
+                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", "-i", self.ignore_players, "-p", player, "next"]
+                _ = self.call_process(command)
+        if button == BUTTON_MIDDLE:
+            # Jump to the screen that the player is on
+            # clients = list(self.bar.qtile.windows_map.values())
+            # logger.warning("{}")
+            pass
+
+    @expose_command()
+    def update_custom_player(self, player_name, data):
+        # Update firefox player
+        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
+                self.custom_player_data['firefox']['title'] = data['title']
+            elif data['playing'] and not data['muted']:
+                self.custom_player_data['firefox']['showing'] = True
+                self.custom_player_data['firefox']['state'] = MediaWidget.Status.PLAYING
+                self.custom_player_data['firefox']['title'] = data['title']
+            elif not data['playing'] and data['muted']:
+                self.custom_player_data['firefox']['showing'] = True
+                self.custom_player_data['firefox']['state'] = MediaWidget.Status.STOPPED
+                self.custom_player_data['firefox']['title'] = data['title']
+            elif not data['playing'] and not data['muted']:
+                self.custom_player_data['firefox']['showing'] = False
+                self.custom_player_data['firefox']['state'] = MediaWidget.Status.OFFLINE
+                self.custom_player_data['firefox']['title'] = data['title']
+
+    def _get_players(self):
+        players = []
+
+        # Playerctl players
+        try:
+            result = self.call_process(["playerctl", "-i", self.ignore_players, "-l"])
+        except subprocess.CalledProcessError:
+            result = None
+
+        if result:
+            players.extend([x for x in result.split("\n") if x])
+
+        # Custom players - Firefox
+        if self.custom_player_data['firefox']['showing']:
+            players.append('firefox')
+
+        if players:
+            return players
+        else:
+            return None
+
+    def _get_info(self):
+        players = self._get_players()
+
+        if not players:
+            return {}
+        else:
+            result = {}
+
+            for player in players:
+                if player in self.custom_player_data.keys():
+                    # Custom player -- Firefox
+                    if player == "firefox":
+                        result[player] = [self.custom_player_data['firefox']['state'], self.custom_player_data['firefox']['title']]
+
+                    # Other custom players -- generic attempt with error catching
+                    else:
+                        try:
+                            result[player] = [self.custom_player_data[player]['state'],
+                                              self.custom_player_data[player]['title']]
+                        except KeyError:
+                            pass
+
+                else:
+                    # PlayerCtl player
+                    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', "-i", self.ignore_players, '-p', player, 'metadata', 'artist']).strip()
+                        except subprocess.CalledProcessError:
+                            artist = None
+                        try:
+                            title = self.call_process(['playerctl', "-i", self.ignore_players, '-p', player, 'metadata', 'title']).strip()
+                        except subprocess.CalledProcessError:
+                            title = None
+
+                        if artist and title:
+                            text = "{} - {}".format(artist, title)
+                        elif artist:
+                            text = artist
+                        elif title:
+                            text = title
+
+                    if cmd_result == "Playing":
+                        result[player] = [MediaWidget.Status.PLAYING, text]
+                    elif cmd_result == "Paused":
+                        result[player] = [MediaWidget.Status.PAUSED, text]
+                    elif cmd_result == "Stopped":
+                        result[player] = [MediaWidget.Status.STOPPED, ""]
+
+        return result
+
+    def _get_formatted_text(self, status):
+        if status[0] == MediaWidget.Status.PLAYING:
+            res = self.on_text_play.format(status[1])
+        elif status[0] == MediaWidget.Status.PAUSED:
+            res = self.on_text_pause.format(status[1])
+        elif status[0] == MediaWidget.Status.STOPPED:
+            res = self.on_text_stop.format(status[1])
+        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
+
+    def draw(self):
+        super(MediaWidget, self).draw()
+
+    def poll(self):
+        text = []
+        status = self._get_info()
+        if not status:
+            return self.off_text
+        else:
+            for player in status.keys():
+                # 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
+
+
+class AudioVisualizerWidget(_Graph):
+    """Display Audio Visualization graph"""
+    orientations = base.ORIENTATION_HORIZONTAL
+    fixed_upper_bound = True
+    defaults = [
+        ("graph_color", "FFFFFF.0", "Graph color"),
+        ("fill_color", "FFFFFF.0", "Fill color for linefill graph"),
+        ("border_color", "FFFFFF.0", "Widget border color"),
+        ("border_width", 0, "Widget border width"),
+        ("line_width", 0, "Line width"),
+    ]
+
+    def __init__(self, **config):
+        _Graph.__init__(self, **config)
+        self.add_defaults(AudioVisualizerWidget.defaults)
+
+        self.client = None
+        self.screen = None
+
+        self.old_position = None
+
+    def set_client(self, c, s):
+        self.client = c
+        self.screen = s
+
+    def update_graph(self):
+        if self.client is not None:
+            viz_info = self.info()
+            pos_x = viz_info['offset'] + self.margin_x + self.screen.x
+            pos_y = 0 + self.margin_y + self.screen.y
+            if self.old_position != (pos_x, pos_y):
+                self.old_position = (pos_x, pos_y)
+
+                # Check if a window on this screen is full-screen
+                fullscreen = False
+                for window in self.screen.group.windows:
+                    if isinstance(window, Window):
+                        if window.fullscreen:
+                            fullscreen = True
+                            break
+
+                logger.debug("Repositioning {} {} to {}x{}".format(self.client, self.client.window.wid, pos_x, pos_y))
+                self.client.reposition(pos_x, pos_y, above=not fullscreen)
+
+        self.draw()
+
+    def draw(self):
+        self.drawer.clear(self.background or self.bar.background)
+        self.drawer.draw(offsetx=self.offset, width=self.width)
+
+
+class KuroCurrentLayoutIcon(CurrentLayoutIcon):
+    def _get_layout_names(self):
+        names = list(super(KuroCurrentLayoutIcon, self)._get_layout_names())
+
+        from kuro.utils import layouts as kuro_layouts
+        from libqtile.layout.base import Layout
+        klayouts = [
+            layout_class_name.lower()
+            for layout_class, layout_class_name
+            in map(lambda x: (getattr(kuro_layouts, x), x), dir(kuro_layouts))
+            if isinstance(layout_class, six.class_types) and issubclass(layout_class, Layout)
+        ]
+        names.extend(klayouts)
+
+        return set(names)
+
+
+class KuroTaskList(TaskList):
+    defaults = [
+        (
+            'txt_pinned',
+            'P ',
+            'Text representation of the pinned window state. '
+            'e.g., "P " or "\U0001F5D7 "'
+        ),
+        (
+            'markup_pinned',
+            None,
+            'Text markup of the pinned window state. Supports pangomarkup with markup=True.'
+            'e.g., "{}" or "{}"'
+        ),
+    ]
+
+    def __init__(self, *args, **kwargs):
+        super(KuroTaskList, self).__init__(*args, **kwargs)
+        self.add_defaults(KuroTaskList.defaults)
+
+    def get_taskname(self, window):
+        """
+        Get display name for given window.
+        Depending on its state minimized, maximized and floating
+        appropriate characters are prepended.
+        """
+        state = ''
+        markup_str = self.markup_normal
+
+        # Enforce markup and new string format behaviour when
+        # at least one markup_* option is used.
+        # Mixing non markup and markup may cause problems.
+        if self.markup_minimized or self.markup_maximized\
+                or self.markup_floating or self.markup_focused or self.markup_pinned:
+            enforce_markup = True
+        else:
+            enforce_markup = False
+
+        if window is None:
+            pass
+        elif hasattr(window, "is_static_window") and window.is_static_window:
+            state = self.txt_pinned
+            markup_str = self.markup_pinned
+        elif window.minimized:
+            state = self.txt_minimized
+            markup_str = self.markup_minimized
+        elif window.maximized:
+            state = self.txt_maximized
+            markup_str = self.markup_maximized
+        elif window.floating:
+            state = self.txt_floating
+            markup_str = self.markup_floating
+        elif window is window.group.current_window:
+            markup_str = self.markup_focused
+
+        window_name = window.name if window and window.name else "?"
+
+        # Emulate default widget behavior if markup_str is None
+        if enforce_markup and markup_str is None:
+            markup_str = "%s{}" % (state)
+
+        if markup_str is not None:
+            self.markup = True
+            window_name = pangocffi.markup_escape_text(window_name)
+            return markup_str.format(window_name)
+
+        return "%s%s" % (state, window_name)
+
+
 class GPUStatusWidget(base._TextBox):
     """Displays the currently used GPU."""
 
@@ -254,11 +655,7 @@ class GPUStatusWidget(base._TextBox):
         self.icons.update(self.custom_icons)
 
     def _get_info(self):
-        try:
-            output = self.call_process(self.check_command, shell=True)
-        except subprocess.CalledProcessError as e:
-            logger.error(f"Error while calling {self.check_command} - {e}")
-            output = None
+        output = self.call_process(self.check_command, shell=True)
         mode = "nvidia" if "nvidia" in output else "intel" if "intel" in output else "unknown"
         return {'error': False, 'mode': mode}
 
@@ -334,16 +731,14 @@ class GPUStatusWidget(base._TextBox):
         if button == BUTTON_LEFT:
             try:
                 next_gpu = self.call_process(self.next_command, shell=True).split(":")[1].strip()
-            except subprocess.CalledProcessError as e:
-                logger.error(f"Error while calling {self.next_command} - {e}")
             except IndexError:
                 next_gpu = "Unknown"
 
             if self.current_status == "Unknown":
-                notify(None, "GPU Status", "The currently used GPU is unknown.\n\nAfter the next login it will be the {} GPU.".format(next_gpu),
+                notify("GPU Status", "The currently used GPU is unknown.\n\nAfter the next login it will be the {} GPU.".format(next_gpu),
                        image=os.path.join(self.theme_path, "gpu-unknown.png"))
             else:
-                notify(None, "GPU Status", "The system is currently running on the {} GPU. Press the middle mouse "
+                notify("GPU Status", "The system is currently running on the {} GPU. Press the middle mouse "
                                      "button on this icon to switch GPUs.\n\nAfter the next login it will be the {} GPU.".format(
                                          self.current_status, next_gpu
                                      ),
@@ -352,25 +747,38 @@ class GPUStatusWidget(base._TextBox):
 
         if button == BUTTON_MIDDLE:
             command = ["optimus-manager", "--no-confirm", "--switch", "auto"]
-            try:
-                output = self.call_process(command)
-            except subprocess.CalledProcessError as e:
-                logger.error(f"Error while calling {command} - {e}")
-                output = ""
+            output = self.call_process(command)
             if "nvidia" in output:
-                notify(None, "GPU Switched", "The GPU has been switched from Intel to NVidia.\n"
+                notify("GPU Switched", "The GPU has been switched from Intel to NVidia.\n"
                                        "Please log out and log back in to apply the changes to the session.",
                        image=os.path.join(self.theme_path, "gpu-nvidia.png"))
             elif "intel" in output:
-                notify(None, "GPU Switched", "The GPU has been switched from NVidia to Intel.\n"
+                notify("GPU Switched", "The GPU has been switched from NVidia to Intel.\n"
                                        "Please log out and log back in to apply the changes to the session.",
                        image=os.path.join(self.theme_path, "gpu-intel.png"))
             else:
-                notify(None, "GPU Switch Error", "I could not determine if the GPU was switched successfully.\n"
+                notify("GPU Switch Error", "I could not determine if the GPU was switched successfully.\n"
                                            "Please log out and log back in to clear up the inconsistency.",
                        image=os.path.join(self.theme_path, "gpu-unknown.png"))
 
 
+class TextSpacerWidget(base._TextBox):
+    """Displays a text separator"""
+    orientations = base.ORIENTATION_HORIZONTAL
+    defaults = [
+        ('spacer', None, 'The character/text to use as separator. Default "|" if None.'),
+        ('color', "#ffffff", "Color of the text."),
+    ]
+
+    def __init__(self, **config):
+        super(TextSpacerWidget, self).__init__("Separator", bar.CALCULATED, **config)
+        self.add_defaults(TextSpacerWidget.defaults)
+        self.text = self.spacer or "|"
+
+    def draw(self):
+        base._TextBox.draw(self)
+
+
 class ThermalSensorWidget(DualPaneTextboxBase):
     defaults = [
         ('show_tag', False, 'Show tag sensor'),
@@ -408,11 +816,7 @@ class ThermalSensorWidget(DualPaneTextboxBase):
         self.timeout_add(self.update_interval, self.timer_setup)
 
     def _update_values(self):
-        try:
-            sensors_out = self.call_process(self.get_command())
-        except subprocess.CalledProcessError as e:
-            logger.error(f"Error while calling {self.get_command()} - {e}")
-            return
+        sensors_out = self.call_process(self.get_command())
         temperature_values = {}
         for name, temp, symbol in self.sensors_temp.findall(sensors_out):
             name = name.strip()
@@ -442,7 +846,7 @@ class ThermalSensorWidget(DualPaneTextboxBase):
 
     def button_press(self, x, y, button):
         if button == BUTTON_LEFT:
-            notify(None, "Temperature Information", "\n".join(
+            notify("Temperature Information", "\n".join(
                 "{}: {}{}".format(name, *values) for name, values in self.values.items()
             ))
 
@@ -495,7 +899,7 @@ class CPUInfoWidget(DualPaneTextboxBase):
     def button_press(self, x, y, button):
         if button == BUTTON_LEFT:
             total = sum([self.cpu_old[0], self.cpu_old[1], self.cpu_old[2], self.cpu_old[3]])
-            notify(None, "CPU Information", "user: {} %\nnice: {} %\nsys: {} %\nidle: {} %\ntotal: {} %".format(
+            notify("CPU Information", "user: {} %\nnice: {} %\nsys: {} %\nidle: {} %\ntotal: {} %".format(
                 math.ceil((self.cpu_old[0] / total) * 100),
                 math.ceil((self.cpu_old[1] / total) * 100),
                 math.ceil((self.cpu_old[2] / total) * 100),
@@ -548,7 +952,7 @@ class MemoryInfoWidget(DualPaneTextboxBase):
         val['SwapUsed'] = swap.used // 1024 // 1024
 
         if button == BUTTON_LEFT:
-            notify(None, "Memory Information", "Memory: {}MB / {}MB\n        {}%\nSwap: {}MB / {}MB\n      {}%".format(
+            notify("Memory Information", "Memory: {}MB / {}MB\n        {}%\nSwap: {}MB / {}MB\n      {}%".format(
                 val['MemUsed'], val['MemTotal'],
                 math.ceil((mem.used / mem.total) * 100),
                 val['SwapUsed'], val['SwapTotal'],
@@ -604,7 +1008,7 @@ class DiskIOInfoWidget(DualPaneTextboxBase):
 
     def button_press(self, x, y, button):
         if button == BUTTON_LEFT:
-            notify(None, "Disk IO Information",
+            notify("Disk IO Information",
                    "Time that there were IO requests queued for /dev/{}: {} ms".format(self.hdd_device, self.io))
 
 
@@ -621,7 +1025,6 @@ class NetworkInfoWidget(DualPaneTextboxBase):
         ('critical_color', "#ffffff", "Color when value is critical"),
         ('wireless_interface', "wifi0", "Wireless interface device name"),
         ('wired_interface', "enp7s0", "Wired interface device name"),
-        ('config_application', None, "Application to launch when right/middle clicking"),
     ]
 
     def __init__(self, **config):
@@ -650,70 +1053,57 @@ class NetworkInfoWidget(DualPaneTextboxBase):
 
     def _update_values(self):
         # Wifi
-        if self.wireless_interface:
-            try:
-                essid, quality = get_status(self.wireless_interface)
-                status = iwlib.get_iwconfig(self.wireless_interface)
-                self.wireless_ips = netifaces.ifaddresses(self.wireless_interface)
-                disconnected = essid is None
-                percent = math.ceil(((quality or 0) / 70) * 100)
-                self.wireless_quality = quality
-                self.wireless_signal = percent
-                self.wireless_name = essid
-                self.wireless_connected = not disconnected
-                self.wireless_accesspoint = status.get('Access Point', b'Unknown').decode()
-                self.wireless_frequency = status.get('Frequency', b'Unknown').decode()
-                self.wireless_ipv4 = self.wireless_ips.get(netifaces.AF_INET, [{'addr': ""}])[0]['addr']
-                self.wireless_ipv6 = self.wireless_ips.get(netifaces.AF_INET6, [{'addr': ""}])[0]['addr']
-                self.wireless_mac = self.wireless_ips.get(netifaces.AF_LINK, [{'addr': ""}])[0]['addr']
-            except EnvironmentError:
-                pass
+        try:
+            essid, quality = get_status(self.wireless_interface)
+            status = iwlib.get_iwconfig(self.wireless_interface)
+            self.wireless_ips = netifaces.ifaddresses(self.wireless_interface)
+            disconnected = essid is None
+            percent = math.ceil((quality / 70) * 100)
+            self.wireless_quality = quality
+            self.wireless_signal = percent
+            self.wireless_name = essid
+            self.wireless_connected = not disconnected
+            self.wireless_accesspoint = status.get('Access Point', b'Unknown').decode()
+            self.wireless_frequency = status.get('Frequency', b'Unknown').decode()
+            self.wireless_ipv4 = self.wireless_ips.get(netifaces.AF_INET, [{'addr': ""}])[0]['addr']
+            self.wireless_ipv6 = self.wireless_ips.get(netifaces.AF_INET6, [{'addr': ""}])[0]['addr']
+            self.wireless_mac = self.wireless_ips.get(netifaces.AF_LINK, [{'addr': ""}])[0]['addr']
+        except EnvironmentError:
+            pass
 
         # Wired
-        if self.wired_interface:
-            try:
-                self.wired_ips = netifaces.ifaddresses(self.wired_interface)
-                self.wired_ipv4 = self.wired_ips.get(netifaces.AF_INET, [{'addr': ""}])[0]['addr']
-                self.wired_ipv6 = self.wired_ips.get(netifaces.AF_INET6, [{'addr': ""}])[0]['addr']
-                self.wired_mac = self.wired_ips.get(netifaces.AF_LINK, [{'addr': ""}])[0]['addr']
-                command = ["ip", "link", "show", "{}".format(self.wired_interface)]
-                try:
-                    eth_status = call_process(command)
-                except subprocess.CalledProcessError as e:
-                    logger.error(f"Error while calling {command} - {e}")
-                    return
-                m = self.wired_up_regex.search(eth_status)
-                if m:
-                     self.wired_connected = "UP" in m.group(1)
-                else:
-                    self.wired_connected = False
+        try:
+            self.wired_ips = netifaces.ifaddresses(self.wired_interface)
+            self.wired_ipv4 = self.wired_ips.get(netifaces.AF_INET, [{'addr': ""}])[0]['addr']
+            self.wired_ipv6 = self.wired_ips.get(netifaces.AF_INET6, [{'addr': ""}])[0]['addr']
+            self.wired_mac = self.wired_ips.get(netifaces.AF_LINK, [{'addr': ""}])[0]['addr']
+            eth_status = call_process(["ip", "link", "show", "{}".format(self.wired_interface)])
+            m = self.wired_up_regex.search(eth_status)
+            if m:
+                 self.wired_connected = "UP" in m.group(1)
+            else:
+                self.wired_connected = False
 
-            except (EnvironmentError, ValueError):
-                pass
+        except (EnvironmentError, ValueError):
+            pass
 
     def update(self):
         self._update_values()
         self.draw()
 
     def draw(self):
-        if self.wireless_interface:
-            if self.wireless_connected:
-                strength = ""
-                if self.wireless_signal < 66:
-                    strength = ""
-                if self.wireless_signal < 33:
-                    strength = ""
-                self.text_left = strength
-            else:
-                self.text_left = ""
+        if self.wireless_connected:
+            strength = ""
+            if self.wireless_signal < 66:
+                strength = ""
+            if self.wireless_signal < 33:
+                strength = ""
+            self.text_left = strength
         else:
-            self.text_left = ""
+            self.text_left = ""
 
-        if self.wired_interface:
-            if self.wired_connected:
-                self.text_right = ""
-            else:
-                self.text_right = ""
+        if self.wired_connected:
+            self.text_right = ""
         else:
             self.text_right = ""
 
@@ -745,12 +1135,9 @@ class NetworkInfoWidget(DualPaneTextboxBase):
                 wired_text = "Wired: Not connected"
 
             if wifi_text:
-                notify(None, title, "{}\n\n{}".format(wifi_text, wired_text))
+                notify(title, "{}\n\n{}".format(wifi_text, wired_text))
             else:
-                notify(None, title, "\n{}".format(wired_text))
-        if button == BUTTON_RIGHT or button == BUTTON_MIDDLE:
-            if self.config_application:
-                execute(self.config_application)
+                notify(title, "\n{}".format(wired_text))
 
 
 class BatteryInfoWidget(DualPaneTextboxBase):
@@ -826,7 +1213,7 @@ class BatteryInfoWidget(DualPaneTextboxBase):
     def button_press(self, x, y, button):
         if button == BUTTON_LEFT:
             output = subprocess.check_output(self.status_cmd).decode('utf-8')
-            notify(None, "Battery Status", output)
+            notify("Battery Status", output)
 
 
 class VolumeInfoWidget(DualPaneTextboxBase):
@@ -861,8 +1248,7 @@ class VolumeInfoWidget(DualPaneTextboxBase):
             else:
                 cmd = self.status_cmd
             mixer_out = self.call_process(cmd.split(" "))
-        except subprocess.CalledProcessError as e:
-            logger.error(f"Error while calling {cmd} - {e}")
+        except subprocess.CalledProcessError:
             return -1
         try:
             return int(mixer_out)
@@ -906,7 +1292,7 @@ class VolumeInfoWidget(DualPaneTextboxBase):
             output = subprocess.check_output(cmd.split(" ")).decode('utf-8')
 
             sink = "Sink {}\n".format(self.pulse_sink) if self.pulse_sink else ""
-            notify(None, "Volume Status", sink+output)
+            notify("Volume Status", sink+output)
 
         elif button == BUTTON_RIGHT:
             if "{sink}" in self.volume_app:
@@ -957,16 +1343,3 @@ class VolumeInfoWidget(DualPaneTextboxBase):
     def run_app(self):
         # Emulate button press.
         self.button_press(0, 0, BUTTON_RIGHT)
-
-
-class KuroGroupBox(GroupBox):
-    @property
-    def length(self):
-        try:
-            return super(KuroGroupBox, self).length
-        except AttributeError:
-            return 1
-
-    @length.setter
-    def length(self, length):
-        logger.warning(f"Setting groupbox length to {length}")
diff --git a/kuro/utils/windows.py b/kuro/utils/windows.py
index 7370274..6bfd997 100644
--- a/kuro/utils/windows.py
+++ b/kuro/utils/windows.py
@@ -1,11 +1,6 @@
 from cairocffi.test_xcb import xcffib
-from libqtile import hook, qtile
-
-if qtile.core.name == "x11":
-    from libqtile.backend.x11.window import Window, Static
-else:
-    from libqtile.backend.wayland.window import Window, Static
-
+from libqtile import hook
+from libqtile.backend.x11.window import Window, Static
 
 class KuroStatic(Static):
 
diff --git a/required_packages.txt b/required_packages.txt
index 3462d93..ff3bd88 100644
--- a/required_packages.txt
+++ b/required_packages.txt
@@ -4,34 +4,9 @@ notification-daemon
 otf-font-awesome
 python-osc
 
-qtile-extras
-
 # /optional/
 playerctl
 
 xfce4-screenshooter
 xfce4-clipman-plugin
 wireless_tools
-
-# Utilities
-kdeconnect      # KDE Connect
-vorta           # Backup scheduler
-
-# Xorg-only
-picom           # Compositor
-xfce4-clipman   # Clipboard manager
-dunst           # Notification daemon
-xiccd           # Color profile manager
-
-# Wayland-only
-xorg-xwayland
-python-pywlroots
-wofi                # dmenu replacement
-grim                # Screenshot utility
-swappy              # Screenshot editor
-slurp               # Region selector
-cliphist            # Clipboard history
-mako                # Notifications daemon
-kanshi              # Display hotplugging
-wallutils           # Display/wallpaper utilities (lsmon)
-papirus-icon-theme  # Icon theme