diff --git a/config/lemonbar/new.py b/config/lemonbar/new.py new file mode 100755 index 0000000..5a3745f --- /dev/null +++ b/config/lemonbar/new.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 + +import enum +import logging +import threading +import time + +# TODO Update strategies (periodic, inotify file) +# TODO Section order (priority system maybe ?) +# TODO Allow deletion of Bar, BarGroup and Section for screen changes +# TODO Optimize to use write() calls instead of string concatenation (writing +# BarGroup strings should be a good compromise) + + +class BarGroupType(enum.Enum): + LEFT = 0 + RIGHT = 1 + # MID_LEFT = 2 + # MID_RIGHT = 3 + + +class Bar: + """ + One bar for each screen + """ + + everyone = set() + string = "" + + @staticmethod + def init(): + Section.init() + + # Debug + Bar() + + def __init__(self): + self.screen = 0 # TODO + self.groups = dict() + + for groupType in BarGroupType: + group = BarGroup(groupType, self) + self.groups[groupType] = group + + self.childsChanged = False + + self.everyone.add(self) + + @staticmethod + def addSectionAll(section, group, screens=None): + """ + .. note:: + Add the section before updating it for the first time. + """ + assert isinstance(section, Section) + assert isinstance(group, BarGroupType) + # TODO screens selection + for bar in Bar.everyone: + bar.addSection(section, group=group) + + def addSection(self, section, group): + assert isinstance(section, Section) + assert isinstance(group, BarGroupType) + self.groups[group].addSection(section) + + def update(self): + if self.childsChanged: + self.string = "" + + self.childsChanged = False + + @staticmethod + def updateAll(): + + for bar in Bar.everyone: + bar.update() + + Bar.string = "" + + +class BarGroup: + """ + One for each group of each bar + """ + + everyone = set() + + def __init__(self, groupType, parent): + assert isinstance(groupType, BarGroupType) + assert isinstance(parent, Bar) + + self.groupType = groupType + self.parent = parent + + self.sections = list() + self.string = '' + self.form = '' + + #: One of the sections that had their theme or visibility changed + self.childsThemeChanged = False + + #: One of the sections that had their text (maybe their size) changed + self.childsTextChanged = False + + BarGroup.everyone.add(self) + + def addSection(self, section): + self.sections.append(section) + section.parents.add(self) + + def update(self): + if self.childsThemeChanged: + # TODO + self.form = ">".join([sec.curText for sec in self.sections + if sec.visible]) + + if self.childsTextChanged or self.childsThemeChanged: + print("118") + self.string = ">".join([sec.curText for sec in self.sections + if sec.visible]) + print("|{0}|".format(self.string)) + + self.parent.childsChanged = True + + self.childsThemeChanged = False + self.childsTextChanged = False + + @staticmethod + def updateAll(): + + print("130") + + for group in BarGroup.everyone: + group.update() + + Bar.updateAll() + + +class SectionThread(threading.Thread): + def run(self): + while Section.somethingChanged.wait(): + Section.updateAll() + while len(Section.sizeChanging) > 0: + time.sleep(0.1) + Section.updateAll() + + +class Section: + + #: Sections that do not have their destination size + sizeChanging = set() + somethingChanged = threading.Event() + updateThread = None + + @staticmethod + def init(): + Section.updateThread = SectionThread(daemon=True) + Section.updateThread.start() + + def __init__(self): + #: Displayed section + #: Note: A section can be empty and displayed! + self.visible = False + + #: Displayed text + self.curText = '' + #: Displayed text size + self.curSize = 0 + + #: Destination text + self.dstText = '' + #: Destination size + self.dstSize = 0 + + #: Groups that have this section + self.parents = set() + + def informParentsThemeChanged(self): + for parent in self.parents: + parent.childsThemeChanged = True + + def informParentsTextChanged(self): + for parent in self.parents: + parent.childsTextChanged = True + + def updateText(self, text): + if len(text): + self.dstText = ' {} '.format(text) + self.dstSize = len(self.dstText) + else: + self.dstSize = 0 + + if self.curSize == self.dstSize: + self.curText = self.dstText + self.informParentsTextChanged() + else: + Section.sizeChanging.add(self) + Section.somethingChanged.set() + + def updateVisibility(self, visibility): + assert isinstance(visibility, bool) + + self.visible = visibility + self.informParentsThemeChanged() + Section.somethingChanged.set() + + @staticmethod + def fit(text, size): + t = len(text) + return text[:size] if t >= size else text + " " * (size - t) + + def update(self): + # TODO Might profit of a better logic + if not self.visible: + self.updateVisibility(True) + return + + if self.dstSize > self.curSize: + self.curSize += 1 + elif self.dstSize < self.curSize: + self.curSize -= 1 + else: + # Visibility toggling must be done one step after curSize = 0 + if self.dstSize == 0: + self.updateVisibility(False) + Section.sizeChanging.remove(self) + return + + self.curText = Section.fit(self.dstText, self.curSize) + self.informParentsTextChanged() + + @staticmethod + def updateAll(): + """ + Process all sections for text size changes + """ + + for sizeChanging in Section.sizeChanging.copy(): + sizeChanging.update() + # print("{1:3d} |{0}|".format(sizeChanging.curText, sizeChanging.curSize)) + + BarGroup.updateAll() + + Section.somethingChanged.clear() + + +if __name__ == '__main__': + Bar.init() + sec = Section() + sec1 = Section() + Bar.addSectionAll(sec, BarGroupType.LEFT) + Bar.addSectionAll(sec1, BarGroupType.LEFT) + + sec.updateText("Hello") + # sec1.updateText("world!") + time.sleep(2) + sec.updateText("Salut") + # sec1.updateText("le monde !") + time.sleep(2) + sec.updateText("") + # sec1.updateText("") + time.sleep(2) diff --git a/config/lemonbar/pip.py b/config/lemonbar/pip.py index f25b2ab..655b6b2 100755 --- a/config/lemonbar/pip.py +++ b/config/lemonbar/pip.py @@ -15,44 +15,53 @@ 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'] +# 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]), - } + '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: + t = len(text) + if t >= size: return text[:size] else: - return ('%' + str(size) + 's') % text + 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 = '' @@ -108,12 +117,15 @@ class Section: 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) +sHost.update( + os.environ['USER'] + '@' + hostname.split('-')[-1] + if '-' in hostname else hostname) # Groups definition @@ -123,6 +135,7 @@ gRight = [sTime, sHost] # Bar handling bar = subprocess.Popen(['lemonbar', '-f', FONT, '-b'], stdin=subprocess.PIPE) + def updateBar(): global timeLastUpdate, timeUpdate global gLeft, gRight @@ -131,37 +144,55 @@ def updateBar(): 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)) + 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 + # 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:]: + 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) + '}' + 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) + 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 @@ -170,6 +201,7 @@ def on_workspace_focus(): sNames = [s.name for s in gLeft] newGLeft = [] + def actuate(section, workspace): if workspace: section.name = workspace.name @@ -189,21 +221,22 @@ def on_workspace_focus(): 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] + 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 + 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 + if tag in ('insert', 'replace'): # If the workspaces were removed for workspace in workspaces[k:l]: section = Section() actuate(section, workspace) @@ -211,6 +244,8 @@ def on_workspace_focus(): gLeft = newGLeft updateBar() + + on_workspace_focus() @@ -230,20 +265,24 @@ def i3events(i3childPipe): 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() @@ -262,5 +301,3 @@ while True: lastUpdate = now time.sleep(0.05) - -