summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Guschin <saintruler@gmail.com>2021-05-10 14:44:07 +0400
committerAndrew Guschin <saintruler@gmail.com>2021-05-10 14:44:07 +0400
commit40ce35bd2f379f2c30e51173752926f8c620ffd6 (patch)
treed8bb4e1651758edd2af52e4b0a4b40c3c0f13c0e
parent2f6db370d4086eca956279ea41cd305b474f9caf (diff)
Changed window manager to bspwm and added custom config for lemonbar
-rwxr-xr-x.config/bspwm/bspwmrc18
-rw-r--r--.config/sxhkd/sxhkdrc121
-rw-r--r--.local/bin/lemonbar/bargen.py72
-rw-r--r--.local/bin/lemonbar/bspc.py55
-rw-r--r--.local/bin/lemonbar/chain.py59
-rw-r--r--.local/bin/lemonbar/modules/battery.py11
-rw-r--r--.local/bin/lemonbar/modules/clock.py6
-rw-r--r--.local/bin/lemonbar/modules/language.py10
-rw-r--r--.local/bin/lemonbar/modules/updates.py48
-rw-r--r--.local/bin/lemonbar/modules/volume.py93
-rw-r--r--.local/bin/lemonbar/run_lemonbar.sh10
-rw-r--r--.profile3
-rwxr-xr-x.xinitrc2
-rw-r--r--.xprofile2
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
diff --git a/.profile b/.profile
index 2624bc8..5a428d7 100644
--- a/.profile
+++ b/.profile
@@ -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
diff --git a/.xinitrc b/.xinitrc
index cc07d5e..9509671 100755
--- a/.xinitrc
+++ b/.xinitrc
@@ -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
diff --git a/.xprofile b/.xprofile
index 1dd20c3..fd74714 100644
--- a/.xprofile
+++ b/.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"