From 93531f44dd6dd5e9d0d7b4cdee86ced147fdf8e3 Mon Sep 17 00:00:00 2001 From: flifloo Date: Mon, 10 Feb 2020 11:09:07 +0100 Subject: [PATCH] Init commit --- .gitignore | 1 + config.py | 224 ++++++++++++++++++++++++++++++++++++++ qtile-venv-entry | 30 +++++ widget_custom/__init__.py | 1 + widget_custom/memory.py | 43 ++++++++ widget_custom/notify.py | 118 ++++++++++++++++++++ 6 files changed, 417 insertions(+) create mode 100644 .gitignore create mode 100644 config.py create mode 100755 qtile-venv-entry create mode 100644 widget_custom/__init__.py create mode 100644 widget_custom/memory.py create mode 100644 widget_custom/notify.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/config.py b/config.py new file mode 100644 index 0000000..010daa2 --- /dev/null +++ b/config.py @@ -0,0 +1,224 @@ +from libqtile.config import Key, Screen, Group, Drag, Click +from libqtile.lazy import lazy +from libqtile import layout, bar, widget, hook + +from typing import List # noqa: F401 +from os import getenv, environ, execl +from subprocess import run, Popen + +import widget_custom + +mod = "mod4" +newpath = getenv("PATH").replace("/opt/qtile/venv/bin", "") +exitvenv = f"env -u VIRTUAL_ENV PATH='{newpath}'" + + +def cmd_run(prompt, sudo=False): + name = "cmd" + if sudo: + name += "(root)" + def f(args): + if sudo: + args = f"sudo -A {args}" + Popen(f"{exitvenv} {args}", shell=True) + + prompt.start_input(name, f, "cmd") + + +def hard_restart(misc): + execl("/opt/qtile/qtile-venv-entry", " ") + + +keys = [ + # Switch between windows in current stack pane + Key([mod], "k", lazy.layout.down()), + Key([mod], "j", lazy.layout.up()), + + # Move windows up or down in current stack + Key([mod, "control"], "k", lazy.layout.shuffle_down()), + Key([mod, "control"], "j", lazy.layout.shuffle_up()), + + # Switch window focus to other pane(s) of stack + Key([mod], "Tab", lazy.layout.next()), + + # Swap panes of split stack + Key([mod, "shift"], "space", lazy.layout.rotate()), + + # 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([mod, "shift"], "Return", lazy.layout.toggle_split()), + + # Toggle between different layouts as defined below + Key([mod], "space", lazy.next_layout()), + Key([mod], "w", lazy.window.kill()), + + # Windows opacity + Key([mod, "shift"], "o", lazy.window.down_opacity()), + Key([mod, "shift"], "t", lazy.window.up_opacity()), + + # Qtile managment + Key([mod, "control"], "r", lazy.restart()), + Key([mod, "control"], "f", lazy.function(hard_restart)), + Key([mod, "control"], "q", lazy.shutdown()), + Key([mod, "control"], "s", lazy.spawn("systemctl poweroff")), + Key([mod, "control"], "v", lazy.spawn("xtrlock"), lazy.spawn("systemctl suspend")), + + # Run + Key([mod], "r", lazy.widget["prompt"].function(cmd_run)), + Key([mod, "shift"], "r", lazy.widget["prompt"].function(cmd_run, True)), + + # Lock + Key([mod], "l", lazy.spawn(f"xtrlock")), + Key([mod, "control"], "l", lazy.spawn(f"parrots -n 9 -l")), + Key([mod, "shift"], "l", lazy.spawn(f"xtrlock -b")), + + # Brightness + Key(["mod1"], "F7", lazy.spawn("xbrightness 0.5")), + Key(["mod1"], "F6", lazy.spawn("xbrightness +0.2")), + Key(["mod1"], "F5", lazy.spawn("xbrightness -0.2")), + + # Audio + Key([], "XF86AudioRaiseVolume", lazy.spawn("amixer -c 0 -D default -q set Master 2%+")), + Key([], "XF86AudioLowerVolume", lazy.spawn("amixer -c 0 -D default -q set Master 2%-")), + Key([], "XF86AudioMute", lazy.spawn("amixer -c 0 -D default -q set Master toggle")), + + # Apps + Key([mod, "mod1"], "k", lazy.spawn(f"{exitvenv} kitty")), + Key([mod, "mod1"], "v", lazy.spawn(f"{exitvenv} vivaldi")), + Key([mod, "mod1"], "d", lazy.spawn(f"{exitvenv} discord")), + Key([mod, "mod1"], "t", lazy.spawn(f"{exitvenv} telegram-desktop")), + + + # Notify + Key([mod, "mod1"], "Right", lazy.widget["notify"].next()), + Key([mod, "mod1"], "Left", lazy.widget["notify"].prev()), + Key([mod, "mod1"], "Down", lazy.widget["notify"].clear()), + +] + + +groups = [] +for i in [["F1", "\N{globe with meridians}"], ["F2", "\N{incoming envelope}"], ["F3", "\N{briefcase}"], ["F4", "\N{floppy disk}"], ["F5", "\N{bookmark tabs}"], ["F6", "\N{video game}"]]: + groups.append(Group(i[1])) + keys.extend([ + Key([mod], i[0], lazy.group[i[1]].toscreen()), + Key([mod, "shift"], i[0], lazy.window.togroup(i[1], switch_group=True)) + ]) + + +layouts = [ + layout.Max(), + layout.Stack(num_stacks=2), + # Try more layouts by unleashing below layouts. + # layout.Bsp(), + #layout.Columns(), + #layout.Matrix(), + #layout.MonadTall(), + #layout.MonadWide(), + #layout.RatioTile(), + #layout.Tile() + #layout.TreeTab(), + #layout.VerticalTile(), + #layout.Zoomy(), +] + + +widget_defaults = dict( + font='sans', + fontsize=12, + padding=3, +) +extension_defaults = widget_defaults.copy() + + +screens = [ + Screen( + top=bar.Bar( + [ + widget.CurrentLayoutIcon(), + widget.GroupBox(hide_unused=True), + widget.Prompt(), + widget.TaskList(txt_floating="🗗", txt_maximized="🗖", txt_minimized="🗕"), + widget.Systray(), + widget.Notify(), + widget.CheckUpdates(custom_command="apt list --upgradable", execute="sudo -A apt update", display_format="{updates}", colour_have_updates="ff7300", colour_no_updates="5eff00"), + widget.Sep(), + widget.CPU(format="{load_percent}%"), + widget_custom.Memory(), + widget.ThermalSensor(tag_sensor="Core 0"), + widget.Sep(), + widget.Wlan(disconnected_message="", interface="wlp1s0"), + widget.Net(format="{down}\u2193\u2191{up}"), + widget.Sep(), + #widget.BatteryIcon(battery="BATC"), + widget.Battery(charge_char="\N{electric plug}", discharge_char="\N{battery}", empty_char="\N{cross mark}", unknown_char="\N{question mark}", battery="BATC", format="{char}{percent:2.0%} {hour:d}:{min:02d}", low_percentage=0.35, hide_threshold=True), + widget.Volume(front="material-design-icons-iconfont",emoji=True), + widget.Clock(format='%d/%m/%Y %H:%M'), + ], + 24, + ), + ), +] + + +# 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 = True +cursor_warp = False +floating_layout = layout.Floating(float_rules=[ + {'wmclass': 'confirm'}, + {'wmclass': 'dialog'}, + {'wmclass': 'download'}, + {'wmclass': 'error'}, + {'wmclass': 'file_progress'}, + {'wmclass': 'notification'}, + {'wmclass': 'splash'}, + {'wmclass': 'toolbar'}, + {'wmclass': 'confirmreset'}, # gitk + {'wmclass': 'makebranch'}, # gitk + {'wmclass': 'maketag'}, # gitk + {'wname': 'branchdialog'}, # gitk + {'wname': 'pinentry'}, # GPG key password entry + {'wname': 'Onboard'}, + {'wmclass': 'ssh-askpass'}, # ssh-askpass +]) + +auto_fullscreen = True +focus_on_window_activation = "smart" + + +@hook.subscribe.client_new +def func(c): + if c.name == "Vivaldi - Vivaldi": + c.togroup("\N{globe with meridians}") + elif c.name in ["Discord", "Telegram"]: + c.togroup("\N{incoming envelope}") + elif c.name == "Typora": + c.togroup("\N{briefcase}") + elif c.name == "win0": + c.togroup("\N{floppy disk}") + elif c.name == "docs": + c.togroup("\N{bookmark tabs}") + elif c.name in ["Lutris", "Shadow"]: + c.togroup("\N{video game}") + + +def main(q): + Popen(["nextcloud", "--background"]) + Popen("kdeconnect-indicator") + +wmname = "LG3D" + diff --git a/qtile-venv-entry b/qtile-venv-entry new file mode 100755 index 0000000..36e88b4 --- /dev/null +++ b/qtile-venv-entry @@ -0,0 +1,30 @@ +#!/bin/bash + +export SUDO_ASKPASS=/usr/bin/ssh-askpass +export _JAVA_AWT_WM_NONREPARENTING=1 + +picom --vsync --backend glx -b +pulseaudio -D + +feh --bg-fill ~/Nextcloud/Images/Furry/Commis/Kat-LUK.J_/poster_etha.png +xbrightness 0.5 + +# Fix touchscreen +xinput set-prop "pointer:ELAN22A6:00 04F3:22A6" --type=float "Coordinate Transformation Matrix" 0 1 0 -1 0 1 0 0 1 +# setup touchpad parameters +touchpadid=`xinput list | grep Touchpad | cut -d= -f2 | cut -f1` +tapid=`xinput list-props $touchpadid | grep "Tapping Enabled" | grep -v "Default" | cut -d'(' -f2 | cut -d')' -f1` +middleid=`xinput list-props $touchpadid | grep "Middle Emulation Enabled" | grep -v "Default" | cut -d'(' -f2 | cut -d')' -f1` +scrollid=`xinput list-props $touchpadid | grep "Natural Scrolling Enabled" | grep -v "Default" | cut -d'(' -f2 | cut -d')' -f1` +speedid=`xinput list-props $touchpadid | grep "Coordinate Transformation Matrix" | grep -v "Default" | cut -d'(' -f2 | cut -d')' -f1` +disablewhiletypingid=`xinput list-props $touchpadid | grep "Disable While Typing Enabled" | grep -v "Default" | cut -d'(' -f2 | cut -d')' -f1` +xinput set-prop $touchpadid $tapid 1 +xinput set-prop $touchpadid $middleid 1 +xinput set-prop $touchpadid $scrollid 0 +xinput set-prop $touchpadid $speedid 5 0 0 0 5 0 0 0 2 +xinput set-prop $touchpadid $disablewhiletypingid 0 + + +source /opt/qtile/venv/bin/activate +python /opt/qtile/bin/qtile $* > /opt/qtile/qtile.log + diff --git a/widget_custom/__init__.py b/widget_custom/__init__.py new file mode 100644 index 0000000..f110900 --- /dev/null +++ b/widget_custom/__init__.py @@ -0,0 +1 @@ +from widget_custom.memory import Memory diff --git a/widget_custom/memory.py b/widget_custom/memory.py new file mode 100644 index 0000000..e320eca --- /dev/null +++ b/widget_custom/memory.py @@ -0,0 +1,43 @@ +import psutil + +from libqtile.widget import base + +__all__ = ["Memory"] + + +class Memory(base.ThreadedPollText): + """Displays memory/swap usage + + MemUsed: Returns memory in use + MemTotal: Returns total amount of memory + MemFree: Returns amount of memory free + Buffers: Returns buffer amount + Active: Returns active memory + Inactive: Returns inactive memory + Shmem: Returns shared memory + SwapTotal: Returns total amount of swap + SwapFree: Returns amount of swap free + SwapUsed: Returns amount of swap in use +""" + + orientations = base.ORIENTATION_HORIZONTAL + defaults = [ + ("format", "{MemUsed}%", "Formatting for field names."), + ("update_interval", 1.0, "Update interval for the Memory"), + ] + + def __init__(self, **config): + super().__init__(**config) + self.add_defaults(Memory.defaults) + + def tick(self): + self.update(self.poll()) + return self.update_interval + + def poll(self): + mem = psutil.virtual_memory() + swap = psutil.swap_memory() + val = {} + val["MemUsed"] = round((mem.used / mem.total) * 100) + return self.format.format(**val) + diff --git a/widget_custom/notify.py b/widget_custom/notify.py new file mode 100644 index 0000000..2d6b7e0 --- /dev/null +++ b/widget_custom/notify.py @@ -0,0 +1,118 @@ +from . import base +from .. import bar, utils, pangocffi +from libqtile.notify import notifier +from os import path + + +class Notify(base._TextBox): + """A notify widget""" + orientations = base.ORIENTATION_HORIZONTAL + defaults = [ + ("foreground_urgent", "ff0000", "Foreground urgent priority colour"), + ("foreground_low", "dddddd", "Foreground low priority colour"), + ( + "default_timeout", + None, + "Default timeout (seconds) for notifications" + ), + ("audiofile", None, "Audiofile played during notifications"), + ] + + def __init__(self, width=bar.CALCULATED, **config): + base._TextBox.__init__(self, "", width, **config) + self.add_defaults(Notify.defaults) + notifier.register(self.update) + self.current_id = 0 + + def _configure(self, qtile, bar): + base._TextBox._configure(self, qtile, bar) + self.layout = self.drawer.textlayout( + self.text, + self.foreground, + self.font, + self.fontsize, + self.fontshadow, + markup=True + ) + + def set_notif_text(self, notif): + self.text = pangocffi.markup_escape_text(notif.summary) + urgency = notif.hints.get('urgency', 1) + if urgency != 1: + self.text = '%s' % ( + utils.hex( + self.foreground_urgent if urgency == 2 + else self.foreground_low + ), + self.text + ) + if notif.body: + self.text = '%s - %s' % ( + self.text, pangocffi.markup_escape_text(notif.body) + ) + if self.audiofile and path.exists(self.audiofile): + self.qtile.cmd_spawn("aplay -q '%s'" % self.audiofile) + + def update(self, notif): + self.qtile.call_soon_threadsafe(self.real_update, notif) + + def real_update(self, notif): + self.set_notif_text(notif) + self.current_id = notif.id - 1 + if notif.timeout and notif.timeout > 0: + self.timeout_add(notif.timeout / 1000, self.clear) + elif self.default_timeout: + self.timeout_add(self.default_timeout, self.clear) + self.bar.draw() + return True + + def display(self): + self.set_notif_text(notifier.notifications[self.current_id]) + self.bar.draw() + + def clear(self): + self.text = '' + self.current_id = len(notifier.notifications) - 1 + self.bar.draw() + + def prev(self): + if self.current_id > 0: + self.current_id -= 1 + self.display() + + def next(self): + if self.current_id < len(notifier.notifications) - 1: + self.current_id += 1 + self.display() + + def button_press(self, x, y, button): + if button == 1: + self.clear() + elif button == 4: + self.prev() + elif button == 5: + self.next() + + def cmd_display(self): + """Display the notifcication""" + self.display() + + def cmd_clear(self): + """Clear the notification""" + self.clear() + + def cmd_toggle(self): + """Toggle showing/clearing the notification""" + if self.text == '': + self.display() + else: + self.clear() + + def cmd_prev(self): + """Show previous notification""" + self.prev() + + def cmd_next(self): + """Show next notification""" + self.next() +