diff --git a/kuro/config.py b/kuro/config.py index a7c8486..ba86da4 100644 --- a/kuro/config.py +++ b/kuro/config.py @@ -10,8 +10,11 @@ class Config(BaseConfig): # Default Applications app_terminal = "terminator" app_launcher = "dmenu_run -i -p '»' -nb '#000000' -fn 'Noto Sans-11' -nf '#777777' -sb '#1793d0' -sf '#ffffff'" + web_browser = "firefox" + file_manager = "thunar" cmd_brightness_up = "sudo /usr/bin/xbacklight -inc 10" cmd_brightness_down = "sudo /usr/bin/xbacklight -dec 10" + lock_command = "/home/kevin/bin/lock.sh" # Images desktop_bg = "/home/kevin/Pictures/wallpapers/desktop.png" diff --git a/kuro/theme.py b/kuro/theme.py index c2d3b1f..7c00083 100644 --- a/kuro/theme.py +++ b/kuro/theme.py @@ -7,8 +7,9 @@ from kuro.utils import general as utils # Import variables from kuro.base import BaseTheme -from kuro.utils.general import display_wm_class +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 try: from kuro.config import Config @@ -103,9 +104,17 @@ class Kuro(BaseTheme): # 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-F to start file manager +# Key([self.mod], "f", 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"))), # Backlight keys Key([], "XF86MonBrightnessUp", lazy.spawn(Config.get('cmd_brightness_up', 'xbacklight -inc 10'))), @@ -120,9 +129,6 @@ class Kuro(BaseTheme): # Restart QTile Key([self.mod, "control"], "r", lazy.restart()), - # Redraw the top bar - Key([self.mod, "shift", "control"], "r", lazy.function(self.redraw_bar)), - # Shutdown QTile Key([self.mod, "control"], "q", lazy.shutdown()), @@ -132,20 +138,39 @@ class Kuro(BaseTheme): ## # Debug keyboard shortcuts ## - Key([self.mod, "control"], "w", lazy.function(display_wm_class)) + Key([self.mod, "control"], "w", lazy.function(display_wm_class)), + + # Redraw the top bar + Key([self.mod, "shift", "control"], "r", lazy.function(self.redraw_bar)), + + # Spawn a popup, and despawn it after 3 seconds + Key([self.mod, "control"], "p", lazy.function(test_popups)), ] def init_groups(self): self.log_debug("Initializing groups") - + + groups = [] + # http://fontawesome.io/cheatsheet - return [Group(i) for i in ""] + groups.append(Group("", spawn=Config.get('web_browser', "xterm links"))) + groups.append(Group("", spawn=Config.get('app_terminal', "xterm"))) + groups.append(Group("")) + groups.append(Group("", spawn="franz4-bin")) + groups.append(Group("", spawn="quasselclient")) + groups.append(Group("", spawn=Config.get('file_manager', "thunar"))) + groups.append(Group("", spawn="thunderbird")) + groups.append(Group("")) + groups.append(Group("", spawn="qupzilla https://music.kurocon.nl/")) + groups.append(Group("")) + + return groups def init_layouts(self): self.log_debug("Initializing layouts") return [ - layout.Wmii( + kuro_layouts.KuroWmii( 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"), @@ -261,7 +286,7 @@ class Kuro(BaseTheme): frequency=2, ), - widget.BatteryIcon( + utils.KuroBatteryIcon( battery_name=Config.get('battery_name', 'BAT0'), energy_full_file=Config.get('battery_energy_full_file', 'charge_full'), energy_now_file=Config.get('battery_energy_now_file', 'charge_now'), @@ -378,6 +403,8 @@ class Kuro(BaseTheme): self.log_debug("Updating keys") for i, g in enumerate(self.groups): + if i == 9: + i = -1 # mod1 + number = switch to group self.keys.append( Key([self.mod], str(i + 1), lazy.group[g.name].toscreen()) @@ -441,3 +468,38 @@ class Kuro(BaseTheme): def callback_focus_change(self, *args, **kwargs): kb_handle_focus_change(self) + + initial_windows = [] + + def callback_startup_complete(self, *args, **kwargs): + # Only run on first startup + if not self.qtile.no_spawn: + dg = self.qtile.dgroups + for r in dg.rules: + pid = -1 + # noinspection PyProtectedMember + for r2 in r.match._rules: + if r2[0] == "net_wm_pid": + pid = r2[1] + break + if pid != -1: + self.initial_windows.append((pid, r.group)) + + self.callback_client_new() + + def callback_client_new(self, *args, **kwargs): + if len(self.initial_windows) > 0: + init = self.initial_windows.copy() + for pid, gname in init: + for group in self.qtile.groups: + if len(group.windows) > 0: + for window in group.windows: + w_pid = window.window.get_net_wm_pid() + if pid == w_pid: + c = self.qtile.dgroups.rules_map.copy() + for rid, r in c.items(): + if r.matches(window): + self.qtile.dgroups.remove_rule(rid) + self.initial_windows.remove((pid, gname)) + self.log_info("Removed group rule for PID {}, window {}".format(pid, window.name)) + self.log_info(str(self.qtile.dgroups.rules_map)) diff --git a/kuro/utils/general.py b/kuro/utils/general.py index ab2ff17..255dc6a 100644 --- a/kuro/utils/general.py +++ b/kuro/utils/general.py @@ -1,10 +1,12 @@ import os import re import subprocess +from time import sleep import cairocffi import notify2 from libqtile import widget, bar +from libqtile.window import Internal from libqtile.bar import Bar from libqtile.utils import catch_exception_and_warn, UnixCommandNotFound from libqtile.widget import base @@ -13,6 +15,7 @@ from libqtile.widget.check_updates import CheckUpdates from libqtile.widget.image import Image from libqtile.widget.sensors import ThermalSensor from libqtile.widget.volume import Volume +from libqtile.widget.battery import BatteryIcon from libqtile.widget.wlan import get_status from libqtile.log_utils import logger from notify2 import Notification, URGENCY_NORMAL @@ -80,13 +83,45 @@ def notify(title, content, urgency=URGENCY_NORMAL, timeout=5000, image=None): def spawn_popup(qtile, x, y, text): + """ + :param qtile: The main qtile instance + :type qtile: Qtile + :param x: x-coordinate + :type x: int + :param y: y-coordinate + :type y: int + :param text: String to display + :type text: str + :return: The popup instance + :rtype: Internal + """ + popup = Internal.create( + qtile, x, y, 100, 100, opacity=1 + ) + # Create textwidget for in window - pass + popup.bordercolor = "#000000" + popup.borderwidth = 1 + + popup.focus(False) + + #popup. + + return popup - # window.Internal.create( - # qtile, x, y, width, height, opacity=1 - # ) +def despawn_popup(popup): + """ + :type popup: Internal + :param popup: The popup to despawn + """ + popup.kill() + + +def test_popups(qtile): + popup = spawn_popup(qtile, 10, 10, "Hello World!") + sleep(3) + despawn_popup(popup) def display_wm_class(qtile): @@ -218,6 +253,17 @@ class CheckUpdatesYaourt(CheckUpdates): elif button == BUTTON_MIDDLE and self.execute is not None: subprocess.Popen(self.execute, shell=True) +class KuroBatteryIcon(BatteryIcon): + status_cmd = "acpi" + + def button_press(self, x, y, button): + if button == BUTTON_LEFT: + output = subprocess.check_output(self.status_cmd).decode('utf-8') + + notify( + "Battery Status", + output + ) class PulseVolumeWidget(Volume): diff --git a/kuro/utils/kb_backlight.py b/kuro/utils/kb_backlight.py index d028aa9..35f46a9 100644 --- a/kuro/utils/kb_backlight.py +++ b/kuro/utils/kb_backlight.py @@ -59,7 +59,7 @@ def handle_focus_change(theme): name = window.name if wm_class: - theme.log_info(str(wm_class)) + theme.log_info("{}, {}".format(wm_class, name)) # Check which window we entered and do some special effects if it is a special window. diff --git a/kuro/utils/layouts.py b/kuro/utils/layouts.py new file mode 100644 index 0000000..7b34071 --- /dev/null +++ b/kuro/utils/layouts.py @@ -0,0 +1,51 @@ +from libqtile.layout.wmii import Wmii + + +class KuroWmii(Wmii): + def cmd_previous(self): + super(KuroWmii, self).cmd_previous() + + def cmd_next(self): + super(KuroWmii, self).cmd_next() + + def add(self, client): + """ + Add a new client window to the layout and focus it. It will be added to either the current column if there + are less rows in the current column than columns on the screen, or to a new row to the right of the current + column if there are less columns than rows in the current column. + :param client: The client window to add. + """ + self.clients.append(client) + c = self.current_column() + if c is None: + if len(self.columns) == 0: + self.columns = [{'active': 0, 'width': 100, 'mode': 'split', 'rows': []}] + c = self.columns[0] + c['rows'].append(client) + else: + num_cols = len(self.columns) + num_rows_curr_col = len(c['rows']) + if num_rows_curr_col < num_cols: + c['rows'].append(client) + else: + self.add_column_to_right(c, client) + self.focus(client) + + def add_column_to_right(self, column, win): + """ + Adds a new column to the right of the given column with the given window in it + :param column: The column that's going to be to the left of the new column + :param win: The window to add to the new column + """ + newwidth = int(100 / (len(self.columns) + 1)) + # we are only called if there already is a column, simplifies things + for c in self.columns: + c['width'] = newwidth + c = {'width': newwidth, 'mode': 'split', 'rows': [win]} + + try: + index = self.columns.index(column) + 1 + except ValueError: + index = 0 + + self.columns.insert(index, c)