import json
import os
import random
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...")
from libqtile.config import Key, Screen, Group, Drag, Click, Match
from libqtile.command import lazy
from libqtile import layout, bar, widget, qtile
from qtile_extras import widget as extra_widget
logger.warning("Importing theme util functions...")
# Import theme util functions
from xcffib.xproto import WindowError
logger.warning("Importing kuro utils...")
import kuro.utils.widgets
from kuro.utils import general as utils
logger.warning("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 import layouts as kuro_layouts
logger.warning("Importing configuration...")
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.warning("Imports done")
class Kuro(BaseTheme):
# Shorthand for modifier key
mod = Config.get("modifier", "mod4")
# Screen count
num_screens = 0
# Top bars
topbars = []
# Static windows
static_windows = []
# Current wallpaper path
current_wallpaper = None
# 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
Match(title='origin.exe', wm_class='Wine'), # Wine Origin game launcher
]
)
def initialize(self):
# Update color scheme
logger.warning("Initializing colorscheme...")
self.initialize_colorscheme()
logger.warning("Initializing superclass...")
super(Kuro, self).initialize()
logger.warning("Updating keys for groups and layouts...")
self.update_keys()
def init_keys(self):
logger.warning("Initializing keys")
return [
# Switch between windows in current stack pane
Key([self.mod], "k", lazy.layout.down()),
Key([self.mod], "j", lazy.layout.up()),
# Move windows up or down in current stack
Key([self.mod, "control"], "k", lazy.layout.shuffle_down()),
Key([self.mod, "control"], "j", lazy.layout.shuffle_up()),
# Switch window focus to other pane(s) of stack
Key([self.mod], "space", lazy.layout.next()),
# Swap panes of split stack
Key([self.mod, "shift"], "space", lazy.layout.rotate()),
# Fullscreen toggle
Key([self.mod], 'f', lazy.window.toggle_fullscreen()),
# Floating toggle
Key([self.mod, "shift"], 'f', lazy.window.toggle_floating()),
# Toggle between split and unsplit sides of stack.
# Split = all windows displayed
# Unsplit = 1 window displayed, like Max layout, but still with
# multiple stack panes
Key([self.mod, "shift"], "Return", lazy.layout.toggle_split()),
# Super-Enter to start terminal
Key([self.mod], "Return", lazy.spawn(Config.get('app_terminal', "xterm"))),
# Super-R to start dmenu_run
Key([self.mod], "r", lazy.spawn(Config.get('app_launcher', "dmenu_run"))),
# 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-Shift-R to start spawncmd
Key([self.mod, "shift"], "r", lazy.spawncmd()),
# Lock shortcut
Key([self.mod], "l", lazy.spawn(Config.get('lock_command', "i3lock"))),
# 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'))),
# 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'))),
# Media keys
Key([], "XF86AudioPlay", lazy.spawn(Config.get('cmd_media_play', 'true'))),
Key([], "XF86AudioNext", lazy.spawn(Config.get('cmd_media_next', 'true'))),
Key([], "XF86AudioMute", lazy.spawn(Config.get('cmd_media_mute', 'true'))),
Key([], "XF86AudioRaiseVolume", lazy.spawn(Config.get('cmd_media_volume_up', 'true'))),
Key([], "XF86AudioLowerVolume", lazy.spawn(Config.get('cmd_media_volume_down', 'true'))),
# Sleep key
Key([], "XF86Sleep", lazy.spawn(Config.get('cmd_sleep', 'true'))),
# Screenshot key
Key([], "Print", lazy.spawn(Config.get('cmd_screenshot', 'xfce4-screenshooter'))),
# Alt screenshot
Key([self.mod], "Print", lazy.spawn(Config.get('cmd_alt_screenshot', 'xfce4-screenshooter'))),
# 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()),
# 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.function(Config.get('cmd_reconfigure_screens', 'true'))),
# Reload colorscheme
Key([self.mod, "control"], "t", lazy.function(self.update_colorscheme)),
# Toggle static windows
Key([self.mod], "p", lazy.function(self.toggle_window_static)),
##
# Debug keyboard shortcuts
##
Key([self.mod, "control"], "c", lazy.function(display_wm_class)),
# Show extensive window info
Key([self.mod, "shift", "control"], "i", lazy.function(self.show_window_info)),
# Spawn a popup, and despawn it after 3 seconds
Key([self.mod, "control"], "p", lazy.function(test_popups)),
]
def init_groups(self):
logger.warning("Initializing groups")
# http://fontawesome.io/cheatsheet
groups = [
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group("", layout='floating', layouts=[
layout.Floating(
border_focus="#990000",
border_normal="#440000"
)
])
]
return groups
def init_layouts(self):
logger.warning("Initializing layouts")
return [
kuro_layouts.KuroWmii(
theme=self,
border_focus=Config.get('colour_border_focus', "#ffffff"),
border_focus_stack=Config.get('colour_border_normal', "#777777"),
border_normal=Config.get('colour_border_normal', "#777777"),
border_normal_stack=Config.get('colour_border_normal', "#777777"),
border_width=Config.get('width_border', "1"),
grow_amount=Config.get('grow_amount', "5"),
margin=Config.get('margin_layout', "0"),
),
layout.Max(),
layout.Zoomy(
columnwidth=Config.get('width_zoomy_column', 150),
margin=Config.get('margin_layout', "0"),
)
]
def init_widget_defaults(self):
logger.warning("Initializing widget_defaults")
return {
"font": Config.get('font_topbar', "Sans"),
"fontsize": Config.get('fontsize_topbar', 16),
"padding": 3,
}
def init_screens(self):
logger.warning("Initializing screens")
self.reinit_screens()
return self.screens
def init_mouse(self):
logger.warning("Initializing mouse")
# Drag floating layouts.
mouse = [
Drag([self.mod], "Button1", lazy.window.set_position_floating(),
start=lazy.window.get_position()),
Drag([self.mod], "Button3", lazy.window.set_size_floating(),
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("/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']
def reinit_screens(self):
# Re-initalize bars
self.topbars.clear()
if qtile.core.name == "x11":
self.num_screens = max(1, utils.get_screen_count())
else:
self.num_screens = max(1, len(qtile.core.get_screen_info()))
logger.warning(f"Detected {self.num_screens} screens.")
screens = []
for x in range(self.num_screens):
logger.warning("Initializing bars for screen {}".format(x))
topbar = self.build_bar_for_screen(x)
self.topbars.append(topbar)
screens.append(Screen(top=topbar))
self.screens.clear()
for s in screens:
self.screens.append(s)
def update_keys(self):
logger.warning("Updating keys")
for i, g in enumerate(self.groups):
if i == 9:
i = -1
elif i > 9:
continue
# mod1 + number = switch to group
self.keys.append(
Key([self.mod], str(i + 1), lazy.group[g.name].toscreen())
)
# mod1 + shift + number = switch to & move focused window to group
self.keys.append(
Key([self.mod, "shift"], str(i + 1), lazy.window.togroup(g.name))
)
# Keys for the Wmii layout
self.keys.extend([
Key([self.mod, "shift"], "j", lazy.layout.shuffle_down()),
Key([self.mod, "shift"], "k", lazy.layout.shuffle_up()),
Key([self.mod, "shift"], "h", lazy.layout.shuffle_left()),
Key([self.mod, "shift"], "l", lazy.layout.shuffle_right()),
Key([self.mod, "shift", "control"], "j", lazy.layout.grow_down()),
Key([self.mod, "shift", "control"], "k", lazy.layout.grow_up()),
Key([self.mod, "shift", "control"], "h", lazy.layout.grow_left()),
Key([self.mod, "shift", "control"], "l", lazy.layout.grow_right()),
Key([self.mod], "s", lazy.layout.toggle_split()),
Key([self.mod], "n", lazy.layout.normalize()),
])
def build_bar_for_screen(self, screen_num):
widgets = [
# Workspaces
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)
),
# Spawn prompt (only shown if activated)
widget.Prompt(**self.widget_defaults),
# 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=" ",
)
]
# Media widget(s)
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,
}
)
])
# Sensor widgets
sensor_widgets = []
# 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
))
# 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),
])
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="|")
])
# Battery level
if Config.get('show_battery_widget', False):
widgets.extend([
kuro.utils.widgets.BatteryInfoWidget(fontsize_left=16, fontsize_right=11),
])
# Volume widget(s)
widgets.append(
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),
)
)
# Violet has multiple volume widgets
if socket.gethostname() in ["Violet"]:
widgets.append(
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),
)
)
# 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)
),
])
# 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.CurrentLayoutIcon(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)
)
# 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()
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(["wal", "-n", "-i", "{}".format(self.current_wallpaper)])
p.wait()
else:
wallpaper = None
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)
else:
logger.warning("No wallpaper to restore.")
# Update color scheme
self.update_colorscheme()
self.startup_completed = True
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()
# After first startup is complete, autostart configured apps
logger.warning("Autostarting apps...")
for app in Config.get("apps_autostart", []):
logger.warning(f"Starting '{app}'...")
utils.execute_once(app)
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")
def callback_client_managed(self, *args, **kwargs):
client: Optional[Window] = args[0] if len(args) > 0 else None
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_screens_reconfigured(self, *args, **kwargs):
logger.warning(f"Re-configuring screens!")
self.reinit_screens()
self.set_wallpaper(self.current_wallpaper)
self.update_colorscheme()
def callback_setgroup(self, *args, **kwargs):
for window in self.static_windows:
# Only move if the window is not currently on any screen.
if window.group.screen is None:
try:
window.togroup()
except WindowError as e:
logger.warning("Could not move static window {}, removing from list: {}".format(window.name, e))
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 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
window.floating = True
def set_random_wallpaper(self, *args, **kwargs):
wallpapers = []
wallpaper_dir = Config.get("desktop_bg_folder", "")
try:
wallpapers = [x for x in os.listdir(wallpaper_dir) if ".vertical." not in x]
except os.error as e:
logger.warning("Could not load wallpapers from directory: {}".format(e))
if wallpapers:
if Config.get("desktop_bg_override", False):
wallpaper_file = Config.get("desktop_bg_override", "")
else:
wallpaper_file = os.path.join(wallpaper_dir, random.choice(wallpapers))
self.set_wallpaper(wallpaper_file)
else:
logger.warning("Random wallpaper requested but no wallpapers are available.")
def set_wallpaper(self, filename):
if qtile.core.name == "x11":
p = utils.execute_once(f"{Config.get('wallpaper_config_command', 'wal-nitrogen-noupdate')} {filename}")
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(["wal", "-n", "-i", "{}".format(self.current_wallpaper)])
p.wait()
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']
# Update border colors in layouts
for group in qtile.groups:
for layout in group.layouts:
if isinstance(layout, kuro_layouts.KuroWmii):
layout.border_focus = colors['color15']
layout.border_focus_stack = colors['color1']
layout.border_normal = colors['color1']
layout.border_normal_stack = colors['color1']
for screen_i, screen in enumerate(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)
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_normal'):
w.foreground_normal = colors['color15']
if hasattr(w, 'foreground_alert'):
w.foreground_alert = colors['color3']
if hasattr(w, 'border'):
w.border = colors['color15']
if hasattr(w, 'active'):
w.active = colors['color15']
if hasattr(w, 'highlight_color'):
w.highlight_color = colors['color3']
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_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_screen_border'):
w.other_screen_border = colors['color8']
if isinstance(w, widget.WidgetBox):
for subw in w.widgets:
update_widget(subw)
for w in bar.widgets:
update_widget(w)
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"])
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']}")