My Qtile window manager configuration
git clone https://github.com/m-col/qtile-config
Files | Refs | Readme

-rw-r--r-- config.py


      1 """
      2 Qtile main config file
      3 ======================
      4 """
      5 
      6 from __future__ import annotations
      7 
      8 import importlib
      9 import os
     10 import re
     11 import subprocess
     12 import sys
     13 from typing import TYPE_CHECKING
     14 
     15 from libqtile import bar, hook, layout, qtile, utils, widget
     16 from libqtile.backend import base
     17 from libqtile.config import Click, Drag, Key, Match, Screen
     18 from libqtile.lazy import lazy
     19 from libqtile.widget.backlight import ChangeDirection
     20 from libqtile.widget.battery import Battery, BatteryState
     21 
     22 import qtile_extras.widget as widget_extras
     23 
     24 assert qtile is not None
     25 
     26 if TYPE_CHECKING:
     27     from typing import Any
     28 
     29     from libqtile.core.manager import Qtile
     30 
     31 
     32 # Config imports
     33 def reload(module):
     34     if module in sys.modules:
     35         importlib.reload(sys.modules[module])
     36 
     37 
     38 import traverse
     39 from toggle_debug import toggle_debug
     40 
     41 use_tags = False
     42 
     43 if use_tags:
     44     reload("tags")
     45     from tags import groups, keys_group
     46 else:
     47     reload("groups")
     48     from groups import groups, keys_group
     49 
     50 reload("scratchpad")
     51 from scratchpad import keys_scratchpad, scratchpad
     52 
     53 reload("power_menu")
     54 from power_menu import keys_power_menu
     55 
     56 IS_WAYLAND: bool = qtile.core.name == "wayland"
     57 IS_XEPHYR: bool = int(os.environ.get("QTILE_XEPHYR", 0)) > 0
     58 
     59 reload(qtile.core.name)
     60 if IS_WAYLAND:
     61     from wayland import keys_backend, term, wl_input_rules, wl_shadows  # noqa: F401
     62 else:
     63     from x11 import keys_backend, term, wmname  # noqa: F401
     64 
     65 # Colours
     66 theme = dict(
     67     foreground="#CFCCD6",
     68     background="#0C1F20",
     69     color0="#030405",
     70     color8="#1f1c32",
     71     color1="#8742a5",
     72     color9="#9a5eb3",
     73     color2="#406794",
     74     color10="#5fd75f",
     75     color3="#653c21",
     76     color11="#6e9fcd",
     77     color4="#8f4ff0",
     78     color12="#BBC2E2",
     79     color5="#5d479d",
     80     color13="#998dd1",
     81     color6="#3e3e73",
     82     color14="#9a9dcc",
     83     color7="#495068",
     84     color15="#e1e1e4",
     85 )
     86 
     87 colours = [theme[f"color{n}"] for n in range(16)]
     88 background = theme["background"]
     89 foreground = theme["foreground"]
     90 colour_focussed = "#6fa3e0"
     91 colour_unfocussed = "#0e101c"
     92 inner_gaps = 8
     93 outer_gaps = 0
     94 
     95 # https://coolors.co/1b2021-cfccd6-bbc2e2-b7b5e4-847979
     96 
     97 
     98 # Keys
     99 
    100 if IS_XEPHYR:
    101     mod = "mod1"
    102     alt = "control"
    103 else:
    104     mod = "mod4"
    105     alt = "mod1"
    106 
    107 # My keybindings - they are converted at the bottom of this file
    108 my_keys: list[tuple[list[str], str, Any, str]] = []
    109 my_keys.extend(keys_backend)
    110 my_keys.extend(keys_group)
    111 my_keys.extend(keys_scratchpad)
    112 my_keys.extend(keys_power_menu)
    113 
    114 
    115 def float_to_front(qtile: Qtile) -> None:
    116     """Bring all floating windows of the group to front"""
    117     for window in qtile.current_group.windows:
    118         if window.floating:
    119             window.bring_to_front()
    120 
    121 
    122 my_keys.extend(
    123     [
    124         # Window management
    125         ([mod, "control"], "q", lazy.window.kill(), "Close window"),
    126         ([mod], "f", lazy.window.toggle_fullscreen(), "Toggle fullscreen"),
    127         ([mod, "shift"], "space", lazy.window.toggle_floating(), "Toggle floating"),
    128         ([mod], "s", lazy.spawn("ddg_rofi"), "Search web via rofi"),
    129         ([mod, "shift"], "s", lazy.window.static(), "Make window static"),
    130         (
    131             [mod],
    132             "space",
    133             lazy.function(float_to_front),
    134             "Move floating windows to the front",
    135         ),
    136         ([mod], "j", lazy.function(traverse.down), "Traverse down"),
    137         ([mod], "k", lazy.function(traverse.up), "Traverse up"),
    138         ([mod], "h", lazy.function(traverse.left), "Traverse left"),
    139         ([mod], "l", lazy.function(traverse.right), "Traverse right"),
    140         ([mod, "shift"], "j", lazy.layout.shuffle_down(), "Shuffle down"),
    141         ([mod, "shift"], "k", lazy.layout.shuffle_up(), "Shuffle up"),
    142         ([mod, "shift"], "h", lazy.layout.shuffle_left(), "Shuffle left"),
    143         ([mod, "shift"], "l", lazy.layout.shuffle_right(), "Shuffle right"),
    144         ([mod, alt], "j", lazy.layout.grow_down(), "Grow down"),
    145         ([mod, alt], "k", lazy.layout.grow_up(), "Grow up"),
    146         ([mod, alt], "h", lazy.layout.grow_left(), "Grow left"),
    147         ([mod, alt], "l", lazy.layout.grow_right(), "Grow right"),
    148         ([mod, "control"], "r", lazy.reload_config(), "Reload the config"),
    149         ([mod, "control"], "Escape", lazy.shutdown(), "Shutdown Qtile"),
    150         ([mod], "Tab", lazy.next_layout(), "Next layout"),
    151         ([mod], "b", lazy.hide_show_bar("bottom"), "Hide bar"),
    152         ([mod, "control"], "d", lazy.function(toggle_debug), "Toggle debug logging"),
    153         # Volume control - MyVolume widget is defined further down
    154         ([], "XF86AudioMute", lazy.widget["myvolume"].mute(), "Mute audio"),
    155         ([mod], "F10", lazy.widget["myvolume"].mute(), "Mute audio"),
    156         (
    157             [],
    158             "XF86AudioRaiseVolume",
    159             lazy.widget["myvolume"].increase_vol(),
    160             "Increase volume",
    161         ),
    162         ([mod], "F12", lazy.widget["myvolume"].increase_vol(), "Increase volume"),
    163         (
    164             [],
    165             "XF86AudioLowerVolume",
    166             lazy.widget["myvolume"].decrease_vol(),
    167             "Decrease volume",
    168         ),
    169         ([mod], "F11", lazy.widget["myvolume"].decrease_vol(), "Decrease volume"),
    170         # Backlight control
    171         (
    172             [],
    173             "XF86MonBrightnessUp",
    174             lazy.widget["backlight"].change_backlight(ChangeDirection.UP, 3),
    175             "Increase backlight",
    176         ),
    177         (
    178             [],
    179             "XF86MonBrightnessDown",
    180             lazy.widget["backlight"].change_backlight(ChangeDirection.DOWN, 3),
    181             "Decrease backlight",
    182         ),
    183         (
    184             [mod],
    185             "F6",
    186             lazy.widget["backlight"].change_backlight(ChangeDirection.UP, 3),
    187             "Increase backlight",
    188         ),
    189         (
    190             [mod],
    191             "F5",
    192             lazy.widget["backlight"].change_backlight(ChangeDirection.DOWN, 3),
    193             "Decrease backlight",
    194         ),
    195         # Music control
    196         (
    197             [mod],
    198             "bracketright",
    199             lazy.spawn("playerctl next"),
    200             "Next song",
    201         ),
    202         ([], "XF86AudioNext", lazy.spawn("playerctl next"), "Next song"),
    203         (
    204             [mod],
    205             "bracketleft",
    206             lazy.spawn("playerctl previous"),
    207             "Previous song",
    208         ),
    209         ([], "XF86AudioPrev", lazy.spawn("playerctl previous"), "Previous song"),
    210         ([], "Pause", lazy.spawn("playerctl play-pause"), "Play/unpause music"),
    211         ([], "XF86AudioPlay", lazy.spawn("playerctl play-pause"), "Play/unpause music"),
    212         # Launchers
    213         (
    214             [mod],
    215             "d",
    216             lazy.spawn("rofi -show run -theme ~/.config/rofi/common.rasi"),
    217             "Spawn with rofi",
    218         ),
    219         ([mod], "Return", lazy.spawn(term), "Spawn terminal"),
    220         ([mod, "shift"], "f", lazy.spawn("firefox"), "Spawn Firefox"),
    221         (
    222             [mod, "control"],
    223             "f",
    224             lazy.spawn("cleanfox"),
    225             "Spawn Firefox in clean profile",
    226         ),
    227         ([], "Print", lazy.spawn("screenshot copy"), "Screenshot to clipboard"),
    228         (["shift"], "Print", lazy.spawn("screenshot"), "Screenshot to file"),
    229         ([mod], "p", lazy.spawn("get_password_rofi"), "Keepass passwords"),
    230         ([mod], "i", lazy.spawn("systemctl suspend -i"), "Suspend system"),
    231         (
    232             [mod, "shift"],
    233             "i",
    234             lazy.spawn("gtklock & systemctl suspend -i", shell=True),
    235             "Suspend and lock",
    236         ),
    237         (
    238             ["control"],
    239             "space",
    240             lazy.spawn("swaync-client -t -sw"),
    241             "Toggle notification panel",
    242         ),
    243         (
    244             [mod, "control"],
    245             "y",
    246             lazy.spawn("yt-first"),
    247             "yt-first script",
    248         ),
    249     ]
    250 )
    251 
    252 
    253 # Mouse control
    254 mouse = [
    255     Click([mod], "Button2", lazy.window.kill()),
    256 
    257     # Move with drag or swipe
    258     Drag(
    259         [mod],
    260         "Button1",
    261         lazy.window.set_position_floating(),
    262         start=lazy.window.get_position(),
    263     ),
    264     #Swipe(
    265     #    [mod],
    266     #    3,
    267     #    lazy.window.set_position_floating(),
    268     #    start=lazy.window.get_position(),
    269     #),
    270 
    271     # Resize with drag or swipe
    272     Drag(
    273         [mod, alt],
    274         "Button1",
    275         lazy.window.set_size_floating(),
    276         start=lazy.window.get_size(),
    277     ),
    278     #Swipe(
    279     #    [mod, alt],
    280     #    3,
    281     #    lazy.window.set_size_floating(),
    282     #    start=lazy.window.get_size(),
    283     #),
    284 
    285     # Slide between groups
    286     Drag(
    287         [mod, "shift"],
    288         "Button1",
    289         lazy.screen.group_slide(),
    290         start=lazy.screen.start_group_slide(scale=2.5),
    291     ),
    292 ]
    293 
    294 if IS_WAYLAND:
    295     try:
    296         from libqtile.config import Swipe
    297     except ImportError:
    298         pass
    299     else:
    300 
    301         def _slide(qtile):
    302             groups = qtile.groups
    303             if len(qtile.screens) > 1:
    304                 if qtile.current_screen.index == 0:
    305                     groups = groups[:4]
    306                 else:
    307                     groups = groups[4:]
    308             return qtile.current_screen.start_group_slide(
    309                 groups=groups, scale=1.6, inertia_threshold=15,
    310             )
    311 
    312         mouse.append(
    313             Swipe(
    314                 [],
    315                 3 if IS_XEPHYR else 4,
    316                 lazy.screen.group_slide(),
    317                 start=lazy.function(_slide),
    318             ),
    319         )
    320 
    321 
    322 ## Firefox 3-finger vertical swipe to zoom
    323 #def swipe_zoom(qtile, _dx: int, dy: int) -> None:
    324 #    if dy % 3:
    325 #        # Only send key every 10 steps
    326 #        return
    327 #    if dy < 0:
    328 #        qtile.spawn("wlrctl keyboard type +", shell=True)
    329 #    else:
    330 #        qtile.spawn("wlrctl keyboard type -", shell=True)
    331 #
    332 #
    333 #mouse.append(
    334 #    Swipe(
    335 #        [],
    336 #        3,
    337 #        lazy.function(swipe_zoom).when(focused=Match(wm_class="firefox")),
    338 #    )
    339 #)
    340 
    341 # Layouts
    342 border_focus = [colours[5], "323974"]
    343 border_normal = "001122"
    344 
    345 
    346 class MyColumns(layout.Columns):
    347     """
    348     I only override this method so I can make 'foot' have wide margins at each side if
    349     it's the only window open.
    350     """
    351 
    352     def configure(self, client, screen_rect):
    353         pos = 0
    354         for col in self.columns:
    355             if client in col:
    356                 break
    357             pos += col.width
    358         else:
    359             client.hide()
    360             return
    361         if client.has_focus:
    362             color = self.border_focus if col.split else self.border_focus_stack
    363         else:
    364             color = self.border_normal if col.split else self.border_normal_stack
    365         border = self.border_width
    366         margin_size = self.margin
    367         if len(self.columns) == 1 and (len(col) == 1 or not col.split):
    368             if not self.border_on_single:
    369                 border = 0
    370             if "foot" in (client.get_wm_class() or []):
    371                 margin_size = [0, 200, 0, 200]
    372         width = int(0.5 + col.width * screen_rect.width * 0.01 / len(self.columns))
    373         x = screen_rect.x + int(
    374             0.5 + pos * screen_rect.width * 0.01 / len(self.columns)
    375         )
    376         if col.split:
    377             pos = 0
    378             for c in col:
    379                 if client == c:
    380                     break
    381                 pos += col.heights[c]
    382             height = int(
    383                 0.5 + col.heights[client] * screen_rect.height * 0.01 / len(col)
    384             )
    385             y = screen_rect.y + int(0.5 + pos * screen_rect.height * 0.01 / len(col))
    386             client.place(
    387                 x,
    388                 y,
    389                 width - 2 * border,
    390                 height - 2 * border,
    391                 border,
    392                 color,
    393                 margin=margin_size,
    394             )
    395             client.unhide()
    396         elif client == col.cw:
    397             client.place(
    398                 x,
    399                 screen_rect.y,
    400                 width - 2 * border,
    401                 screen_rect.height - 2 * border,
    402                 border,
    403                 color,
    404                 margin=margin_size,
    405             )
    406             client.unhide()
    407         else:
    408             client.hide()
    409 
    410 
    411 # Weird custom behaviour here but isn't that what qtile is for?
    412 # I want a Columns layout but also sometimes i want 'foot' windows to be thinner and
    413 # centred in a 'central' column, so I define a whole new MyColumns layout that does that
    414 # and I just jump between the two when I want that.
    415 col_opts = dict(
    416     insert_position=1,
    417     border_width=5,
    418     border_focus=border_focus,
    419     border_normal="00000000",
    420     border_on_single=False,
    421     wrap_focus_columns=False,
    422     wrap_focus_rows=False,
    423     margin=0,
    424     # margin_on_single=30,  # 4
    425 )
    426 
    427 layouts = [
    428     MyColumns(**col_opts),
    429     layout.Columns(**col_opts),
    430 ]
    431 
    432 floating_layout = layout.Floating(
    433     border_width=3,
    434     border_focus=border_focus,
    435     border_normal="00000000",
    436     fullscreen_border_width=0,
    437     float_rules=[
    438         Match(func=base.Window.has_fixed_size),
    439         Match(func=base.Window.has_fixed_ratio),
    440         Match(func=lambda c: bool(c.is_transient_for())),
    441         Match(role="gimp-file-export"),
    442         Match(title="Bluetooth Devices"),
    443         Match(title="File Operation Progress", wm_class=re.compile("[Tt]hunar")),
    444         Match(title="Firefox — Sharing Indicator"),
    445         Match(title="KDE Connect Daemon"),
    446         Match(title="Open File"),
    447         Match(title="Unlock Database - KeePassXC"),
    448         Match(title="KeePassXC -  Access Request"),
    449         Match(title=re.compile("Presenting: .*"), wm_class="libreoffice-impress"),
    450         Match(wm_class="Arandr"),
    451         Match(wm_class="Dragon"),
    452         Match(wm_class="Dragon-drag-and-drop"),
    453         Match(wm_class="Pinentry-gtk-2"),
    454         Match(wm_class="Xephyr"),
    455         Match(wm_class="confirm"),
    456         Match(wm_class="dialog"),
    457         Match(wm_class="download"),
    458         Match(wm_class="eog"),
    459         Match(wm_class="error"),
    460         Match(wm_class="file_progress"),
    461         Match(wm_class="imv"),
    462         Match(wm_class="io.github.celluloid_player.Celluloid"),
    463         Match(wm_class="lxappearance"),
    464         Match(wm_class="matplotlib"),
    465         #Match(wm_class="mpv"),
    466         Match(wm_class="nm-connection-editor"),
    467         Match(wm_class="notification"),
    468         Match(wm_class="org.gnome.clocks"),
    469         Match(wm_class="org.kde.ark"),
    470         Match(wm_class="pavucontrol"),
    471         Match(wm_class="qt5ct"),
    472         Match(wm_class="ssh-askpass"),
    473         Match(wm_class="thunar"),
    474         Match(wm_class="toolbar"),
    475         Match(wm_class="tridactyl"),
    476         Match(wm_class="wdisplays"),
    477         Match(wm_class="wlroots"),
    478         Match(wm_class="zoom"),
    479         Match(wm_type="dialog"),
    480     ],
    481 )
    482 
    483 
    484 # Screens and Bars
    485 widget_defaults = {
    486     "padding": 10,
    487     "foreground": colours[6],
    488     "font": "Font Awesome 5 Free",
    489     "fontsize": 16,
    490 }
    491 
    492 icon_font_size = 22
    493 
    494 groupbox_config = {
    495     "active": foreground,
    496     "highlight_method": "block",
    497     "this_current_screen_border": colour_focussed,
    498     "other_current_screen_border": colours[5],
    499     "highlight_color": [background, colours[5]],
    500     "disable_drag": True,
    501     "padding": 4,
    502     "font": "TamzenForPowerline Bold",
    503     "fontsize": 12,
    504 }
    505 
    506 # mpd2 = widget.Mpd2(
    507 #    no_connection="",
    508 #    status_format="{artist} - {title}",
    509 #    status_format_stopped="",
    510 #    foreground=colours[12],
    511 #    idle_format="",
    512 #    font="TamzenForPowerline Bold",
    513 #    update_interval=10,
    514 #    scroll=True,
    515 # )
    516 
    517 mpd2 = widget.Mpris2(
    518     name="mpris",
    519     width=1000,
    520     objname=None,
    521     format="{xesam:title} - {xesam:artist}",
    522     font="TamzenForPowerline Bold",
    523 )
    524 
    525 
    526 class MyVolume(widget.Volume):
    527     def _configure(self, qtile, bar):
    528         widget.Volume._configure(self, qtile, bar)
    529         self.volume = self.get_volume()
    530         if self.volume <= 0:
    531             self.text = ""
    532         elif self.volume <= 15:
    533             self.text = ""
    534         elif self.volume < 50:
    535             self.text = ""
    536         else:
    537             self.text = ""
    538 
    539     def _update_drawer(self):
    540         if self.volume <= 0:
    541             self.text = ""
    542         elif self.volume <= 15:
    543             self.text = ""
    544         elif self.volume < 50:
    545             self.text = ""
    546         else:
    547             self.text = ""
    548         self.draw()
    549 
    550     def increase_vol(self):
    551         subprocess.run("amixer -c PCH set PCM 3%+".split(), capture_output=True)
    552         self.volume = self.get_volume()
    553 
    554     def decrease_vol(self):
    555         subprocess.run("amixer -c PCH set PCM 3%-".split(), capture_output=True)
    556         self.volume = self.get_volume()
    557 
    558     def mute(self):
    559         subprocess.run("amixer -c PCH set PCM toggle".split(), capture_output=True)
    560         self.volume = self.get_volume()
    561 
    562 
    563 bklight = widget.Backlight(
    564     backlight_name=os.listdir("/sys/class/backlight")[-1],
    565     step=1,
    566     update_interval=None,
    567     format="",
    568     fontsize=icon_font_size,
    569     change_command=None,
    570 )
    571 
    572 volume = MyVolume(
    573     fontsize=icon_font_size,
    574     channel="PCM",
    575     font="Font Awesome 5 Free",
    576     update_interval=60,
    577     cardid="PCH",
    578     device=None,
    579 )
    580 
    581 if IS_WAYLAND:
    582     systray = widget_extras.StatusNotifier(padding=20)
    583 else:
    584     systray = widget.Systray(padding=20, icon_size=24)
    585 
    586 
    587 class MyBattery(Battery):
    588     """
    589     This is basically the Battery widget except it uses some icons, and if you click it
    590     it will show the percentage numerically for 1 second.
    591     """
    592 
    593     def build_string(self, status):
    594         if self.layout is not None:
    595             self.layout.colour = self.foreground
    596             if (
    597                 status.state == BatteryState.DISCHARGING
    598                 and status.percent < self.low_percentage
    599             ):
    600                 self.background = self.low_background
    601             else:
    602                 self.background = self.normal_background
    603         if status.state == BatteryState.DISCHARGING:
    604             if status.percent > 0.75:
    605                 char = ""
    606             elif status.percent > 0.45:
    607                 char = ""
    608             elif status.percent > 0.25:
    609                 char = ""
    610             else:
    611                 char = ""
    612         elif status.percent >= 1 or status.state == BatteryState.FULL:
    613             char = ""
    614         elif status.state == BatteryState.EMPTY or (
    615             status.state == BatteryState.UNKNOWN and status.percent == 0
    616         ):
    617             char = ""
    618         else:
    619             char = ""
    620         return self.format.format(char=char, percent=status.percent)
    621 
    622     def restore(self):
    623         self.format = "{char}"
    624         self.font = "Font Awesome 5 Free"
    625         self.timer_setup()
    626 
    627     def button_press(self, x, y, button):
    628         self.format = "{percent:2.0%}"
    629         self.font = "TamzenForPowerline Bold"
    630         self.timer_setup()
    631         self.timeout_add(1, self.restore)
    632 
    633 
    634 battery = MyBattery(
    635     format="{char}",
    636     low_background=colours[1],
    637     show_short_text=False,
    638     low_percentage=0.12,
    639     notify_below=12,
    640     fontsize=icon_font_size + 10,
    641 )
    642 
    643 date = widget.Clock(
    644     format="%e/%m/%g",
    645     fontsize=16,
    646     font="TamzenForPowerline Bold",
    647     update_interval=60,
    648     name="date",
    649 )
    650 
    651 time = widget.Clock(
    652     fontsize=20,
    653     font="TamzenForPowerline Medium",
    654     update_interval=60,
    655     name="time",
    656 )
    657 
    658 groupboxes = [
    659     widget.GroupBox(**groupbox_config),
    660     widget.GroupBox(**groupbox_config, visible_groups=["q", "w", "e", "r"]),
    661 ]
    662 
    663 
    664 @hook.subscribe.startup
    665 def _():
    666     # Set up initial GroupBox visible groups
    667     if len(qtile.screens) > 1:
    668         groupboxes[0].visible_groups = ["1", "2", "3", "4"]
    669     else:
    670         groupboxes[0].visible_groups = None
    671 
    672 
    673 @hook.subscribe.screens_reconfigured
    674 def _():
    675     # Reconfigure GroupBox visible groups
    676     if len(qtile.screens) > 1:
    677         groupboxes[0].visible_groups = ["1", "2", "3", "4"]
    678     else:
    679         groupboxes[0].visible_groups = None
    680     if hasattr(groupboxes[0], "bar"):
    681         groupboxes[0].bar.draw()
    682 
    683 
    684 #bar_border_width = [0, 3, 0, 3]
    685 #bar_border_color = ["000000", colours[5], "000000", colours[5]]
    686 
    687 screens = [
    688     Screen(
    689         top=bar.Bar(
    690             [
    691                 groupboxes[0],
    692                 widget.Spacer(name="s1"),
    693                 mpd2,
    694                 widget.Spacer(name="s2"),
    695                 systray,
    696                 volume,
    697                 bklight,
    698                 battery,
    699                 date,
    700                 time,
    701             ],
    702             28,
    703             background=background,
    704         ),
    705         bottom=bar.Gap(outer_gaps),
    706         left=bar.Gap(outer_gaps),
    707         right=bar.Gap(outer_gaps),
    708         #wallpaper="~/pictures/Wallpapers/fractal.bmp",
    709         #wallpaper_mode="fill",
    710     ),
    711     Screen(
    712         top=bar.Bar(
    713             [
    714                 groupboxes[1],
    715                 widget.Spacer(name="s3"),
    716                 mpd2,
    717                 widget.Spacer(name="s4"),
    718                 volume,
    719                 bklight,
    720                 battery,
    721                 date,
    722                 time,
    723             ],
    724             28,
    725             background=background,
    726         ),
    727         #wallpaper="~/pictures/Wallpapers/fractal.bmp",
    728         #wallpaper_mode="fill",
    729         bottom=bar.Gap(outer_gaps),
    730         left=bar.Gap(outer_gaps),
    731         right=bar.Gap(outer_gaps),
    732     ),
    733 ]
    734 
    735 
    736 @hook.subscribe.client_focus
    737 def _(_):
    738     # Keep Static windows on top
    739     for window in qtile.windows_map.values():
    740         if isinstance(window, base.Static):
    741             window.bring_to_front()
    742 
    743 
    744 reconfigure_screens = True
    745 follow_mouse_focus = True
    746 bring_front_click = True
    747 cursor_warp = False
    748 auto_fullscreen = True
    749 focus_on_window_activation = "focus"
    750 
    751 keys = [Key(mods, key, cmd, desc=desc) for mods, key, cmd, desc in my_keys]
    752 groups.append(scratchpad)
    753