From 99d4919539c663d9a23753b006d0091dcf21b58c Mon Sep 17 00:00:00 2001 From: Kevin Alberts Date: Mon, 27 May 2019 23:43:25 +0200 Subject: [PATCH] Add new widget and only show thermal widget if setting is set --- config.py | 102 +++++++++++++++++--------------- kuro/config.py | 6 +- kuro/theme.py | 70 ++++++++++++++++------ kuro/utils/widgets.py | 131 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 63 deletions(-) diff --git a/config.py b/config.py index d502141..1b3fac7 100644 --- a/config.py +++ b/config.py @@ -46,57 +46,69 @@ try: except ImportError as e: logger.error("Could not load Kuro Config. Trying to load BaseConfig. Error: {}".format(e)) try: - from kuro.baseconfig import BaseConfig as Config + 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)) -# Initialize the Theme -Theme.initialize() -# Hook theme into all hooks we know of -hook.subscribe.startup_once(Theme.callback_startup_once) -hook.subscribe.startup(Theme.callback_startup) -hook.subscribe.startup_complete(Theme.callback_startup_complete) -hook.subscribe.setgroup(Theme.callback_setgroup) -hook.subscribe.addgroup(Theme.callback_addgroup) -hook.subscribe.delgroup(Theme.callback_delgroup) -hook.subscribe.changegroup(Theme.callback_changegroup) -hook.subscribe.focus_change(Theme.callback_focus_change) -hook.subscribe.float_change(Theme.callback_float_change) -hook.subscribe.group_window_add(Theme.callback_group_window_add) -hook.subscribe.client_new(Theme.callback_client_new) -hook.subscribe.client_managed(Theme.callback_client_managed) -hook.subscribe.client_killed(Theme.callback_client_killed) -hook.subscribe.client_state_changed(Theme.callback_client_state_changed) -hook.subscribe.client_type_changed(Theme.callback_client_type_changed) -hook.subscribe.client_focus(Theme.callback_client_focus) -hook.subscribe.client_mouse_enter(Theme.callback_client_mouse_enter) -hook.subscribe.client_name_updated(Theme.callback_client_name_updated) -hook.subscribe.client_urgent_hint_changed(Theme.callback_client_urgent_hint_changed) -hook.subscribe.layout_change(Theme.callback_layout_change) -hook.subscribe.net_wm_icon_change(Theme.callback_net_wm_icon_change) -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.current_screen_change(Theme.callback_current_screen_change) +try: + logger.error("Initializing theme...") + # Initialize the Theme + Theme.initialize() + logger.error("Initialize done") -# Initialize variables from theme -keys = Theme.keys -groups = Theme.groups -layouts = Theme.layouts -widget_defaults = Theme.widget_defaults -screens = Theme.screens -dgroups_key_binder = Theme.dgroups_key_binder -dgroups_app_rules = Theme.dgroups_app_rules -main = Theme.main -follow_mouse_focus = Theme.follow_mouse_focus -bring_front_click = Theme.bring_front_click -cursor_warp = Theme.cursor_warp -floating_layout = Theme.floating_layout -auto_fullscreen = Theme.auto_fullscreen -focus_on_window_activation = Theme.focus_on_window_activation -extentions = Theme.extensions + logger.error("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) + hook.subscribe.startup_complete(Theme.callback_startup_complete) + hook.subscribe.setgroup(Theme.callback_setgroup) + hook.subscribe.addgroup(Theme.callback_addgroup) + hook.subscribe.delgroup(Theme.callback_delgroup) + hook.subscribe.changegroup(Theme.callback_changegroup) + hook.subscribe.focus_change(Theme.callback_focus_change) + hook.subscribe.float_change(Theme.callback_float_change) + hook.subscribe.group_window_add(Theme.callback_group_window_add) + hook.subscribe.client_new(Theme.callback_client_new) + hook.subscribe.client_managed(Theme.callback_client_managed) + hook.subscribe.client_killed(Theme.callback_client_killed) + hook.subscribe.client_state_changed(Theme.callback_client_state_changed) + hook.subscribe.client_type_changed(Theme.callback_client_type_changed) + hook.subscribe.client_focus(Theme.callback_client_focus) + hook.subscribe.client_mouse_enter(Theme.callback_client_mouse_enter) + hook.subscribe.client_name_updated(Theme.callback_client_name_updated) + hook.subscribe.client_urgent_hint_changed(Theme.callback_client_urgent_hint_changed) + hook.subscribe.layout_change(Theme.callback_layout_change) + hook.subscribe.net_wm_icon_change(Theme.callback_net_wm_icon_change) + 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.current_screen_change(Theme.callback_current_screen_change) + logger.error("Hooking done") + + logger.error("Initializing theme variables") + # Initialize variables from theme + keys = Theme.keys + groups = Theme.groups + layouts = Theme.layouts + widget_defaults = Theme.widget_defaults + screens = Theme.screens + dgroups_key_binder = Theme.dgroups_key_binder + dgroups_app_rules = Theme.dgroups_app_rules + main = Theme.main + follow_mouse_focus = Theme.follow_mouse_focus + bring_front_click = Theme.bring_front_click + cursor_warp = Theme.cursor_warp + floating_layout = Theme.floating_layout + auto_fullscreen = Theme.auto_fullscreen + focus_on_window_activation = Theme.focus_on_window_activation + extentions = Theme.extensions + logger.error("Variable initialization done") +except Exception as e: + Theme = None + Config = None + raise AttributeError("Could not configure theme! Error: {}".format(e)) def main(qtile): diff --git a/kuro/config.py b/kuro/config.py index 8410049..467a055 100644 --- a/kuro/config.py +++ b/kuro/config.py @@ -105,6 +105,9 @@ class Config(BaseConfig): wifi_theme_path = "/home/kevin/.config/qtile/kuro/resources/wifi" wifi_update_interval = 5 + # GPU variables + gpu_theme_path = "/home/kevin/.config/qtile/kuro/resources/gpu" + # Normal volume icon variables volume_font = "Noto Sans" volume_fontsize = 11 @@ -133,7 +136,8 @@ class Config(BaseConfig): updates_colour_available = '#f4d742' # Screen organization - laptop_screen = "eDP-1-1" + laptop_screen_nvidia = "eDP-1-1" + laptop_screen_intel = "eDP1" # Keyboard colors do_keyboard_updates = False diff --git a/kuro/theme.py b/kuro/theme.py index 20afe3a..a4a269e 100644 --- a/kuro/theme.py +++ b/kuro/theme.py @@ -2,16 +2,27 @@ import json import os import random +# Initialize logging +from libqtile.log_utils import logger + +logger.error("Importing qtile theme requirements...") + from libqtile.config import Key, Screen, Group, Drag, Click from libqtile.command import lazy from libqtile import layout, bar, widget +logger.error("Importing theme util functions...") + # Import theme util functions from xcffib.xproto import WindowError +logger.error("Importing kuro utils...") + import kuro.utils.widgets from kuro.utils import general as 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 @@ -19,6 +30,8 @@ from kuro.utils.kb_backlight import handle_focus_change as kb_handle_focus_chang from kuro.utils import layouts as kuro_layouts from kuro.utils.windows import KuroStatic +logger.error("Importing configuration...") + try: from kuro.config import Config except ImportError: @@ -28,9 +41,7 @@ except ImportError: Config = None raise ImportError("Could not load theme Config or BaseConfig!") -# Initialize logging -from libqtile.log_utils import logger - +logger.error("Imports done") class Kuro(BaseTheme): # Shorthand for modifier key @@ -84,7 +95,8 @@ class Kuro(BaseTheme): for field in self.debug_textfields: field.text = text for bar in self.debug_bars: - bar.draw() + if self.qtile is not None: + bar.draw() def log_debug(self, text): if Config.get('verbose', False): @@ -96,6 +108,7 @@ class Kuro(BaseTheme): logger.info(text) def initialize(self): + logger.error("Initializing Kuro theme...") self.log_debug("Initializing Kuro Theme...") # Update color scheme @@ -313,18 +326,23 @@ class Kuro(BaseTheme): kuro.utils.widgets.MediaWidget(), kuro.utils.widgets.SeparatorWidget(), + ]) - 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, - ), + if Config.get('show_temperature', False): + 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, + ) + ) + widgets.extend([ widget.CPUGraph( width=Config.get('cpu_width', 25), border_color=Config.get('cpu_border_colour', "#000000"), @@ -374,6 +392,11 @@ class Kuro(BaseTheme): update_delay=Config.get('battery_update_delay', 30) ), + 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'), + ), + kuro.utils.widgets.WifiIconWidget( interface=Config.get('wifi_interface', 'wlp4s0'), theme_path=Config.get('wifi_theme_path', '/home/docs/checkouts/readthedocs.org/user_builds/qtile' @@ -519,12 +542,25 @@ class Kuro(BaseTheme): @staticmethod def update_screens(qtile): out = utils.call_process(["xrandr", "--current"]) + mode_out = utils.call_process(["optimus-manager", "--print-mode"]) + if "nvidia" in mode_out: + video_mode = "nvidia" + elif "intel" in mode_out: + video_mode = "intel" + else: + video_mode = "unknown" laptop_screen = None screens = [] for x in out.split("\n"): if " connected " in x: - if Config.get("laptop_screen", None) is not None and Config.get("laptop_screen", None) in x: - laptop_screen = 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) @@ -534,7 +570,7 @@ class Kuro(BaseTheme): other = screens[0].split()[0] utils.call_process(["xrandr", "--output", laptop, "--below", other]) qtile.cmd_restart() - elif laptop_screen is not None and len(screens) > 1: + else: utils.execute("arandr") def reinitialize_visualizers(self, qtile=None): diff --git a/kuro/utils/widgets.py b/kuro/utils/widgets.py index 2d918f2..6642827 100644 --- a/kuro/utils/widgets.py +++ b/kuro/utils/widgets.py @@ -1,4 +1,5 @@ import os +import re import subprocess import cairocffi @@ -677,3 +678,133 @@ class KuroTaskList(TaskList): return "%s%s" % (state, window_name) + +class GPUStatusWidget(base._TextBox): + """Displays the currently used GPU.""" + + orientations = base.ORIENTATION_HORIZONTAL + defaults = [ + ('check_command', 'optimus-manager --print-mode', 'The command that shows the current mode.'), + ('update_interval', 60, 'The update interval in seconds.'), + ('theme_path', default_icon_path(), 'Path of the icons'), + ('custom_icons', {}, 'dict containing key->filename icon map'), + ] + + def __init__(self, **config): + super(GPUStatusWidget, self).__init__("GPU", bar.CALCULATED, **config) + self.add_defaults(GPUStatusWidget.defaults) + + if self.theme_path: + self.length_type = bar.STATIC + self.length = 0 + self.surfaces = {} + self.current_icon = 'gpu-unknown' + self.icons = dict([(x, '{0}.png'.format(x)) for x in ( + 'gpu-intel', + 'gpu-nvidia', + 'gpu-unknown', + )]) + self.current_status = "Unknown" + self.icons.update(self.custom_icons) + + def _get_info(self): + 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} + + def timer_setup(self): + self.update() + self.timeout_add(self.update_interval, self.timer_setup) + + def _configure(self, qtile, bar): + super(GPUStatusWidget, self)._configure(qtile, bar) + self.setup_images() + + def _get_icon_key(self): + key = 'gpu' + info = self._get_info() + if info.get('mode') == "intel": + key += '-intel' + self.current_status = "Intel" + elif info.get('mode') == "nvidia": + key += '-nvidia' + self.current_status = "NVidia" + else: + key += '-unknown' + self.current_status = "Unknown" + return key + + def update(self): + icon = self._get_icon_key() + if icon != self.current_icon: + self.current_icon = icon + self.draw() + + def draw(self): + if self.theme_path: + self.drawer.clear(self.background or self.bar.background) + self.drawer.ctx.set_source(self.surfaces[self.current_icon]) + self.drawer.ctx.paint() + self.drawer.draw(offsetx=self.offset, width=self.length) + else: + self.text = self.current_icon[8:] + base._TextBox.draw(self) + + def setup_images(self): + for key, name in self.icons.items(): + try: + path = os.path.join(self.theme_path, name) + img = cairocffi.ImageSurface.create_from_png(path) + except cairocffi.Error: + self.theme_path = None + logger.warning('GPU Status Icon switching to text mode') + return + input_width = img.get_width() + input_height = img.get_height() + + sp = input_height / (self.bar.height - 1) + + width = input_width / sp + if width > self.length: + # cast to `int` only after handling all potentially-float values + self.length = int(width + self.actual_padding * 2) + + imgpat = cairocffi.SurfacePattern(img) + + scaler = cairocffi.Matrix() + + scaler.scale(sp, sp) + scaler.translate(self.actual_padding * -1, 0) + imgpat.set_matrix(scaler) + + imgpat.set_filter(cairocffi.FILTER_BEST) + self.surfaces[key] = imgpat + + def button_press(self, x, y, button): + if button == BUTTON_LEFT: + if self.current_status == "Unknown": + notify("GPU Status", "The currently used GPU is unknown.", + image=os.path.join(self.theme_path, "gpu-unknown.png")) + else: + notify("GPU Status", "The system is currently running on the {} GPU.\n" + "Press the middle mouse button on this icon to switch GPUs.".format( + self.current_status + ), + image=os.path.join(self.theme_path, "gpu-{}.png".format(self.current_status.lower())) + ) + + if button == BUTTON_MIDDLE: + command = ["optimus-manager", "--no-confirm", "--switch", "auto"] + output = self.call_process(command) + if "nvidia" in output: + 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("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("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"))