dotfiles/config/lemonbar/pip.py
2018-08-21 21:50:44 +02:00

304 lines
7.5 KiB
Python
Executable file

#!/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"
# TODO Update to be in sync with base16
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:
t = len(text)
if t >= size:
return text[:size]
else:
diff = size - t
return text + " " * diff
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]
# Next visible section for transition
nextTheme = gLeftFiltered[gi + 1].theme if gi + 1 < l else 'CENTER'
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)