diff options
| author | Andrew Guschin <saintruler@gmail.com> | 2021-05-10 14:44:07 +0400 |
|---|---|---|
| committer | Andrew Guschin <saintruler@gmail.com> | 2021-05-10 14:44:07 +0400 |
| commit | 40ce35bd2f379f2c30e51173752926f8c620ffd6 (patch) | |
| tree | d8bb4e1651758edd2af52e4b0a4b40c3c0f13c0e | |
| parent | 2f6db370d4086eca956279ea41cd305b474f9caf (diff) | |
Changed window manager to bspwm and added custom config for lemonbar
| -rwxr-xr-x | .config/bspwm/bspwmrc | 18 | ||||
| -rw-r--r-- | .config/sxhkd/sxhkdrc | 121 | ||||
| -rw-r--r-- | .local/bin/lemonbar/bargen.py | 72 | ||||
| -rw-r--r-- | .local/bin/lemonbar/bspc.py | 55 | ||||
| -rw-r--r-- | .local/bin/lemonbar/chain.py | 59 | ||||
| -rw-r--r-- | .local/bin/lemonbar/modules/battery.py | 11 | ||||
| -rw-r--r-- | .local/bin/lemonbar/modules/clock.py | 6 | ||||
| -rw-r--r-- | .local/bin/lemonbar/modules/language.py | 10 | ||||
| -rw-r--r-- | .local/bin/lemonbar/modules/updates.py | 48 | ||||
| -rw-r--r-- | .local/bin/lemonbar/modules/volume.py | 93 | ||||
| -rw-r--r-- | .local/bin/lemonbar/run_lemonbar.sh | 10 | ||||
| -rw-r--r-- | .profile | 3 | ||||
| -rwxr-xr-x | .xinitrc | 2 | ||||
| -rw-r--r-- | .xprofile | 2 |
14 files changed, 507 insertions, 3 deletions
diff --git a/.config/bspwm/bspwmrc b/.config/bspwm/bspwmrc new file mode 100755 index 0000000..ab87d7f --- /dev/null +++ b/.config/bspwm/bspwmrc @@ -0,0 +1,18 @@ +#! /bin/sh + +pgrep -x sxhkd > /dev/null || sxhkd & + +bspc monitor -d I II III IV V VI VII VIII IX X + +bspc config border_width 2 +bspc config window_gap 12 + +bspc config split_ratio 0.52 +bspc config borderless_monocle true +bspc config gapless_monocle true + +bspc rule -a Gimp desktop='^8' state=floating follow=on +bspc rule -a Chromium desktop='^2' +bspc rule -a mplayer2 state=floating +bspc rule -a Kupfer.py focus=on +bspc rule -a Screenkey manage=off diff --git a/.config/sxhkd/sxhkdrc b/.config/sxhkd/sxhkdrc new file mode 100644 index 0000000..36f0f64 --- /dev/null +++ b/.config/sxhkd/sxhkdrc @@ -0,0 +1,121 @@ +# +# wm independent hotkeys +# + +# terminal emulator +super + Return + tabbed -c -r 2 st -w '' + +# program launcher +super + p + rofi -theme solarized_alternate -show drun + +# make sxhkd reload its configuration files: +super + Escape + pkill -USR1 -x sxhkd + +# +# bspwm hotkeys +# + +# quit/restart bspwm +super + shift + {e,r} + bspc {quit,wm -r} + +# close and kill +super + {_,shift + }w + bspc node -{c,k} + +# alternate between the tiled and monocle layout +super + m + bspc desktop -l next + +# send the newest marked node to the newest preselected node +super + y + bspc node newest.marked.local -n newest.!automatic.local + +# swap the current node and the biggest window +super + g + bspc node -s biggest.window + +# +# state/flags +# + +# set the window state +super + {t,shift + t,s,f} + bspc node -t {tiled,pseudo_tiled,floating,fullscreen} + +# set the node flags +super + ctrl + {m,x,y,z} + bspc node -g {marked,locked,sticky,private} + +# +# focus/swap +# + +# focus the node in the given direction +super + {_,shift + }{h,j,k,l} + bspc node -{f,s} {west,south,north,east} + +# focus the node for the given path jump +super + {p,b,comma,period} + bspc node -f @{parent,brother,first,second} + +# focus the next/previous window in the current desktop +super + {_,shift + }c + bspc node -f {next,prev}.local.!hidden.window + +# focus the next/previous desktop in the current monitor +super + bracket{left,right} + bspc desktop -f {prev,next}.local + +# focus the last node/desktop +super + {grave,Tab} + bspc {node,desktop} -f last + +# focus the older or newer node in the focus history +super + {o,i} + bspc wm -h off; \ + bspc node {older,newer} -f; \ + bspc wm -h on + +# focus or send to the given desktop +super + {_,shift + }{1-9,0} + bspc {desktop -f,node -d} '^{1-9,10}' + +# +# preselect +# + +# preselect the direction +super + ctrl + {h,j,k,l} + bspc node -p {west,south,north,east} + +# preselect the ratio +super + ctrl + {1-9} + bspc node -o 0.{1-9} + +# cancel the preselection for the focused node +super + ctrl + space + bspc node -p cancel + +# cancel the preselection for the focused desktop +super + ctrl + shift + space + bspc query -N -d | xargs -I id -n 1 bspc node id -p cancel + +# +# move/resize +# + +# expand a window by moving one of its side outward +super + alt + {h,j,k,l} + bspc node -z {left -20 0,bottom 0 20,top 0 -20,right 20 0} + +# contract a window by moving one of its side inward +super + alt + shift + {h,j,k,l} + bspc node -z {right -20 0,top 0 20,bottom 0 -20,left 20 0} + +# move a floating window +super + {Left,Down,Up,Right} + bspc node -v {-20 0,0 20,0 -20,20 0} diff --git a/.local/bin/lemonbar/bargen.py b/.local/bin/lemonbar/bargen.py new file mode 100644 index 0000000..a9d3c4d --- /dev/null +++ b/.local/bin/lemonbar/bargen.py @@ -0,0 +1,72 @@ +from chain import ArrowModuleChain +from modules import volume, battery, updates, language, clock +import bspc + + +def get_desktops_bar(): + bar = [] + focused_bg = idle_fg = urgent_fg = occupied_fg = "#ebdbb2" + focused_fg = idle_bg = "#282828" + urgent_bg = "#cc241d" + occupied_bg = "#928374" + + for i, desktop in enumerate(bspc.get_desktops(), 1): + if desktop.focused: + fg = focused_fg + bg = focused_bg + elif desktop.urgent: + fg = urgent_fg + bg = urgent_bg + elif desktop.occupied: + fg = occupied_fg + bg = occupied_bg + else: + fg = idle_fg + bg = idle_bg + + bar.append({ + "callback": lambda idx=i: str(idx), + "foreground": fg, + "background": bg, + }) + + return bar + + +status_modules = [ + { + "callback": updates.callback, + "foreground": "#ebdbb2", + "background": "#689d6a", + }, + { + "callback": volume.callback, + "foreground": "#ebdbb2", + "background": "#458588", + }, + { + "callback": battery.callback, + "foreground": "#ebdbb2", + "background": "#b16286", + }, + { + "callback": language.callback, + "foreground": "#ebdbb2", + "background": "#98971a", + }, + { + "callback": clock.callback, + "foreground": "#ebdbb2", + "background": "#cc241d", + }, +] + +desktops = ArrowModuleChain(right=True, capped_left=True, capped_right=False) +desktops.extend(*get_desktops_bar()) + +focused_window = bspc.get_focused_window_name() + +status = ArrowModuleChain(right=False, capped_left=False, capped_right=True) +status.extend(*status_modules) + +print(f"%{{l}}{desktops} %{{F#ebdbb2}}{focused_window} %{{r}}{status}%{{B#000000}}") diff --git a/.local/bin/lemonbar/bspc.py b/.local/bin/lemonbar/bspc.py new file mode 100644 index 0000000..f2b3cb2 --- /dev/null +++ b/.local/bin/lemonbar/bspc.py @@ -0,0 +1,55 @@ +from subprocess import run as _run + + +class Desktop: + def __init__(self): + self.id = None + self.name = None + self.focused = False + self.occupied = False + self.urgent = False + + +def run(command): + return _run(command.split(), capture_output=True).stdout.decode().strip() + + +def get_desktops(): + focused = run("bspc query -D -d .focused") + occupied = run("bspc query -D -d .occupied").split("\n") + urgent = run("bspc query -D -d .urgent").split("\n") + + desktops = [] + for desktop_id in run("bspc query -D").split("\n"): + desktop = Desktop() + desktop.id = desktop_id + desktop.name = run(f"bspc query -D -d {desktop.id} --names") + desktop.focused = desktop.id == focused + desktop.occupied = desktop.id in occupied + desktop.urgent = desktop.id in urgent + + desktops.append(desktop) + + return desktops + + +def get_focused_window(): + return run("bspc query -N -n") + + +def cut_name(name): + if len(name) > 70: + return name[:65] + "..." + else: + return name + + +def get_focused_window_name(): + focused = get_focused_window().lower() + for window in run("wmctrl -l").split("\n"): + wid, _, _, name = window.split(maxsplit=3) + if wid.lower() == focused: + return cut_name(name) + return '' + + diff --git a/.local/bin/lemonbar/chain.py b/.local/bin/lemonbar/chain.py new file mode 100644 index 0000000..c825501 --- /dev/null +++ b/.local/bin/lemonbar/chain.py @@ -0,0 +1,59 @@ +class ArrowModuleChain: + def __init__(self, right, background="#000000", capped_left=False, capped_right=False): + self.sep = "\uE0B0" if right else "\uE0B2" + + self.bg = background + self.right = right + + self.chain = [] + self.modules = [] + + self.capped_left = capped_left + self.capped_right = capped_right + + def append(self, module): + if len(self.modules) > 0: + self.chain.pop() + fg = module["background"] + bg = self.modules[-1]["background"] + else: + bg = module["background"] if self.capped_left else self.bg + fg = module["background"] + + if not self.right: + bg, fg = fg, bg + self.chain.append(self.get_sep(bg, fg)) + + bg = module["background"] if self.capped_right else self.bg + fg = module["background"] + + if not self.right: + bg, fg = fg, bg + + self.chain.append(self.get_sep(fg, bg)) + + self.modules.append(module) + + def extend(self, *modules): + for module in modules: + self.append(module) + + def get_sep(self, fg, bg): + return f"%{{F{fg}}}%{{B{bg}}}{self.sep}" + + def __str__(self): + result = [] + for i in range(len(self.modules)): + result.append(self.chain[i]) + + bg = self.modules[i]["background"] + fg = self.modules[i]["foreground"] + text = self.modules[i]["callback"]() + text = f"%{{F{fg}}}%{{B{bg}}} {text} " + result.append(text) + + if len(self.chain) > 0: + result.append(self.chain[-1]) + + return "".join(result) + diff --git a/.local/bin/lemonbar/modules/battery.py b/.local/bin/lemonbar/modules/battery.py new file mode 100644 index 0000000..cb93f03 --- /dev/null +++ b/.local/bin/lemonbar/modules/battery.py @@ -0,0 +1,11 @@ +def read_file(filename): + with open(filename) as f: + return f.read().strip() + +def callback(): + battery = "BAT0" + now = int(read_file(f"/sys/class/power_supply/{battery}/energy_now")) + full = int(read_file(f"/sys/class/power_supply/{battery}/energy_full")) + percent = round(now / full * 100) + return f"BAT: {percent}%" + diff --git a/.local/bin/lemonbar/modules/clock.py b/.local/bin/lemonbar/modules/clock.py new file mode 100644 index 0000000..c4dc713 --- /dev/null +++ b/.local/bin/lemonbar/modules/clock.py @@ -0,0 +1,6 @@ +from time import strftime + + +def callback(): + clock = strftime("%d %b (%a) %H:%M") + return f"CLK: {clock}" diff --git a/.local/bin/lemonbar/modules/language.py b/.local/bin/lemonbar/modules/language.py new file mode 100644 index 0000000..50058c8 --- /dev/null +++ b/.local/bin/lemonbar/modules/language.py @@ -0,0 +1,10 @@ +from subprocess import run as _run + + +def run(command): + return _run(command.split(), capture_output=True).stdout.decode().strip() + + +def callback(): + name = run('xkblayout-state print "%s"').strip('"') + return f"LNG: {name.upper()}" diff --git a/.local/bin/lemonbar/modules/updates.py b/.local/bin/lemonbar/modules/updates.py new file mode 100644 index 0000000..67c8e93 --- /dev/null +++ b/.local/bin/lemonbar/modules/updates.py @@ -0,0 +1,48 @@ +from subprocess import run as _run +from time import time + + +def run(command): + return _run(command.split(), capture_output=True).stdout.decode().strip() + + +def pacman_count(): + cnt = 0 + for line in run('pacman -Qu').split("\n"): + if "[ignored]" not in line: + cnt += 1 + return cnt + + +def yay_count(): + cnt = 0 + for line in run('yay -Qau').split("\n"): + if "[ignored]" not in line: + cnt += 1 + return cnt + + +def callback(): + tmp_file = "/tmp/lemonbar/updates" + try: + with open(tmp_file) as f: + data = f.read().split() + except FileNotFoundError: + data = '' + + try: + timestamp = float(data[0]) + except (ValueError, IndexError): + timestamp = 0 + + if time() - timestamp > 900: + pacman = pacman_count() + aur = yay_count() + with open(tmp_file, "w") as f: + f.write(f"{time()} {pacman} {aur}") + else: + pacman = data[1] + aur = data[2] + + return f"UPD: {pacman}+{aur}" + diff --git a/.local/bin/lemonbar/modules/volume.py b/.local/bin/lemonbar/modules/volume.py new file mode 100644 index 0000000..f911571 --- /dev/null +++ b/.local/bin/lemonbar/modules/volume.py @@ -0,0 +1,93 @@ +from subprocess import run as _run +import re + + +def run(command): + return _run(command.split(), capture_output=True).stdout.decode().strip() + + +def get_default_sink(): + default_name = None + for line in run("pactl info").split("\n"): + key, val = line.split(": ") + if key == "Default Sink": + default_name = val + break + + if default_name is None: + return None + + for sid, sink in get_sinks().items(): + if sink["Name"] == default_name: + return sink + return None + + +def get_sinks(): + SINK, PARAMS, PARAM = 0, 1, 2 + parsing = SINK + + re_param_str = re.compile("(.+?): (.*)") + re_param_list = re.compile("(.+?):") + + current_sink = None + current_param = None + + sinks = {} + for line in run("pactl list sinks").split("\n"): + indent = line.count("\t") + line = line.strip() + + if parsing == SINK: + if line.startswith("Sink"): + n = line.split()[1] + sinks[n] = {} + current_sink = sinks[n] + + parsing = PARAMS + + elif parsing == PARAMS: + if line == '': + parsing = SINK + continue + + match = re_param_str.match(line) + if indent == 1 and match is not None: + name, value = match.groups() + current_param = name + current_sink[name] = value + continue + + match = re_param_list.match(line) + if indent == 1 and match is not None: + name = match.group(1) + current_param = name + current_sink[name] = [] + continue + + value = current_sink[name] + if isinstance(value, str): + current_sink[name] = [value] + + current_sink[name].append(line) + + return sinks + + +def get_sink_volume(sink): + if sink is None: + return "None" + if sink["Mute"] == "yes": + return "Muted" + + match = re.search(r"(\d+?)%", sink["Volume"][0]) + if match is not None: + return f"{match.groups()[0]}%" + + +def callback(): + def_sink = get_default_sink() + vol = get_sink_volume(def_sink) + + return f"VOL: {vol}" + diff --git a/.local/bin/lemonbar/run_lemonbar.sh b/.local/bin/lemonbar/run_lemonbar.sh new file mode 100644 index 0000000..3212fbf --- /dev/null +++ b/.local/bin/lemonbar/run_lemonbar.sh @@ -0,0 +1,10 @@ +fontsize="16" +font="DejaVu Sans Mono:style=Bold,size=${fontsize}" +iconsfont="xos4 Terminess Powerline:size=${fontsize}" + +rm -rf /tmp/lemonbar +mkdir /tmp/lemonbar +while :; do + printf "%s\n" "$(python3 bargen.py)" + sleep 0.5 +done | lemonbar -f "${font}" -f "${iconsfont}" -p @@ -3,7 +3,8 @@ if [ -d "$HOME/.local/bin" ] ; then PATH="$PATH:$(du "$HOME/.local/bin/" | cut -f2 | paste -sd ':')" fi -export SHELL="/bin/bash" +export SHELL="/bin/zsh" +export EDITOR="nvim" # For Jetbrains products export _JAVA_AWT_WM_NONREPARENTING=1 @@ -4,7 +4,7 @@ # Otherwise, display manager should set up this variable and # source the xprofile. -export DESKTOP_SESSION="xmonad" +export DESKTOP_SESSION="bspwm" [ -f /etc/xprofile ] && . /etc/xprofile [ -f ~/.xprofile ] && . ~/.xprofile @@ -2,6 +2,7 @@ if [ "$DESKTOP_SESSION" = "openbox-session" ] || \ [ "$DESKTOP_SESSION" = "xmonad" ] || \ + [ "$DESKTOP_SESSION" = "bspwm" ] || \ [ "$DESKTOP_SESSION" = "dwm" ]; then setxkbmap -layout us,ru -option "grp:alt_shift_toggle" @@ -35,5 +36,4 @@ fi fi export TERMINAL="tabbed -c -r 2 st -w ''" -export EDITOR="nvim" export BROWSER="firefox" |