From ca47c8a8a2cd340c01803acea7e437eff75a3287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geoffrey=20=E2=80=9CFrogeye=E2=80=9D=20Preud=27homme?= Date: Tue, 21 Aug 2018 18:10:57 +0200 Subject: [PATCH] lemonbar --- config/i3/bg.jpg | Bin config/lemonbar/bar.py | 157 ++++++++++++++++++++++++ config/lemonbar/pip.py | 266 +++++++++++++++++++++++++++++++++++++++++ gitconfig | 3 +- vimrc | 2 +- 5 files changed, 426 insertions(+), 2 deletions(-) mode change 100755 => 100644 config/i3/bg.jpg create mode 100755 config/lemonbar/bar.py create mode 100755 config/lemonbar/pip.py diff --git a/config/i3/bg.jpg b/config/i3/bg.jpg old mode 100755 new mode 100644 diff --git a/config/lemonbar/bar.py b/config/lemonbar/bar.py new file mode 100755 index 0000000..edeb089 --- /dev/null +++ b/config/lemonbar/bar.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 + +import i3ipc +import os +import psutil +# import alsaaudio +from time import time +import subprocess + +i3 = i3ipc.Connection() +lemonbar = subprocess.Popen(['lemonbar', '-b'], stdin=subprocess.PIPE) + +# Utils +def upChart(p): + block = ' ▁▂▃▄▅▆▇█' + return block[round(p * (len(block)-1))] + +def humanSizeOf(num, suffix='B'): # TODO Credit + for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']: + if abs(num) < 1024.0: + return "%3.0f%2s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.0f%2s%s" % (num, 'Yi', suffix) + +# Values +mode = '' +container = i3.get_tree().find_focused() +workspaces = i3.get_workspaces() +outputs = i3.get_outputs() + +username = os.environ['USER'] +hostname = os.environ['HOSTNAME'] +if '-' in hostname: + hostname = hostname.split('-')[-1] + +oldNetIO = dict() +oldTime = time() + +def update(): + activeOutputs = sorted(sorted(list(filter(lambda o: o.active, outputs)), key=lambda o: o.rect.y), key=lambda o: o.rect.x) + z = '' + for aOutput in range(len(activeOutputs)): + output = activeOutputs[aOutput] + # Mode || Workspaces + t = [] + if (mode != ''): + t.append(mode) + else: + t.append(' '.join([(w.name.upper() if w.focused else w.name) for w in workspaces if w.output == output.name])) + + # Windows Title + #if container: + # t.append(container.name) + + # CPU + t.append('C' + ''.join([upChart(p/100) for p in psutil.cpu_percent(percpu=True)])) + + # Memory + t.append('M' + str(round(psutil.virtual_memory().percent)) + '% ' + + 'S' + str(round(psutil.swap_memory().percent)) + '%') + + # Disks + d = [] + for disk in psutil.disk_partitions(): + e = '' + if disk.device.startswith('/dev/sd'): + e += 'S' + disk.device[-2:].upper() + elif disk.device.startswith('/dev/mmcblk'): + e += 'M' + disk.device[-3] + disk.device[-1] + else: + e += '?' + e += ' ' + e += str(round(psutil.disk_usage(disk.mountpoint).percent)) + '%' + d.append(e) + t.append(' '.join(d)) + + # Network + netStats = psutil.net_if_stats() + netIO = psutil.net_io_counters(pernic=True) + net = [] + for iface in filter(lambda i: i != 'lo' and netStats[i].isup, netStats.keys()): + s = '' + if iface.startswith('eth'): + s += 'E' + elif iface.startswith('wlan'): + s += 'W' + else: + s += '?' + + s += ' ' + now = time() + global oldNetIO, oldTime + + sent = ((oldNetIO[iface].bytes_sent if iface in oldNetIO else 0) - (netIO[iface].bytes_sent if iface in netIO else 0)) / (oldTime - now) + recv = ((oldNetIO[iface].bytes_recv if iface in oldNetIO else 0) - (netIO[iface].bytes_recv if iface in netIO else 0)) / (oldTime - now) + s += '↓' + humanSizeOf(abs(recv), 'B/s') + ' ↑' + humanSizeOf(abs(sent), 'B/s') + + oldNetIO = netIO + oldTime = now + + net.append(s) + t.append(' '.join(net)) + + # Battery + if os.path.isdir('/sys/class/power_supply/BAT0'): + with open('/sys/class/power_supply/BAT0/charge_now') as f: + charge_now = int(f.read()) + with open('/sys/class/power_supply/BAT0/charge_full_design') as f: + charge_full = int(f.read()) + t.append('B' + str(round(100*charge_now/charge_full)) + '%') + + # Volume + # t.append('V ' + str(alsaaudio.Mixer('Master').getvolume()[0]) + '%') + + t.append(username + '@' + hostname) + + # print(' - '.join(t)) + # t = [output.name] + + z += ' - '.join(t) + '%{S' + str(aOutput + 1) + '}' + #lemonbar.stdin.write(bytes(' - '.join(t), 'utf-8')) + #lemonbar.stdin.write(bytes('%{S' + str(aOutput + 1) + '}', 'utf-8')) + + lemonbar.stdin.write(bytes(z+'\n', 'utf-8')) + lemonbar.stdin.flush() + +# Event listeners +def on_mode(i3, e): + global mode + if (e.change == 'default'): + mode = '' + else : + mode = e.change + update() + +i3.on("mode", on_mode) + +#def on_window_focus(i3, e): +# global container +# container = e.container +# update() +# +#i3.on("window::focus", on_window_focus) + +def on_workspace_focus(i3, e): + global workspaces + workspaces = i3.get_workspaces() + update() + +i3.on("workspace::focus", on_workspace_focus) + +# Starting + +update() + + +i3.main() diff --git a/config/lemonbar/pip.py b/config/lemonbar/pip.py new file mode 100755 index 0000000..f25b2ab --- /dev/null +++ b/config/lemonbar/pip.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python3 + +""" +Beautiful script +""" + +import subprocess +import time +import datetime +import os +import multiprocessing +import i3ipc +import difflib + +# Constants +FONT = "DejaVu Sans Mono for Powerline" + +thm = ['#002b36','#dc322f','#859900','#b58900','#268bd2','#6c71c4','#2aa198','#93a1a1','#657b83','#dc322f','#859900','#b58900','#268bd2','#6c71c4','#2aa198','#fdf6e3'] +fg = '#93a1a1' +bg = '#002b36' + +THEMES = { + 'CENTER': (fg, bg), + 'DEFAULT': (thm[0], thm[8]), + '1': (thm[0], thm[9]), + '2': (thm[0], thm[10]), + '3': (thm[0], thm[11]), + '4': (thm[0], thm[12]), + '5': (thm[0], thm[13]), + '6': (thm[0], thm[14]), + '7': (thm[0], thm[15]), + } + +# Utils + +def fitText(text, size): + """ + Add spaces or cut a string to be `size` characters long + """ + if size > 0: + if len(text) >= size: + return text[:size] + else: + return ('%' + str(size) + 's') % text + else: + return '' + +def fgColor(theme): + global THEMES + return THEMES[theme][0] + +def bgColor(theme): + global THEMES + return THEMES[theme][1] + +class Section: + def __init__(self, theme='DEFAULT'): + self.text = '' + self.size = 0 + self.toSize = 0 + self.theme = theme + self.visible = False + self.name = '' + + def update(self, text): + if text == '': + self.toSize = 0 + else: + if len(text) < len(self.text): + self.text = text + self.text[len(text):] + else: + self.text = text + self.toSize = len(text) + 3 + + def updateSize(self): + """ + Set the size for the next frame of animation + Return if another frame is needed + """ + if self.toSize > self.size: + self.size += 1 + elif self.toSize < self.size: + self.size -= 1 + self.visible = self.size + return self.toSize == self.size + + def draw(self, left=True, nextTheme='DEFAULT'): + s = '' + if self.visible: + if not left: + if self.theme == nextTheme: + s += '' + else: + s += '%{F' + bgColor(self.theme) + '}' + s += '%{B' + bgColor(nextTheme) + '}' + s += '' + s += '%{F' + fgColor(self.theme) + '}' + s += '%{B' + bgColor(self.theme) + '}' + s += ' ' if self.size > 1 else '' + s += fitText(self.text, self.size - 3) + s += ' ' if self.size > 2 else '' + if left: + if self.theme == nextTheme: + s += '' + else: + s += '%{F' + bgColor(self.theme) + '}' + s += '%{B' + bgColor(nextTheme) + '}' + s += '' + return s + +# Section definition +sTime = Section('3') + +hostname = os.environ['HOSTNAME'].split('.')[0] +sHost = Section('2') +sHost.update(os.environ['USER'] + '@' + hostname.split('-')[-1] if '-' in hostname else hostname) + + +# Groups definition +gLeft = [] +gRight = [sTime, sHost] + +# Bar handling +bar = subprocess.Popen(['lemonbar', '-f', FONT, '-b'], stdin=subprocess.PIPE) + +def updateBar(): + global timeLastUpdate, timeUpdate + global gLeft, gRight + global outputs + + text = '' + for oi in range(len(outputs)): + output = outputs[oi] + gLeftFiltered = list(filter(lambda s: s.visible and (not s.output or s.output == output.name), gLeft)) + tLeft = '' + l = len(gLeftFiltered) + for gi in range(l): + g = gLeftFiltered[gi] + nextTheme = gLeftFiltered[gi + 1].theme if gi + 1 < l else 'CENTER' # Next visible section for transition + tLeft = tLeft + g.draw(True, nextTheme) + + tRight = '' + for gi in range(len(gRight)): + g = gRight[gi] + nextTheme = 'CENTER' + for gn in gRight[gi+1:]: + if gn.visible: + nextTheme = gn.theme + break + tRight = g.draw(False, nextTheme) + tRight + text += '%{l}' + tLeft + '%{r}' + tRight + '%{B' + bgColor('CENTER') + '}' + '%{S' + str(oi + 1) + '}' + + bar.stdin.write(bytes(text + '\n', 'utf-8')) + bar.stdin.flush() + +# Values +i3 = i3ipc.Connection() +outputs = [] + +def on_output(): + global outputs + outputs = sorted(sorted(list(filter(lambda o: o.active, i3.get_outputs())), key=lambda o: o.rect.y), key=lambda o: o.rect.x) +on_output() + +def on_workspace_focus(): + global i3 + global gLeft + workspaces = i3.get_workspaces() + wNames = [w.name for w in workspaces] + sNames = [s.name for s in gLeft] + + newGLeft = [] + def actuate(section, workspace): + if workspace: + section.name = workspace.name + section.output = workspace.output + if workspace.visible: + section.update(workspace.name) + else: + section.update(workspace.name.split(' ')[0]) + + if workspace.focused: + section.theme = '4' + elif workspace.urgent: + section.theme = '1' + else: + section.theme = '6' + else: + section.update('') + section.theme = '6' + + for tag, i, j, k, l in difflib.SequenceMatcher(None, sNames, wNames).get_opcodes(): + if tag == 'equal': # If the workspaces didn't changed + for a in range(j-i): + workspace = workspaces[k+a] + section = gLeft[i+a] + actuate(section, workspace) + newGLeft.append(section) + if tag in ('delete', 'replace'): # If the workspaces were removed + for section in gLeft[i:j]: + if section.visible: + actuate(section, None) + newGLeft.append(section) + else: + del section + if tag in ('insert', 'replace'): # If the workspaces were removed + for workspace in workspaces[k:l]: + section = Section() + actuate(section, workspace) + newGLeft.append(section) + gLeft = newGLeft + + updateBar() +on_workspace_focus() + + +def i3events(i3childPipe): + global i3 + + # Proxy functions + def on_workspace_focus(i3, e): + global i3childPipe + i3childPipe.send('on_workspace_focus') + i3.on("workspace::focus", on_workspace_focus) + + def on_output(i3, e): + global i3childPipe + i3childPipe.send('on_output') + i3.on("output", on_output) + + i3.main() + +i3parentPipe, i3childPipe = multiprocessing.Pipe() +i3process = multiprocessing.Process(target=i3events, args=(i3childPipe,)) +i3process.start() + +def updateValues(): + # Time + now = datetime.datetime.now() + sTime.update(now.strftime('%x %X')) + +def updateAnimation(): + for s in set(gLeft + gRight): + s.updateSize() + updateBar() + +lastUpdate = 0 +while True: + now = time.time() + if i3parentPipe.poll(): + msg = i3parentPipe.recv() + if msg == 'on_workspace_focus': + on_workspace_focus() + elif msg == 'on_output': + on_output() + # TODO Restart lemonbar + else: + print(msg) + updateAnimation() + if now >= lastUpdate + 1: + updateValues() + lastUpdate = now + + time.sleep(0.05) + + diff --git a/gitconfig b/gitconfig index 6860844..0dac09d 100644 --- a/gitconfig +++ b/gitconfig @@ -1,6 +1,7 @@ [user] - name = Geoffrey Frogeye + name = Geoffrey “Frogeye” Preud'homme email = geoffrey@frogeye.fr + signingkey = D8A7ECA00A8CD3DD [core] editor = vim excludesfile = ~/.gitignore diff --git a/vimrc b/vimrc index f6c1eef..d68cf57 100644 --- a/vimrc +++ b/vimrc @@ -83,7 +83,7 @@ let g:airline#extensions#tabline#enabled = 1 let g:airline_section_a = airline#section#create(['mode']) let g:airline_section_b = airline#section#create(['branch', 'hunks']) -let g:airline_section_z = airline#section#create(['%B', '@', '%l', ':', '%c']) +" let g:airline_section_z = airline#section#create(['%B', '@', '%l', ':', '%c']) let g:airline_theme = 'base16_monokai' """ AUTOFORMAT """