diff --git a/config.py b/config.py index e4e63a5..c530902 100644 --- a/config.py +++ b/config.py @@ -91,7 +91,11 @@ try: groups = Theme.groups layouts = Theme.layouts widget_defaults = Theme.widget_defaults - screens = Theme.screens + if Config.get("use_fake_screens", False): + logger.warning(f"This host uses fake screens!") + fake_screens = Theme.screens + else: + screens = Theme.screens dgroups_key_binder = Theme.dgroups_key_binder dgroups_app_rules = Theme.dgroups_app_rules main = Theme.main diff --git a/kuro/config/__init__.py b/kuro/config/__init__.py index ffdf584..85c1bff 100644 --- a/kuro/config/__init__.py +++ b/kuro/config/__init__.py @@ -1,5 +1,6 @@ from kuro.base import BaseConfig from libqtile.log_utils import logger +from libqtile.lazy import lazy # Config variables used in the main configuration @@ -10,6 +11,13 @@ class Config(BaseConfig): 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" @@ -18,8 +26,6 @@ class Config(BaseConfig): inactive_dark = "#333333" # Predefined commands - cmd_brightness_up = "sudo /usr/bin/xbacklight -inc 10" - cmd_brightness_down = "sudo /usr/bin/xbacklight -dec 10" cmd_screenshot = "/home/kevin/bin/screenshot.sh" cmd_alt_screenshot = "/home/kevin/bin/screenshot.sh" lock_command = "bash /home/kevin/bin/lock.sh" @@ -28,11 +34,52 @@ class Config(BaseConfig): # 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" 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"]}, @@ -69,12 +116,8 @@ class Config(BaseConfig): cmd_media_volume_up = "pamixer -d 2" # Display mode commands - cmd_monitor_mode_3s144 = "bash /home/kevin/.screenlayout/3scrns_144_rrot.sh" - cmd_monitor_mode_3s60 = "bash /home/kevin/.screenlayout/3scrns_60_rrot.sh" - cmd_monitor_mode_day = "bash /home/kevin/bin/monitor_day.sh" - cmd_monitor_mode_night = "bash /home/kevin/bin/monitor_night.sh" - cmd_monitor_mode_alt = "bash /home/kevin/bin/monitor_gamenight.sh" cmd_reconfigure_screens = "kanshictl reload" + cmd_wal = ["wallust", "run"] # Commands x11_wallpaper_config_command = "/home/kevin/bin/wal-nitrogen-noupdate" # TODO: Remove @@ -208,6 +251,9 @@ class Config(BaseConfig): # Show battery widget show_battery_widget = False + # Show media widget + show_media_widget = True + # Comma-separated list of ignored players in the media widget media_ignore_players = "kdeconnect" @@ -219,4 +265,4 @@ class Config(BaseConfig): # 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 = "/home/kevin/bin/dmenu_wal.sh" + cls.app_launcher = cls.app_launcer_x11 diff --git a/kuro/config/ppc1006083.py b/kuro/config/ppc1006083.py new file mode 100644 index 0000000..ceb9eae --- /dev/null +++ b/kuro/config/ppc1006083.py @@ -0,0 +1,133 @@ +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': 840, 'width': 1920, 'height': qtile_height-840}, + {'x': 1920, 'y': 0, 'width': 1080, 'height': qtile_height}, + ] + # Dual 1680x1050 + elif qtile_width == 3360 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 == 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 == 1080: + cls.screen_kwargs = [{'x': 0, 'y': 0, 'width': 1920, 'height': 1080}] + # Single 1680x1050 + elif qtile_width == 1680 and qtile_height == 1050: + cls.screen_kwargs = [{'x': 0, 'y': 0, 'width': 1680, 'height': 1050}] + # Else, set to autoconfigure + else: + cls.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"))}, + + logger.warning(f"Kwargs: {cls.screen_kwargs}") + diff --git a/kuro/theme.py b/kuro/theme.py index d643aa9..3f554a6 100644 --- a/kuro/theme.py +++ b/kuro/theme.py @@ -82,14 +82,8 @@ class Kuro(BaseTheme): Match(wm_class='ssh-askpass'), # ssh-askpass Match(title='branchdialog'), # gitk Match(title='pinentry'), # GPG key password entry - Match(title='origin.exe', wm_class='Wine'), # Wine Origin game launcher - - # Homebank popups - Match(title='Add transaction', wm_class='homebank'), - Match(title='Edit transaction', wm_class='homebank'), - Match(title='Inherit transaction', wm_class='homebank'), - Match(title='Multiple edit transactions', wm_class='homebank'), - Match(title='Transaction splits', wm_class='homebank'), + # Extra rules from host-specific Config + *[Match(**rule) for rule in Config.get('extra_float_rules', [])] ] ) @@ -106,7 +100,7 @@ class Kuro(BaseTheme): def init_keys(self): logger.warning("Initializing keys") - return [ + keys = [ # Switch between windows in current stack pane Key([self.mod], "k", lazy.layout.down()), Key([self.mod], "j", lazy.layout.up()), @@ -151,17 +145,6 @@ class Kuro(BaseTheme): # 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'))), @@ -218,27 +201,32 @@ 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 - groups = [ - Group(""), - Group(""), - Group(""), - Group(""), - Group(""), - Group(""), - Group(""), - Group(""), - Group(""), - Group("", layout='floating', layouts=[ - layout.Floating( - border_focus="#990000", - border_normal="#440000" - ) - ]) - ] + 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'])) return groups def init_layouts(self): @@ -288,8 +276,8 @@ class Kuro(BaseTheme): 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: + 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: @@ -305,26 +293,34 @@ class Kuro(BaseTheme): Config.bar_background = colors['color1'] def reinit_screens(self): - # TODO: Move backend check into utils method - if qtile.core.name == "x11": - self.num_screens = max(1, utils.get_screen_count()) + 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, len(qtile.core.get_screen_info())) + 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("Reconfiguring bars for screen {}".format(x)) + logger.warning(f"Reconfiguring bars for screen {x}") try: screen = self.screens[x] except IndexError: - screen = Screen() + 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(top=topbar)) + self.screens.append(screen) def update_keys(self): logger.warning("Updating keys") @@ -399,60 +395,61 @@ class Kuro(BaseTheme): ] # 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, - } - ) - ]) + 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, + } + ) + ]) # Sensor widgets sensor_widgets = [] @@ -583,12 +580,13 @@ class Kuro(BaseTheme): logger.warning("Restoring wallpaper...") if self.current_wallpaper: - p = utils.execute_once(["wallust", "run", "{}".format(self.current_wallpaper)]) - p.wait() + p = utils.execute_once([*Config.get('cmd_wal', ['wallust', 'run']), "{}".format(self.current_wallpaper)]) + if p: + 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: + 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: wallpaper = json.load(f)['wallpaper'] except KeyError: @@ -741,7 +739,8 @@ class Kuro(BaseTheme): 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}") - p.wait() + if p: + p.wait() else: # Wayland can set wallpaper in qtile directly per screen for screen_i, screen in enumerate(qtile.screens): @@ -760,12 +759,13 @@ class Kuro(BaseTheme): 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(["wallust", "run", "{}".format(self.current_wallpaper)]) - p.wait() + p = utils.execute_once([*Config.get('cmd_wal', ['wallust', 'run']), "{}".format(self.current_wallpaper)]) + if p: + 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: + 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: @@ -851,7 +851,8 @@ class Kuro(BaseTheme): try: logger.warning(f"Calling 'pywalfox update'...") p = utils.execute(["pywalfox", "update"]) - p.wait() + if p: + p.wait() except subprocess.SubprocessError as e: logger.error(f"Error running 'pywalfox update': {e}") diff --git a/kuro/utils/general.py b/kuro/utils/general.py index 0fb66e9..35944f2 100644 --- a/kuro/utils/general.py +++ b/kuro/utils/general.py @@ -65,7 +65,7 @@ def start_in_group(theme, qtile, group: str, command: List[str], floating: bool theme.autostart_app_rules[proc.pid] = rule_id return proc except FileNotFoundError as e: - logger.error(f"Could not execute {process}, FileNotFoundError - {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, @@ -90,17 +90,12 @@ def get_screen_count(): 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: - logger.info("Using lsmon (wallutils) to detect screen count") - output = subprocess.check_output(["lsmon"]).decode('utf-8') - output = output.split("\n") + return max(1, len(qtile.core.get_screen_info())) except subprocess.CalledProcessError: - return 1 - - if output: - return len(output) - else: - return 1 + pass + return 1 def bar_separator(config):