mirror of
https://github.com/GRFreire/dotfiles.git
synced 2026-01-09 20:19:37 +00:00
506 lines
16 KiB
Python
506 lines
16 KiB
Python
import time
|
|
import os
|
|
import subprocess
|
|
import psutil
|
|
from typing import List # noqa: F401
|
|
|
|
from libqtile import bar, layout, widget, qtile, hook
|
|
from libqtile.config import Click, Drag, Group, ScratchPad, DropDown, Key, Match, Screen
|
|
from libqtile.lazy import lazy
|
|
|
|
MOD = "mod4"
|
|
ALT = "mod1"
|
|
TERMINAL = "alacritty"
|
|
FILE_MANAGER = "pcmanfm --no-desktop -n"
|
|
SCREENSHOT = "gnome-screenshot"
|
|
SCREENSHOT_UTILITY = "gnome-screenshot -i"
|
|
CALCULATOR = "gnome-calculator"
|
|
CALENDAR = "gnome-calendar"
|
|
SHOW_KEYBINDS = f"{TERMINAL} -t Keybinds -e python3 /home/grfreire/.config/qtile/list_keybinds.py"
|
|
POWER_MENU = os.path.expanduser(
|
|
"~/.scripts/bin/simple-power-menu"
|
|
)
|
|
|
|
MEDIA_CONTROL = os.path.expanduser("~/.config/qtile/media_control.sh")
|
|
keys = [
|
|
# Closes window.
|
|
Key([MOD], "q", lazy.window.kill(), desc="Kill focused window"),
|
|
# Switch between windows
|
|
Key([MOD], "h", lazy.layout.left(), desc="Move focus to left"),
|
|
Key([MOD], "l", lazy.layout.right(), desc="Move focus to right"),
|
|
Key([MOD], "j", lazy.layout.down(), desc="Move focus down"),
|
|
Key([MOD], "k", lazy.layout.up(), desc="Move focus up"),
|
|
Key([ALT], "Tab", lazy.layout.next(), desc="Move window focus to other window"),
|
|
# Move windows between left/right columns.
|
|
Key([MOD, "shift"], "h", lazy.layout.shuffle_left(), desc="Move window to the left"),
|
|
Key([MOD, "shift"], "l", lazy.layout.shuffle_right(), desc="Move window to the right"),
|
|
Key([MOD, "shift"], "j", lazy.layout.shuffle_down(), desc="Move window down"),
|
|
Key([MOD, "shift"], "k", lazy.layout.shuffle_up(), desc="Move window up"),
|
|
# Grow and shrink windows.
|
|
Key([MOD, "control"], "h", lazy.layout.shrink_main(), desc="Grow window to the left"),
|
|
Key([MOD, "control"], "l", lazy.layout.grow_main(), desc="Grow window to the right"),
|
|
Key([MOD, "control"], "j", lazy.layout.shrink_main(), desc="Grow window down"),
|
|
Key([MOD, "control"], "k", lazy.layout.grow_main(), desc="Grow window up"),
|
|
Key([MOD], "n", lazy.layout.normalize(), desc="Reset all window sizes"),
|
|
# Toggle between different layouts.
|
|
Key([MOD], "Tab", lazy.next_layout(), desc="Toggle between layouts"),
|
|
Key([MOD], "m", lazy.window.toggle_fullscreen(), desc="Toggle maximize state of the window"),
|
|
Key([MOD], "t", lazy.window.toggle_floating(), desc="Toggle floating state of the window"),
|
|
Key([MOD, "shift"], "t", lazy.function(lambda q: toggle_swallow(q.current_window)), desc="Toggle swallow state of the window"),
|
|
# QTile
|
|
Key([MOD, "control"], "r", lazy.restart(), desc="Restart Qtile"),
|
|
Key([MOD, "control"], "q", lazy.shutdown(), desc="Shutdown Qtile"),
|
|
Key([MOD], "slash", lazy.spawn(SHOW_KEYBINDS), desc="Show list of qtile keybinds"),
|
|
# System
|
|
Key([MOD], "BackSpace", lazy.spawn(POWER_MENU), desc="Open power menu"),
|
|
Key([], "Print", lazy.spawn(SCREENSHOT), desc="Take a screenshot of all the screens"),
|
|
Key([MOD], "Print", lazy.spawn(SCREENSHOT_UTILITY), desc="Open screenshot utility"),
|
|
# Media
|
|
Key([], "XF86AudioPlay", lazy.spawn(f"{MEDIA_CONTROL} play"), desc="Media control - play"),
|
|
Key([], "XF86AudioStop", lazy.spawn(f"{MEDIA_CONTROL} stop"), desc="Media control - stop"),
|
|
Key([], "XF86AudioNext", lazy.spawn(f"{MEDIA_CONTROL} next"), desc="Media control - next"),
|
|
Key([], "XF86AudioPrev", lazy.spawn(f"{MEDIA_CONTROL} prev"), desc="Media control - prev"),
|
|
Key([], "XF86AudioRaiseVolume", lazy.spawn(f"{MEDIA_CONTROL} vol_up"), desc="Media control - volume up"),
|
|
Key([], "XF86AudioLowerVolume", lazy.spawn(f"{MEDIA_CONTROL} vol_down"), desc="Media control - volume down"),
|
|
Key([], "XF86AudioMute", lazy.spawn(f"{MEDIA_CONTROL} vol_mute"), desc="Media control - mute volume"),
|
|
]
|
|
|
|
group_names = [
|
|
("WWW", {"layout": "monadtall"}),
|
|
("DEV", {"layout": "max"}),
|
|
("GMG", {"layout": "max"}),
|
|
("SYS", {"layout": "ratiotile"}),
|
|
("DOC", {"layout": "monadtall"}),
|
|
("CHAT", {"layout": "max"}),
|
|
("MUS", {"layout": "max"}),
|
|
("VID", {"layout": "monadtall"}),
|
|
("EDT", {"layout": "max"}),
|
|
]
|
|
|
|
scratch_pads = [
|
|
[
|
|
DropDown("calculator", CALCULATOR, x=0.75, y=0.45, width=0.2, height=0.4, opacity=0.9, on_focus_lost_hide=False),
|
|
[[MOD], "c"]
|
|
],
|
|
[
|
|
DropDown("filemanager", FILE_MANAGER, x=0.65, y=0.1, width=0.35, height=0.8, opacity=0.9, on_focus_lost_hide=False),
|
|
[[MOD, "control"], "f"]
|
|
]
|
|
]
|
|
|
|
groups = [Group(name, **kwargs) for name, kwargs in group_names]
|
|
|
|
for i, (name, kwargs) in enumerate(group_names, 1):
|
|
# Switch to another group
|
|
keys.append(Key([MOD], str(i), lazy.group[name].toscreen(), desc=f"Switch to group {name}"))
|
|
|
|
# Send current window to another group
|
|
keys.append(Key([MOD, "shift"], str(i), lazy.window.togroup(name), desc=f"Send current window to group {name}"))
|
|
|
|
sp_groups = []
|
|
sp_keys = []
|
|
for sp in scratch_pads:
|
|
sp_groups.append(sp[0])
|
|
sp_keys.append(sp[1])
|
|
|
|
groups.append(ScratchPad("scratchpad", sp_groups))
|
|
|
|
for i, (mod, k) in enumerate(sp_keys, 0):
|
|
name = sp_groups[i].name
|
|
keys.append(Key(mod, *k, lazy.group['scratchpad'].dropdown_toggle(name), desc=f"Open {name} scratchpad"))
|
|
|
|
colors = {
|
|
"background": "#1A1B26",
|
|
"background_highlight": "#24283B",
|
|
"text": "#D4D8EA",
|
|
"accent_color_dark": "#1B294B",
|
|
"accent_color_light": "#495C88",
|
|
"accent_highlight": "#203769",
|
|
"active": "#7AA2F7",
|
|
"inactive": "#565f89",
|
|
}
|
|
|
|
layout_theme = {
|
|
"border_width": 2,
|
|
"margin": 6,
|
|
"border_focus": colors["accent_color_light"],
|
|
"border_normal": colors["background"],
|
|
}
|
|
|
|
layouts = [
|
|
layout.MonadTall(**layout_theme),
|
|
layout.Max(**layout_theme),
|
|
layout.MonadWide(**layout_theme),
|
|
layout.RatioTile(**layout_theme),
|
|
]
|
|
|
|
FONT = "Ubuntu"
|
|
widget_defaults = dict(
|
|
font=FONT,
|
|
fontsize=14,
|
|
padding=4,
|
|
background=colors["background"],
|
|
foreground=colors["text"],
|
|
)
|
|
extension_defaults = widget_defaults.copy()
|
|
|
|
|
|
def generate_bar(left=[], right=[]):
|
|
current_bg_color = colors["background"]
|
|
current_fg_color = colors["text"]
|
|
|
|
widgets = []
|
|
|
|
def create_sep(width=10):
|
|
widgets.append(
|
|
widget.Sep(
|
|
padding=0,
|
|
size_percent=100,
|
|
linewidth=width,
|
|
foreground=current_bg_color,
|
|
)
|
|
)
|
|
|
|
def create_arrow(bg=current_bg_color, fg=current_fg_color):
|
|
widgets.append(
|
|
widget.Sep(
|
|
padding=0,
|
|
size_percent=100,
|
|
linewidth=10,
|
|
background=fg,
|
|
foreground=bg,
|
|
)
|
|
)
|
|
widgets.append(
|
|
widget.TextBox(
|
|
text="", background=bg, foreground=fg, padding=0, fontsize=60
|
|
)
|
|
)
|
|
|
|
create_sep(4)
|
|
|
|
for w in left:
|
|
widgets.append(w)
|
|
|
|
create_sep(4)
|
|
|
|
is_even = True
|
|
is_first_widget = True
|
|
for g in right:
|
|
if is_even:
|
|
current_bg_color = colors["accent_color_dark"]
|
|
is_even = False
|
|
else:
|
|
current_bg_color = colors["accent_color_light"]
|
|
is_even = True
|
|
|
|
if is_first_widget:
|
|
create_arrow(colors["background"], colors["accent_color_dark"])
|
|
|
|
is_first_widget = False
|
|
else:
|
|
create_arrow(
|
|
colors["accent_color_dark"]
|
|
if is_even
|
|
else colors["accent_color_light"],
|
|
colors["accent_color_light"]
|
|
if is_even
|
|
else colors["accent_color_dark"],
|
|
)
|
|
|
|
for w in g:
|
|
w.background = current_bg_color
|
|
w.foreground = current_fg_color
|
|
widgets.append(w)
|
|
|
|
create_sep()
|
|
|
|
return widgets
|
|
|
|
|
|
def group_box():
|
|
return widget.GroupBox(
|
|
disable_drag=True,
|
|
use_mouse_wheel=False,
|
|
font=FONT + " Bold",
|
|
fontsize=10,
|
|
margin_y=6,
|
|
margin_x=0,
|
|
padding=5,
|
|
borderwidth=3,
|
|
highlight_method="line",
|
|
rounded=False,
|
|
active=colors["active"],
|
|
inactive=colors["text"],
|
|
highlight_color=colors["background_highlight"],
|
|
this_current_screen_border=colors["active"],
|
|
this_screen_border=colors["accent_highlight"],
|
|
other_current_screen_border=colors["active"],
|
|
other_screen_border=colors["accent_highlight"],
|
|
foreground=colors["text"],
|
|
background=colors["background"],
|
|
)
|
|
|
|
|
|
def main_bar():
|
|
return generate_bar(
|
|
left=[
|
|
group_box(),
|
|
widget.Sep(
|
|
linewidth=8,
|
|
background=colors["background"],
|
|
foreground=colors["background"],
|
|
),
|
|
widget.WindowName(),
|
|
widget.Systray(padding=10),
|
|
],
|
|
right=[
|
|
[
|
|
widget.TextBox(text="⟳", fontsize=18),
|
|
widget.CheckUpdates(
|
|
distro="Arch", custom_command="checkupdates", display_format="{updates} Updates"
|
|
),
|
|
],
|
|
[
|
|
widget.CurrentLayoutIcon(scale=0.75),
|
|
widget.CurrentLayout(),
|
|
],
|
|
[
|
|
widget.Memory(format='Memory {MemPercent}%'),
|
|
],
|
|
[
|
|
widget.TextBox(
|
|
text="Volume:",
|
|
mouse_callbacks={
|
|
"Button1": lambda: qtile.cmd_spawn(
|
|
"amixer -q -D pulse sset Master toggle"
|
|
)
|
|
},
|
|
),
|
|
widget.Volume(mute_command="amixer -q -D pulse sset Master toggle"),
|
|
],
|
|
[
|
|
widget.Clock(
|
|
format="%d / %m / %y",
|
|
mouse_callbacks={"Button1": lambda: qtile.cmd_spawn(CALENDAR)},
|
|
),
|
|
widget.Clock(format="%I:%M %p", font=FONT + " Bold"),
|
|
],
|
|
[
|
|
widget.TextBox(
|
|
text="⏻",
|
|
fontsize=22,
|
|
mouse_callbacks={"Button1": lambda: qtile.cmd_spawn(POWER_MENU)},
|
|
),
|
|
],
|
|
],
|
|
)
|
|
|
|
|
|
def aux_bar():
|
|
return generate_bar(
|
|
left=[
|
|
widget.CurrentLayoutIcon(scale=0.65),
|
|
group_box(),
|
|
widget.Sep(
|
|
linewidth=8,
|
|
background=colors["background"],
|
|
foreground=colors["background"],
|
|
),
|
|
widget.WindowName(),
|
|
widget.Memory(format='Memory {MemPercent}%'),
|
|
widget.ThermalSensor(fmt='Temp: {}'),
|
|
widget.CPU(format='CPU {load_percent}%'),
|
|
widget.Sep(
|
|
linewidth=8,
|
|
background=colors["background"],
|
|
foreground=colors["background"],
|
|
),
|
|
widget.Clock(
|
|
format="%d / %m / %y",
|
|
mouse_callbacks={"Button1": lambda: qtile.cmd_spawn(CALENDAR)},
|
|
),
|
|
widget.Clock(format="%I:%M %p", font=FONT + " Bold"),
|
|
]
|
|
)
|
|
|
|
|
|
screens = [
|
|
Screen(top=bar.Bar(main_bar(), 28)),
|
|
Screen(top=bar.Bar(aux_bar(), 28)),
|
|
]
|
|
|
|
# Drag floating layouts.
|
|
mouse = [
|
|
Drag(
|
|
[MOD],
|
|
"Button1",
|
|
lazy.window.set_position_floating(),
|
|
start=lazy.window.get_position(),
|
|
),
|
|
Drag(
|
|
[MOD], "Button3", lazy.window.set_size_floating(), start=lazy.window.get_size()
|
|
),
|
|
Click([MOD], "Button2", lazy.window.bring_to_front()),
|
|
]
|
|
|
|
dgroups_key_binder = None
|
|
dgroups_app_rules = [] # type: List
|
|
follow_mouse_focus = True
|
|
bring_front_click = False
|
|
cursor_warp = False
|
|
|
|
floating_layout = layout.Floating(
|
|
**layout_theme,
|
|
float_rules=[
|
|
# Run the utility of `xprop` to see the wm class and name of an X client.
|
|
*layout.Floating.default_float_rules,
|
|
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(wm_class="pinentry-gtk-2"), # GPG key password entry
|
|
Match(wm_class="gnome-calculator"), # Calculator
|
|
Match(wm_class="bitwarden"), # Bitwarden
|
|
Match(wm_class="pavucontrol"), # Audio mixer
|
|
Match(wm_class="gnome-calendar"), # Calendar
|
|
Match(wm_class="yad"), # Yad
|
|
Match(title="Krita - Edit Text — Krita"), # Keybinds window
|
|
Match(title="Keybinds"), # Keybinds window
|
|
]
|
|
)
|
|
|
|
# Exceptions rules to disable floating in default config
|
|
@hook.subscribe.client_new
|
|
def disable_floating(window):
|
|
rules = [
|
|
Match(wm_class="mpv")
|
|
]
|
|
|
|
if any(window.match(rule) for rule in rules):
|
|
window.togroup(qtile.current_group.name)
|
|
window.cmd_disable_floating()
|
|
|
|
auto_fullscreen = True
|
|
focus_on_window_activation = "smart"
|
|
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
|
|
|
|
|
|
@hook.subscribe.startup_once
|
|
def autostart():
|
|
autostart_script = os.path.expanduser("~/.config/qtile/autostart.sh")
|
|
subprocess.call([autostart_script])
|
|
|
|
@hook.subscribe.restart
|
|
def restart_hook():
|
|
os.system(os.path.expanduser("~/.config/qtile/on_restart.sh"))
|
|
|
|
|
|
@hook.subscribe.client_new
|
|
def try_swallow(window):
|
|
swallow_from=[
|
|
Match(wm_class="Alacritty"),
|
|
]
|
|
|
|
not_swallow=[]
|
|
|
|
if any(window.match(rule) for rule in not_swallow):
|
|
return
|
|
|
|
if hasattr(window, 'parent'):
|
|
swallow(window, window.parent)
|
|
return
|
|
|
|
pid = window.window.get_net_wm_pid() # Window PID
|
|
ppid = psutil.Process(pid).ppid() # Parent Window PID
|
|
cpids = {c.window.get_net_wm_pid(): wid for wid, c in window.qtile.windows_map.items()} # All Windows PIDs
|
|
for _ in range(len(cpids)):
|
|
if not ppid:
|
|
return
|
|
if ppid in cpids:
|
|
parent = window.qtile.windows_map.get(cpids[ppid])
|
|
if not any(parent.match(rule) for rule in swallow_from):
|
|
return
|
|
|
|
swallow(window, parent)
|
|
return
|
|
ppid = psutil.Process(ppid).ppid()
|
|
|
|
|
|
def swallow(window, parent):
|
|
parent.minimized = True
|
|
window.parent = parent
|
|
|
|
@hook.subscribe.client_killed
|
|
def try_unswallow(window):
|
|
if hasattr(window, 'parent'):
|
|
unswallow(window.parent)
|
|
|
|
def unswallow(parent):
|
|
parent.minimized = False
|
|
|
|
def toggle_swallow(window):
|
|
if hasattr(window, 'parent'):
|
|
if window.parent.minimized:
|
|
unswallow(window.parent)
|
|
else:
|
|
swallow(window, window.parent)
|
|
|
|
@hook.subscribe.client_new
|
|
def try_regroup_window(client):
|
|
time.sleep(0.01)
|
|
apps_wm_class = {
|
|
"google-chrome": "WWW",
|
|
"Navigator": "WWW",
|
|
"code": "DEV",
|
|
"Steam": "GMG",
|
|
"heroic": "GMG",
|
|
"lutris": "GMG",
|
|
"nitrogen": "SYS",
|
|
"VirtualBox Manager": "SYS",
|
|
"virt-manager": "SYS",
|
|
"baobab": "SYS",
|
|
"gnome-disks": "SYS",
|
|
"gedit": "DOC",
|
|
"discord": "CHAT",
|
|
"microsoft teams - preview": "CHAT",
|
|
"spotify": "MUS",
|
|
"obs": "VID",
|
|
"ghb": "VID",
|
|
"resolve": "EDT",
|
|
"audacity": "EDT",
|
|
"krita": "EDT",
|
|
"mypaint": "EDT",
|
|
}
|
|
|
|
# Notion for class is notion-nativefier-xxxx for example
|
|
apps_wm_class_incomplete = {
|
|
"notion": "DOC",
|
|
"todoist": "DOC",
|
|
}
|
|
|
|
wm_class = client.window.get_wm_class()[0]
|
|
group = apps_wm_class.get(wm_class, None)
|
|
if group:
|
|
client.togroup(group, switch_group=True)
|
|
else:
|
|
for key in apps_wm_class_incomplete:
|
|
if key in wm_class:
|
|
client.togroup(apps_wm_class_incomplete[key], switch_group=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"
|