universe/users/infinidoge/config/qtile/config.py

699 lines
20 KiB
Python

"""
Infinidoge's Qtile Configuration
Sets up
- Keybindings
- Widgets
- Screens
"""
import subprocess
import os
from typing import List, Any # noqa: F401
from libqtile import bar, layout, widget, hook
from libqtile.config import Click, Drag, Group, Key, KeyChord, Match, Screen
from libqtile.lazy import lazy
from libqtile.utils import guess_terminal
colors = [
# panel background
["#282c34", "#282c34"], # 0
# background for current screen tab
["#3d3f4b", "#434758"], # 1
# font color for group names
["#ffffff", "#ffffff"], # 2
# border line color for current tab
["#ff5555", "#ff5555"], # 3
# border line color for 'other tabs' and color for 'odd widgets'
["#74438f", "#74438f"], # 4
# color for the 'even widgets'
["#4f76c7", "#4f76c7"], # 5
# window name
["#e1acff", "#e1acff"], # 6
# background for inactive screens
["#ecbbfb", "#ecbbfb"], # 7
]
class Apps:
"""
Commonly referenced apps, such as the terminal or editor
"""
TERMINAL = guess_terminal(preference="kitty") # Set preference if necessary
SHELL = "zsh"
EDITOR = "emacsclient -c -a 'emacs'"
@classmethod
def terminal_command(cls, program, args=tuple(), *, terminal=None):
"""
Returns a string of a command to open a program in the terminal, with the given arguments.
"""
return f"{terminal or cls.TERMINAL} {' '.join(args)} -- {program}"
@classmethod
def open_in_terminal(cls, program, args=tuple(), *, terminal=None):
"""
Opens a program in the terminal, with the given arguments.
"""
return lazy.spawn(cls.terminal_command(program, args=args, terminal=terminal))
@classmethod
def open_in_editor(cls, file_, args=tuple()):
"""
Opens a file in the editor, with the given arguments
"""
return lazy.spawn(f"{cls.EDITOR} {' '.join(args)} {file_}")
class Keys:
"""
Common modifier keys used in key configuration.
"""
SUPER = "mod4" # Windows key/super key
CONTROL = "control"
SHIFT = "shift"
ALT = "mod1"
ALT_GR = "mod5"
META = None # "mod3" # Not assigned to key yet
CAPS_LOCK = "lock"
NUM_LOCK = "mod2"
# fmt: off
keys = [
# Switch between windows
Key(
[Keys.SUPER], "Left",
lazy.layout.left(),
desc="Move focus to left",
),
Key(
[Keys.SUPER], "Right",
lazy.layout.right(),
desc="Move focus to right",
),
Key(
[Keys.SUPER], "Down",
lazy.layout.down(),
desc="Move focus down",
),
Key(
[Keys.SUPER], "Up",
lazy.layout.up(),
desc="Move focus up",
),
Key(
[Keys.SUPER], "space",
lazy.layout.next(),
desc="Move window focus to other window",
),
# Switch focus of monitors
Key(
[Keys.SUPER, Keys.CONTROL], "1",
lazy.to_screen(1),
desc="Keyboard focus to left monitor"
),
Key(
[Keys.SUPER, Keys.CONTROL], "2",
lazy.to_screen(0),
desc="Keyboard focus to middle monitor"
),
Key(
[Keys.SUPER, Keys.CONTROL], "3",
lazy.to_screen(2),
desc="Keyboard focus to right monitor"
),
Key(
[Keys.SUPER, Keys.CONTROL, Keys.SHIFT], "comma",
lazy.next_screen(),
desc="Move focus to next monitor"
),
Key(
[Keys.SUPER, Keys.CONTROL, Keys.SHIFT], "period",
lazy.prev_screen(),
desc="Move focus to prev monitor"
),
# Move windows between left/right columns or move up/down in current stack.
# Moving out of range in Columns layout will create new column.
Key(
[Keys.SUPER, Keys.SHIFT], "Left",
lazy.layout.shuffle_left(),
desc="Move window to the left",
),
Key(
[Keys.SUPER, Keys.SHIFT], "Right",
lazy.layout.shuffle_right(),
desc="Move window to the right",
),
Key(
[Keys.SUPER, Keys.SHIFT], "Down",
lazy.layout.shuffle_down(),
desc="Move window down",
),
Key(
[Keys.SUPER, Keys.SHIFT], "Up",
lazy.layout.shuffle_up(),
desc="Move window up",
),
# 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(
[Keys.SUPER, Keys.SHIFT], "Return",
lazy.layout.toggle_split(),
desc="Toggle between split and unsplit sides of stack",
),
# Grow windows. If current window is on the edge of screen and direction
# will be to screen edge - window would shrink.
Key(
[Keys.SUPER, Keys.CONTROL], "Left",
lazy.layout.grow_left(),
desc="Grow window to the left",
),
Key(
[Keys.SUPER, Keys.CONTROL], "Right",
lazy.layout.grow_right(),
desc="Grow window to the right",
),
Key(
[Keys.SUPER, Keys.CONTROL], "Down",
lazy.layout.grow_down(),
desc="Grow window down",
),
Key(
[Keys.SUPER, Keys.CONTROL], "Up",
lazy.layout.grow_up(),
desc="Grow window up",
),
Key(
[Keys.SUPER, Keys.CONTROL], "n",
lazy.layout.normalize(),
desc="Reset all window sizes",
),
Key(
[Keys.SUPER, Keys.CONTROL], "m",
lazy.layout.maximize(),
desc="Maximize the selected window",
),
# Toggle between different layouts as defined below
Key(
[Keys.SUPER], "Tab",
lazy.next_layout(),
desc="Move forward through layouts",
),
Key(
[Keys.SUPER, Keys.SHIFT], "Tab",
lazy.prev_layout(),
desc="Move backward through layouts",
),
# Qtile management keys (close, restart, shutdown, lock, spawn)
Key(
[Keys.SUPER], "w",
lazy.window.kill(),
desc="Kill focused window",
),
Key(
[Keys.SUPER, Keys.CONTROL, Keys.SHIFT], "f",
lazy.window.toggle_floating(),
desc="Toggle window's floating mode"
),
Key(
[Keys.SUPER, Keys.CONTROL], "r",
lazy.reload_config(),
desc="Reload Qtile Configuration",
),
# Key(
# [Keys.SUPER, Keys.CONTROL], "r",
# lazy.restart(),
# desc="Restart Qtile",
# ),
# Key(
# [Keys.SUPER, Keys.CONTROL], "q",
# lazy.shutdown(),
# desc="Shutdown Qtile",
# ),
Key(
[Keys.SUPER, Keys.CONTROL], "l",
lazy.spawn("xsecurelock"),
desc="Lock Screen",
),
# Keys for spawning commands or applications
Key(
[Keys.SUPER], "r",
lazy.spawncmd(),
desc="Spawn a command using a prompt widget",
),
Key(
[Keys.SUPER, Keys.SHIFT], "r",
lazy.spawncmd(prompt="shell", command=Apps.terminal_command("%s", args=("--hold",))),
desc="Spawn a command in a shell using a prompt widget",
),
Key(
[Keys.SUPER, Keys.ALT], "r",
lazy.spawn("rofi -show drun"),
desc="Spawn a rofi menu to select a desktop application to open",
),
Key(
[Keys.SUPER, Keys.CONTROL, Keys.SHIFT], "r",
lazy.spawn("rofi -show run"),
desc="Spawn something in the path",
),
# Power management
KeyChord(
[Keys.SUPER], "p",
[
Key(
[], "h",
lazy.spawn("systemctl hibernate"),
desc="Hibernates the system",
),
Key(
[], "s",
lazy.spawn("systemctl suspend"),
desc="Suspends the system",
),
Key(
[], "b",
lazy.spawn("xset dpms force off"),
desc="Turns off the screen",
),
Key(
[], "r",
lazy.spawn("reboot"),
desc="Reboots the system",
),
Key(
[], "p",
lazy.spawn("shutdown now"),
desc="Shuts down the system",
),
Key(
[], "k",
lazy.spawn("systemctl kexec"),
desc="Quick reboots system, directly loading kernel into memory",
),
Key(
[], "l",
lazy.spawn("kill -9 -1"),
desc="Logs out of the system",
),
],
),
# Volume
Key(
[], "XF86AudioRaiseVolume",
lazy.spawn("amixer set Master 2%+"),
desc="Raise volume",
),
Key(
[], "XF86AudioLowerVolume",
lazy.spawn("amixer set Master 2%-"),
desc="Lower volume",
),
Key(
[], "XF86AudioMute",
lazy.spawn("amixer set Master toggle"),
desc="Toggle mute",
),
# Application shortcuts
Key(
[Keys.SUPER], "Return",
lazy.spawn(Apps.TERMINAL),
desc="Launch terminal",
),
Key(
[Keys.SUPER, Keys.ALT], "f",
lazy.spawn("firefox --new-window"),
desc="Start Firefox (New Window)",
),
Key(
[Keys.SUPER, Keys.ALT], "e",
lazy.spawn("emacsclient -c -a 'emacs'"),
desc="Launch Emacs",
),
Key(
[Keys.SUPER, Keys.ALT], "d",
lazy.spawn("discordcanary"),
desc="Launch Discord",
),
Key(
[Keys.SUPER, Keys.ALT], "c",
lazy.spawn("speedcrunch"),
desc="Launch Speedcrunch",
),
Key(
[Keys.SUPER, Keys.ALT], "s",
lazy.spawn("gnome-screenshot -i"), # Potentially replace with shutter
desc="Launch screenshot tool",
),
]
# fmt: on
groups = [
*[Group(i) for i in "123456789"],
Group("0", layouts=[layout.TreeTab()]),
]
for i in groups:
keys.extend(
[
Key(
[Keys.SUPER],
i.name,
lazy.group[i.name].toscreen(toggle=True),
desc=f"Switch to group {i.name}",
),
Key(
[Keys.SUPER, Keys.SHIFT],
i.name,
lazy.window.togroup(i.name, switch_group=True),
desc=f"Switch to & move focused window to group {i.name}",
),
Key(
[Keys.SUPER, Keys.CONTROL, Keys.SHIFT],
i.name,
lazy.window.togroup(i.name),
desc=f"Move focused window to group {i.name}",
),
]
)
layouts = [
layout.Columns(border_focus_stack="#d75f5f"),
layout.Max(),
layout.TreeTab(),
# Try more layouts by unleashing below layouts.
# layout.Stack(num_stacks=2),
# layout.Bsp(),
# layout.Matrix(),
# layout.MonadTall(),
# layout.MonadWide(),
# layout.RatioTile(),
# layout.Tile(),
# layout.VerticalTile(),
]
widget_defaults = dict(font="sans", fontsize=12, padding=2, background=colors[0])
extension_defaults = widget_defaults.copy()
def create_powerline(
widgets: List[List[Any]],
*,
base,
foreground,
backgrounds,
sep="",
padding=0,
fontsize=37,
):
"""
Takes in a list of widgets and produces a powerline-style bar as the result.
Cycles through the provided colors and uses the provided character as a separator.
Takes input as a list of widgets, then base, foreground, and backgrounds as keyword arguments.
Setting sep changes the separator
"""
output = []
for i, widgets_list in enumerate(widgets):
bg_index = (len(widgets) - i) % len(backgrounds)
output.append(
widget.TextBox(
text=sep,
foreground=backgrounds[bg_index],
background=base
if i == 0
else backgrounds[bg_index - (len(backgrounds) - 1)],
fontsize=fontsize,
padding=padding,
)
)
for elem in widgets_list:
elem.foreground = foreground
elem.background = backgrounds[bg_index]
output.append(elem)
return output
def init_widget_list(main=True, laptop=False):
"""
Returns a list of widgets suitable for a qtile bar
"""
widget_list = [
widget.Sep(linewidth=0, padding=6, foreground=colors[2], background=colors[0]),
widget.GroupBox(
font="Ubuntu Mono",
fontsize=14,
margin_y=3,
margin_x=0,
padding_y=5,
padding_x=3,
borderwidth=3,
active=colors[2],
inactive=colors[7],
rounded=False,
highlight_color=colors[1],
highlight_method="line",
this_current_screen_border=colors[5],
this_screen_border=colors[6],
other_current_screen_border=colors[5],
other_screen_border=colors[4],
foreground=colors[2],
background=colors[0],
invert_mouse_wheel=True,
),
widget.Chord(
font="Ubuntu Mono",
padding=10,
foreground=colors[3],
background=colors[1],
max_chars=10,
),
widget.Sep(linewidth=0, padding=6, foreground=colors[2], background=colors[0]),
widget.Prompt(
prompt="{prompt}: ",
font="Ubuntu Mono",
padding=10,
foreground=colors[3],
background=colors[1],
),
widget.Sep(linewidth=0, padding=40, foreground=colors[2], background=colors[0]),
widget.WindowName(foreground=colors[6], background=colors[0], padding=0),
*(
[
widget.Systray(background=colors[0], padding=5),
]
if main
else []
),
widget.Sep(linewidth=0, padding=6, foreground=colors[0], background=colors[0]),
*create_powerline(
[
# Widgets only found on the main screen's powerline
*(
[
[
*sum(
[
[
*(
[widget.Sep(linewidth=2, padding=3)]
if i != 0
else []
),
widget.TextBox(text=f"{interface}:", padding=2),
widget.Net(
interface=interface,
format="{down} ↓↑ {up}",
padding=5,
),
]
for i, interface in enumerate(
os.popen(
"ifconfig -s | awk {'print $1'} | grep -Ev -e Iface -e lo -e vir.+ -e docker.+ | tac"
)
.read()
.splitlines()
)
],
[],
)
],
[
widget.TextBox(
text=" 🖬",
padding=0,
fontsize=14,
),
widget.Memory(
padding=5,
),
],
[
widget.TextBox(
text=" Vol:",
padding=0,
),
widget.Volume(
padding=5,
),
],
*(
[
[
widget.Battery(
format="{char} {percent:2.1%} {hour:d}h:{min:02d}m",
update_interval=10,
padding=5,
),
],
]
if laptop
else []
),
]
if main
else []
),
# Widgets found on the powerline of all screens
[
widget.CurrentLayoutIcon(
custom_icon_paths=[os.path.expanduser("~/.config/qtile/icons")],
padding=0,
scale=0.7,
),
widget.CurrentLayout(
padding=5,
),
],
[
widget.Clock(
format="%A, %B %d, %Y - %H:%M:%S ",
),
],
],
base=colors[0],
foreground=colors[2],
backgrounds=[
colors[4],
colors[5],
],
),
]
return widget_list
screens = [
Screen(
bottom=bar.Bar(
init_widget_list(main=(i == 0), laptop=os.getenv("LAPTOP", False)),
size=20,
opacity=1.0,
),
wallpaper="~/Pictures/BotanBackground.jpg",
wallpaper_mode="fill",
)
for i in range(
int(
os.popen(
"xrandr --listmonitors | grep 'Monitors:' | awk {'print $2'}"
).read()
)
)
]
# Drag floating layouts.
mouse = [
Drag(
[Keys.SUPER],
"Button1",
lazy.window.set_position_floating(),
start=lazy.window.get_position(),
),
Drag(
[Keys.SUPER],
"Button3",
lazy.window.set_size_floating(),
start=lazy.window.get_size(),
),
Click(
[Keys.SUPER],
"Button2",
lazy.window.bring_to_front(),
),
]
floating_layout = layout.Floating(
float_rules=[
# Run the utility of `xprop` to see the wm class and name of an X client.
# Section taken from Default Float Rules
Match(wm_type="utility"),
Match(wm_type="notification"),
Match(wm_type="toolbar"),
Match(wm_type="splash"),
Match(wm_type="dialog"),
Match(wm_class="file_progress"),
Match(wm_class="confirm"),
Match(wm_class="dialog"),
Match(wm_class="download"),
Match(wm_class="error"),
Match(wm_class="notification"),
Match(wm_class="splash"),
Match(wm_class="toolbar"),
Match(func=lambda c: c.has_fixed_size()),
Match(func=lambda c: c.has_fixed_ratio()),
# End section
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(wm_class="pinentry"),
]
)
dgroups_key_binder = None
dgroups_app_rules = [] # type: List
follow_mouse_focus = True
bring_front_click = False
cursor_warp = True
auto_fullscreen = True
focus_on_window_activation = "never"
reconfigure_screens = True
# If things like steam games want to auto-minimize themselves when losing
# focus, should we respect this or not?
auto_minimize = True
# XXX: Gasp! We're lying here. In fact, nobody really uses or cares about this
# string besides java UI toolkits; you can see several discussions on the
# mailing lists, GitHub issues, and other WM documentation that suggest setting
# this string if your java app doesn't work correctly. We may as well just lie
# and say that we're a working one by default.
#
# We choose LG3D to maximize irony: it is a 3D non-reparenting WM written in
# java that happens to be on java's whitelist.
wmname = "LG3D"