From f81fd6bfd2021cdf4b11a4581495505059f99b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geoffrey=20=E2=80=9CFrogeye=E2=80=9D=20Preud=27homme?= Date: Sat, 17 Aug 2024 00:57:06 +0200 Subject: [PATCH 1/4] frobarng: Even more dev --- hm/desktop/frobar/.dev/new.py | 309 +++++++++++++++++++++++++++++----- 1 file changed, 268 insertions(+), 41 deletions(-) diff --git a/hm/desktop/frobar/.dev/new.py b/hm/desktop/frobar/.dev/new.py index bab7e0c..0e33ad2 100644 --- a/hm/desktop/frobar/.dev/new.py +++ b/hm/desktop/frobar/.dev/new.py @@ -3,27 +3,36 @@ import asyncio import datetime import enum +import logging import random import signal import typing +import coloredlogs import i3ipc +import i3ipc.aio + +coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s") +log = logging.getLogger() + +T = typing.TypeVar("T", bound="ComposableText") class ComposableText: + def getFirstParentOfType(self, typ: typing.Type[T]) -> T: + parent = self + while not isinstance(parent, typ): + assert parent.parent, f"{self} doesn't have a parent of {typ}" + parent = parent.parent + return parent + def __init__(self, parent: typing.Optional["ComposableText"] = None) -> None: self.parent = parent - - prevParent = self - while parent: - prevParent = parent - parent = parent.parent - assert isinstance(prevParent, Bar) - self.bar: Bar = prevParent + self.bar = self.getFirstParentOfType(Bar) def updateMarkup(self) -> None: self.bar.refresh.set() - # OPTI See if worth caching the output + # TODO OPTI See if worth caching the output def generateMarkup(self) -> str: raise NotImplementedError(f"{self} cannot generate markup") @@ -38,6 +47,14 @@ def randomColor(seed: int | bytes | None = None) -> str: return "#" + "".join(f"{random.randint(0, 0xff):02x}" for _ in range(3)) +class Button(enum.Enum): + CLICK_LEFT = "1" + CLICK_MIDDLE = "2" + CLICK_RIGHT = "3" + SCROLL_UP = "4" + SCROLL_DOWN = "5" + + class Section(ComposableText): """ Colorable block separated by chevrons @@ -45,54 +62,80 @@ class Section(ComposableText): def __init__(self, parent: "Module") -> None: super().__init__(parent=parent) + self.parent: "Module" + self.color = randomColor() - self.text: str = "" - self.size = 0 + self.desiredText: str | None = None + self.text = "" + self.targetSize = -1 + self.size = -1 self.animationTask: asyncio.Task | None = None + self.actions: dict[Button, str] = dict() def isHidden(self) -> bool: - return self.text is None + return self.size < 0 - # Geometric series + # Geometric series, with a cap ANIM_A = 0.025 ANIM_R = 0.9 + ANIM_MIN = 0.001 async def animate(self) -> None: - targetSize = len(self.text) - increment = 1 if self.size < targetSize else -1 - + increment = 1 if self.size < self.targetSize else -1 loop = asyncio.get_running_loop() frameTime = loop.time() animTime = self.ANIM_A - while self.size != targetSize: + while self.size != self.targetSize: self.size += increment self.updateMarkup() animTime *= self.ANIM_R + animTime = max(self.ANIM_MIN, animTime) frameTime += animTime sleepTime = frameTime - loop.time() # In case of stress, skip refreshing by not awaiting if sleepTime > 0: await asyncio.sleep(sleepTime) + else: + log.warning("Skipped an animation frame") def setText(self, text: str | None) -> None: - # OPTI Skip if same text - oldText = self.text - self.text = f" {text} " - if oldText == self.text: + # OPTI Don't redraw nor reset animation if setting the same text + if self.desiredText == text: return - if len(oldText) == len(self.text): + self.desiredText = text + if text is None: + self.text = "" + self.targetSize = -1 + else: + self.text = f" {text} " + self.targetSize = len(self.text) + if self.animationTask: + self.animationTask.cancel() + # OPTI Skip the whole animation task if not required + if self.size == self.targetSize: self.updateMarkup() else: - if self.animationTask: - self.animationTask.cancel() self.animationTask = self.bar.taskGroup.create_task(self.animate()) + def setAction(self, button: Button, callback: typing.Callable | None) -> None: + if button in self.actions: + command = self.actions[button] + self.bar.removeAction(command) + del self.actions[button] + if callback: + command = self.bar.addAction(callback) + self.actions[button] = command + def generateMarkup(self) -> str: + assert not self.isHidden() pad = max(0, self.size - len(self.text)) - return self.text[: self.size] + " " * pad + text = self.text[: self.size] + " " * pad + for button, command in self.actions.items(): + text = "%{A" + button.value + ":" + command + ":}" + text + "%{A}" + return text class Module(ComposableText): @@ -102,6 +145,8 @@ class Module(ComposableText): def __init__(self, parent: "Side") -> None: super().__init__(parent=parent) + self.parent: "Side" + self.sections: list[Section] = [] self.mirroring: Module | None = None self.mirrors: list[Module] = list() @@ -131,6 +176,8 @@ class Alignment(enum.Enum): class Side(ComposableText): def __init__(self, parent: "Screen", alignment: Alignment) -> None: super().__init__(parent=parent) + self.parent: Screen + self.alignment = alignment self.modules: list[Module] = [] @@ -170,6 +217,8 @@ class Side(ComposableText): class Screen(ComposableText): def __init__(self, parent: "Bar", output: str) -> None: super().__init__(parent=parent) + self.parent: "Bar" + self.output = output self.sides = dict() @@ -192,7 +241,8 @@ class Bar(ComposableText): self.refresh = asyncio.Event() self.taskGroup = asyncio.TaskGroup() self.providers: list["Provider"] = list() - self.running = True + self.actionIndex = 0 + self.actions: dict[str, typing.Callable] = dict() self.screens = [] i3 = i3ipc.Connection() @@ -211,24 +261,43 @@ class Bar(ComposableText): "-f", "DejaVuSansM Nerd Font:size=10", ] - proc = await asyncio.create_subprocess_exec(*cmd, stdin=asyncio.subprocess.PIPE) + proc = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE + ) async def refresher() -> None: assert proc.stdin - while self.running: + while True: await self.refresh.wait() self.refresh.clear() - proc.stdin.write(self.getMarkup().encode()) + markup = self.getMarkup() + # log.debug(markup) + proc.stdin.write(markup.encode()) - async with self.taskGroup as tg: - ref = tg.create_task(refresher()) + async def actionHandler() -> None: + assert proc.stdout + while True: + line = await proc.stdout.readline() + command = line.decode().strip() + callback = self.actions[command] + callback() + + longRunningTasks = list() + + def addLongRunningTask(coro: typing.Coroutine) -> None: + task = self.taskGroup.create_task(coro) + longRunningTasks.append(task) + + async with self.taskGroup: + addLongRunningTask(refresher()) + addLongRunningTask(actionHandler()) for provider in self.providers: - tg.create_task(provider.run()) + addLongRunningTask(provider.run()) def exit() -> None: - print("Terminating") - ref.cancel() - self.running = False + log.info("Terminating") + for task in longRunningTasks: + task.cancel() loop = asyncio.get_event_loop() loop.add_signal_handler(signal.SIGINT, exit) @@ -256,6 +325,15 @@ class Bar(ComposableText): if modules: self.providers.append(provider) + def addAction(self, callback: typing.Callable) -> str: + command = f"{self.actionIndex:x}" + self.actions[command] = callback + self.actionIndex += 1 + return command + + def removeAction(self, command: str) -> None: + del self.actions[command] + class Provider: def __init__(self) -> None: @@ -296,26 +374,175 @@ class StaticProvider(SingleSectionProvider): self.section.setText(self.text) -class TimeProvider(SingleSectionProvider): +class StatefulProvider(SingleSectionProvider): + # TODO Should actually be a Section descendant + NUMBER_STATES: int + + def __init__(self) -> None: + super().__init__() + self.state = 0 + self.stateChanged = asyncio.Event() + + def incrementState(self) -> None: + self.state += 1 + self.changeState() + + def decrementState(self) -> None: + self.state -= 1 + self.changeState() + + def changeState(self) -> None: + self.state %= self.NUMBER_STATES + self.stateChanged.set() + self.stateChanged.clear() + async def run(self) -> None: await super().run() + self.section.setAction(Button.CLICK_LEFT, self.incrementState) + self.section.setAction(Button.CLICK_RIGHT, self.decrementState) - while self.section.bar.running: + +# Providers + + +class I3ModeProvider(SingleSectionProvider): + def on_mode(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None: + self.section.setText(None if e.change == "default" else e.change) + + async def run(self) -> None: + await super().run() + i3 = await i3ipc.aio.Connection(auto_reconnect=True).connect() + i3.on(i3ipc.Event.MODE, self.on_mode) + await i3.main() + + +class I3WindowTitleProvider(SingleSectionProvider): + # TODO FEAT To make this available from start, we need to find the + # `focused=True` element following the `focus` array + def on_window(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None: + self.section.setText(e.container.name) + + async def run(self) -> None: + await super().run() + i3 = await i3ipc.aio.Connection(auto_reconnect=True).connect() + i3.on(i3ipc.Event.WINDOW, self.on_window) + await i3.main() + + +class I3WorkspacesProvider(Provider): + # FIXME Custom names + # FIXME Colors + + async def updateWorkspaces(self, i3: i3ipc.Connection) -> None: + """ + Since the i3 IPC interface cannot really tell you by events + when workspaces get invisible or not urgent anymore. + Relying on those exclusively would require reimplementing some of i3 logic. + Fetching all the workspaces on event looks ugly but is the most maintainable. + Times I tried to challenge this and failed: 2. + """ + workspaces = await i3.get_workspaces() + for workspace in workspaces: + module = self.modulesFromOutput[workspace.output] + insert = False + if workspace.num in self.sections: + section = self.sections[workspace.num] + if section.parent != module: + section.parent.sections.remove(section) + section.parent = module + section.updateMarkup() + insert = True + else: + section = Section(parent=module) + self.sections[workspace.num] = section + insert = True + + def generate_switch_workspace(num: int) -> typing.Callable: + def switch_workspace() -> None: + self.bar.taskGroup.create_task( + i3.command(f"workspace number {num}") + ) + + return switch_workspace + + section.setAction( + Button.CLICK_LEFT, generate_switch_workspace(workspace.num) + ) + if insert: + module.sections.append(section) + revSections = dict((v, k) for k, v in self.sections.items()) + module.sections.sort(key=lambda s: revSections[s]) + name = workspace.name + if workspace.urgent: + name = f"{name} !" + elif workspace.focused: + name = f"{name} +" + elif workspace.visible: + name = f"{name} *" + section.setText(name) + workspacesNums = set(workspace.num for workspace in workspaces) + for num, section in self.sections.items(): + if num not in workspacesNums: + # This should delete the Section but it turned out to be hard + section.setText(None) + + def onWorkspaceChange( + self, i3: i3ipc.Connection, e: i3ipc.Event | None = None + ) -> None: + # Cancelling the task doesn't seem to prevent performance double-events + self.bar.taskGroup.create_task(self.updateWorkspaces(i3)) + + def __init__( + self, + ) -> None: + super().__init__() + + self.sections: dict[int, Section] = dict() + self.modulesFromOutput: dict[str, Module] = dict() + self.bar: Bar + + async def run(self) -> None: + for module in self.modules: + screen = module.getFirstParentOfType(Screen) + output = screen.output + self.modulesFromOutput[output] = module + self.bar = module.bar + + i3 = await i3ipc.aio.Connection(auto_reconnect=True).connect() + i3.on(i3ipc.Event.WORKSPACE, self.onWorkspaceChange) + self.onWorkspaceChange(i3) + await i3.main() + + +class TimeProvider(StatefulProvider): + FORMATS = ["%H:%M", "%m-%d %H:%M:%S", "%a %y-%m-%d %H:%M:%S"] + NUMBER_STATES = len(FORMATS) + + async def run(self) -> None: + await super().run() + self.state = 1 + + while True: now = datetime.datetime.now() - # section.setText(now.strftime("%a %y-%m-%d %H:%M:%S.%f")) - self.section.setText("-" * (now.second % 10)) + format = self.FORMATS[self.state] + self.section.setText(now.strftime(format)) + remaining = 1 - now.microsecond / 1000000 - await asyncio.sleep(remaining) + try: + await asyncio.wait_for(self.stateChanged.wait(), remaining) + except TimeoutError: + pass async def main() -> None: bar = Bar() dualScreen = len(bar.screens) > 1 - bar.addProvider(StaticProvider(text="i3 workspaces"), alignment=Alignment.LEFT) + bar.addProvider(I3ModeProvider(), alignment=Alignment.LEFT) + bar.addProvider(I3WorkspacesProvider(), alignment=Alignment.LEFT) if dualScreen: bar.addProvider( - StaticProvider(text="i3 title"), screenNum=0, alignment=Alignment.CENTER + I3WindowTitleProvider(), screenNum=0, alignment=Alignment.CENTER ) bar.addProvider( StaticProvider(text="mpris"), From c375cb2e11c7843f5da9e1474266239c5e0006e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geoffrey=20=E2=80=9CFrogeye=E2=80=9D=20Preud=27homme?= Date: Sun, 25 Aug 2024 09:48:36 +0200 Subject: [PATCH 2/4] frobarng: Forgotten dev --- hm/desktop/frobar/.dev/new.py | 205 ++++++++++++++++++++++++---------- 1 file changed, 147 insertions(+), 58 deletions(-) diff --git a/hm/desktop/frobar/.dev/new.py b/hm/desktop/frobar/.dev/new.py index 0e33ad2..36ee2ec 100644 --- a/hm/desktop/frobar/.dev/new.py +++ b/hm/desktop/frobar/.dev/new.py @@ -11,14 +11,45 @@ import typing import coloredlogs import i3ipc import i3ipc.aio +import psutil coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s") log = logging.getLogger() T = typing.TypeVar("T", bound="ComposableText") +P = typing.TypeVar("P", bound="ComposableText") +C = typing.TypeVar("C", bound="ComposableText") +Sortable = str | int -class ComposableText: +class ComposableText(typing.Generic[P, C]): + + def __init__( + self, + parent: typing.Optional[P] = None, + sortKey: Sortable = 0, + ) -> None: + self.parent: typing.Optional[P] = None + self.children: typing.MutableSequence[C] = list() + self.sortKey = sortKey + if parent: + self.setParent(parent) + self.bar = self.getFirstParentOfType(Bar) + + def setParent(self, parent: P) -> None: + assert self.parent is None + parent.children.append(self) + assert isinstance(parent.children, list) + parent.children.sort(key=lambda c: c.sortKey) + self.parent = parent + self.parent.updateMarkup() + + def unsetParent(self) -> None: + assert self.parent + self.parent.children.remove(self) + self.parent.updateMarkup() + self.parent = None + def getFirstParentOfType(self, typ: typing.Type[T]) -> T: parent = self while not isinstance(parent, typ): @@ -26,10 +57,6 @@ class ComposableText: parent = parent.parent return parent - def __init__(self, parent: typing.Optional["ComposableText"] = None) -> None: - self.parent = parent - self.bar = self.getFirstParentOfType(Bar) - def updateMarkup(self) -> None: self.bar.refresh.set() # TODO OPTI See if worth caching the output @@ -60,8 +87,8 @@ class Section(ComposableText): Colorable block separated by chevrons """ - def __init__(self, parent: "Module") -> None: - super().__init__(parent=parent) + def __init__(self, parent: "Module", sortKey: Sortable = 0) -> None: + super().__init__(parent=parent, sortKey=sortKey) self.parent: "Module" self.color = randomColor() @@ -146,8 +173,8 @@ class Module(ComposableText): def __init__(self, parent: "Side") -> None: super().__init__(parent=parent) self.parent: "Side" + self.children: typing.MutableSequence[Section] - self.sections: list[Section] = [] self.mirroring: Module | None = None self.mirrors: list[Module] = list() @@ -155,11 +182,11 @@ class Module(ComposableText): self.mirroring = module module.mirrors.append(self) - def getSections(self) -> list[Section]: + def getSections(self) -> typing.Sequence[Section]: if self.mirroring: - return self.mirroring.sections + return self.mirroring.children else: - return self.sections + return self.children def updateMarkup(self) -> None: super().updateMarkup() @@ -177,16 +204,16 @@ class Side(ComposableText): def __init__(self, parent: "Screen", alignment: Alignment) -> None: super().__init__(parent=parent) self.parent: Screen + self.children: typing.MutableSequence[Module] = [] self.alignment = alignment - self.modules: list[Module] = [] def generateMarkup(self) -> str: - if not self.modules: + if not self.children: return "" text = "%{" + self.alignment.value + "}" lastSection: Section | None = None - for module in self.modules: + for module in self.children: for section in module.getSections(): if section.isHidden(): continue @@ -218,16 +245,16 @@ class Screen(ComposableText): def __init__(self, parent: "Bar", output: str) -> None: super().__init__(parent=parent) self.parent: "Bar" + self.children: typing.MutableSequence[Side] self.output = output - self.sides = dict() for alignment in Alignment: - self.sides[alignment] = Side(parent=self, alignment=alignment) + Side(parent=self, alignment=alignment) def generateMarkup(self) -> str: return ("%{Sn" + self.output + "}") + "".join( - side.getMarkup() for side in self.sides.values() + side.getMarkup() for side in self.children ) @@ -238,19 +265,20 @@ class Bar(ComposableText): def __init__(self) -> None: super().__init__() + self.parent: None + self.children: typing.MutableSequence[Screen] + self.refresh = asyncio.Event() self.taskGroup = asyncio.TaskGroup() self.providers: list["Provider"] = list() self.actionIndex = 0 self.actions: dict[str, typing.Callable] = dict() - self.screens = [] i3 = i3ipc.Connection() for output in i3.get_outputs(): if not output.active: continue - screen = Screen(parent=self, output=output.name) - self.screens.append(screen) + Screen(parent=self, output=output.name) async def run(self) -> None: cmd = [ @@ -303,7 +331,7 @@ class Bar(ComposableText): loop.add_signal_handler(signal.SIGINT, exit) def generateMarkup(self) -> str: - return "".join(section.getMarkup() for section in self.screens) + "\n" + return "".join(screen.getMarkup() for screen in self.children) + "\n" def addProvider( self, @@ -315,11 +343,10 @@ class Bar(ComposableText): screenNum: the provider will be added on this screen if set, all otherwise """ modules = list() - for s, screen in enumerate(self.screens): + for s, screen in enumerate(self.children): if screenNum is None or s == screenNum: - side = screen.sides[alignment] + side = next(filter(lambda s: s.alignment == alignment, screen.children)) module = Module(parent=side) - side.modules.append(module) modules.append(module) provider.modules = modules if modules: @@ -355,14 +382,11 @@ class MirrorProvider(Provider): class SingleSectionProvider(MirrorProvider): - def __init__(self) -> None: - super().__init__() - self.section: Section + SECTION_CLASS = Section async def run(self) -> None: await super().run() - self.section = Section(parent=self.module) - self.module.sections.append(self.section) + self.section = self.SECTION_CLASS(parent=self.module) class StaticProvider(SingleSectionProvider): @@ -374,15 +398,17 @@ class StaticProvider(SingleSectionProvider): self.section.setText(self.text) -class StatefulProvider(SingleSectionProvider): - # TODO Should actually be a Section descendant - NUMBER_STATES: int +class StatefulSection(Section): - def __init__(self) -> None: - super().__init__() + def __init__(self, parent: Module, sortKey: Sortable = 0) -> None: + super().__init__(parent=parent, sortKey=sortKey) self.state = 0 + self.numberStates: int self.stateChanged = asyncio.Event() + self.setAction(Button.CLICK_LEFT, self.incrementState) + self.setAction(Button.CLICK_RIGHT, self.decrementState) + def incrementState(self) -> None: self.state += 1 self.changeState() @@ -392,14 +418,13 @@ class StatefulProvider(SingleSectionProvider): self.changeState() def changeState(self) -> None: - self.state %= self.NUMBER_STATES + self.state %= self.numberStates self.stateChanged.set() self.stateChanged.clear() - async def run(self) -> None: - await super().run() - self.section.setAction(Button.CLICK_LEFT, self.incrementState) - self.section.setAction(Button.CLICK_RIGHT, self.decrementState) + +class StatefulProvider(SingleSectionProvider): + SECTION_CLASS = StatefulSection # Providers @@ -444,18 +469,14 @@ class I3WorkspacesProvider(Provider): workspaces = await i3.get_workspaces() for workspace in workspaces: module = self.modulesFromOutput[workspace.output] - insert = False if workspace.num in self.sections: section = self.sections[workspace.num] if section.parent != module: - section.parent.sections.remove(section) - section.parent = module - section.updateMarkup() - insert = True + section.unsetParent() + section.setParent(module) else: - section = Section(parent=module) + section = Section(parent=module, sortKey=workspace.num) self.sections[workspace.num] = section - insert = True def generate_switch_workspace(num: int) -> typing.Callable: def switch_workspace() -> None: @@ -468,10 +489,6 @@ class I3WorkspacesProvider(Provider): section.setAction( Button.CLICK_LEFT, generate_switch_workspace(workspace.num) ) - if insert: - module.sections.append(section) - revSections = dict((v, k) for k, v in self.sections.items()) - module.sections.sort(key=lambda s: revSections[s]) name = workspace.name if workspace.urgent: name = f"{name} !" @@ -514,29 +531,101 @@ class I3WorkspacesProvider(Provider): await i3.main() -class TimeProvider(StatefulProvider): - FORMATS = ["%H:%M", "%m-%d %H:%M:%S", "%a %y-%m-%d %H:%M:%S"] - NUMBER_STATES = len(FORMATS) +class NetworkProviderSection(StatefulSection): + def __init__(self, parent: Module, iface: str, provider: "NetworkProvider") -> None: + super().__init__(parent=parent, sortKey=iface) + self.iface = iface + self.provider = provider + + self.ignore = False + self.icon = "?" + self.wifi = False + if iface == "lo": + self.ignore = True + elif iface.startswith("eth") or iface.startswith("enp"): + if "u" in iface: + self.icon = "" + else: + self.icon = "" + elif iface.startswith("wlan") or iface.startswith("wl"): + self.icon = "" + self.wifi = True + elif ( + iface.startswith("tun") or iface.startswith("tap") or iface.startswith("wg") + ): + self.icon = "" + elif iface.startswith("docker"): + self.icon = "" + elif iface.startswith("veth"): + self.icon = "" + elif iface.startswith("vboxnet"): + self.icon = "" + self.numberStates = 5 if self.wifi else 4 + self.state = 1 if self.wifi else 0 + + async def getText(self) -> str | None: + if self.ignore or not self.provider.if_stats[self.iface].isup: + return None + + text = self.icon + return text + + +class NetworkProvider(MirrorProvider): + def __init__(self) -> None: + self.sections: dict[str, NetworkProviderSection] = dict() + + async def updateIface(self, iface: str) -> None: + section = self.sections[iface] + section.setText(await section.getText()) async def run(self) -> None: await super().run() - self.state = 1 + + while True: + # if_addrs: dict[str, list[psutil._common.snicaddr]] = psutil.net_if_addrs() + # io_counters: dict[str, psutil._common.snetio] = psutil.net_io_counters(pernic=True) + + async with asyncio.TaskGroup() as tg: + self.if_stats = psutil.net_if_stats() + for iface in self.if_stats: + if iface not in self.sections: + section = NetworkProviderSection( + parent=self.module, iface=iface, provider=self + ) + self.sections[iface] = section + + tg.create_task(self.updateIface(iface)) + for iface, section in self.sections.items(): + if iface not in self.if_stats: + section.setText(None) + tg.create_task(asyncio.sleep(1)) + + +class TimeProvider(StatefulProvider): + FORMATS = ["%H:%M", "%m-%d %H:%M:%S", "%a %y-%m-%d %H:%M:%S"] + + async def run(self) -> None: + await super().run() + assert isinstance(self.section, StatefulSection) + self.section.state = 1 + self.section.numberStates = len(self.FORMATS) while True: now = datetime.datetime.now() - format = self.FORMATS[self.state] + format = self.FORMATS[self.section.state] self.section.setText(now.strftime(format)) remaining = 1 - now.microsecond / 1000000 try: - await asyncio.wait_for(self.stateChanged.wait(), remaining) + await asyncio.wait_for(self.section.stateChanged.wait(), remaining) except TimeoutError: pass async def main() -> None: bar = Bar() - dualScreen = len(bar.screens) > 1 + dualScreen = len(bar.children) > 1 bar.addProvider(I3ModeProvider(), alignment=Alignment.LEFT) bar.addProvider(I3WorkspacesProvider(), alignment=Alignment.LEFT) @@ -557,7 +646,7 @@ async def main() -> None: alignment=Alignment.RIGHT, ) bar.addProvider( - StaticProvider("network"), + NetworkProvider(), screenNum=0 if dualScreen else None, alignment=Alignment.RIGHT, ) From 4d39ac076974f506c1b56d0c4acdfe13d72586b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geoffrey=20=E2=80=9CFrogeye=E2=80=9D=20Preud=27homme?= Date: Sun, 25 Aug 2024 10:43:05 +0200 Subject: [PATCH 3/4] displaylink: Use upstream --- curacao/hardware.nix | 7 +++++-- flake.nix | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/curacao/hardware.nix b/curacao/hardware.nix index 6147188..3d01138 100644 --- a/curacao/hardware.nix +++ b/curacao/hardware.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, config, nixos-hardware, displaylinknixpkgs, ... }: +{ pkgs, lib, nixos-hardware, unixpkgs, ... }: let displays = { embedded = { @@ -65,7 +65,10 @@ in }; nixpkgs.overlays = [ (self: super: { - displaylink = (import displaylinknixpkgs { inherit (super) system; config.allowUnfree = true; }).displaylink; + displaylink = (import unixpkgs { + inherit (super) system; + config.allowUnfree = true; + }).displaylink; }) ]; services = { diff --git a/flake.nix b/flake.nix index c91c36b..5a147b9 100644 --- a/flake.nix +++ b/flake.nix @@ -4,7 +4,7 @@ inputs = { # Packages nixpkgs.url = "nixpkgs/nixos-24.05"; - displaylinknixpkgs.url = "github:GeoffreyFrogeye/nixpkgs/displaylink-600"; + unixpkgs.url = "nixpkgs/master"; # OS disko = { url = "disko"; From 830552f8c30d736c8628d4d88512f0555ad2a4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geoffrey=20=E2=80=9CFrogeye=E2=80=9D=20Preud=27homme?= Date: Sun, 1 Sep 2024 14:17:35 +0200 Subject: [PATCH 4/4] Upgrade --- flake.lock | 89 +++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/flake.lock b/flake.lock index eda4147..58ec5ef 100644 --- a/flake.lock +++ b/flake.lock @@ -142,11 +142,11 @@ ] }, "locked": { - "lastModified": 1722217815, - "narHash": "sha256-8r5AJ3n8WEDw3rsZLALSuFQ5kJyWOcssNZvPxYLr2yc=", + "lastModified": 1724895876, + "narHash": "sha256-GSqAwa00+vRuHbq9O/yRv7Ov7W/pcMLis3HmeHv8a+Q=", "owner": "nix-community", "repo": "disko", - "rev": "1e6f8a7b4634fc051cc9361959bf414fcf17e094", + "rev": "511388d837178979de66d14ca4a2ebd5f7991cd3", "type": "github" }, "original": { @@ -154,22 +154,6 @@ "type": "indirect" } }, - "displaylinknixpkgs": { - "locked": { - "lastModified": 1717533296, - "narHash": "sha256-TOxOpOYy/tQB+eYQOTPQXNeUmkMghLVDBO0Gc2nj/vs=", - "owner": "GeoffreyFrogeye", - "repo": "nixpkgs", - "rev": "99006b6f4cd24796b1ff6b6981b8f44c9cebd301", - "type": "github" - }, - "original": { - "owner": "GeoffreyFrogeye", - "ref": "displaylink-600", - "repo": "nixpkgs", - "type": "github" - } - }, "flake-compat": { "locked": { "lastModified": 1696426674, @@ -224,11 +208,11 @@ ] }, "locked": { - "lastModified": 1719994518, - "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", + "lastModified": 1725024810, + "narHash": "sha256-ODYRm8zHfLTH3soTFWE452ydPYz2iTvr9T8ftDMUQ3E=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", + "rev": "af510d4a62d071ea13925ce41c95e3dec816c01d", "type": "github" }, "original": { @@ -285,11 +269,11 @@ ] }, "locked": { - "lastModified": 1721042469, - "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", + "lastModified": 1724857454, + "narHash": "sha256-Qyl9Q4QMTLZnnBb/8OuQ9LSkzWjBU1T5l5zIzTxkkhk=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", + "rev": "4509ca64f1084e73bc7a721b20c669a8d4c5ebe6", "type": "github" }, "original": { @@ -408,11 +392,11 @@ ] }, "locked": { - "lastModified": 1722082646, - "narHash": "sha256-od8dBWVP/ngg0cuoyEl/w9D+TCNDj6Kh4tr151Aax7w=", + "lastModified": 1724994893, + "narHash": "sha256-yutISDGg6HUaZqCaa54EcsfTwew3vhNtt/FNXBBo44g=", "owner": "lnl7", "repo": "nix-darwin", - "rev": "0413754b3cdb879ba14f6e96915e5fdf06c6aab6", + "rev": "c8d3157d1f768e382de5526bb38e74d2245cad04", "type": "github" }, "original": { @@ -476,11 +460,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1722332872, - "narHash": "sha256-2xLM4sc5QBfi0U/AANJAW21Bj4ZX479MHPMPkB+eKBU=", + "lastModified": 1724878143, + "narHash": "sha256-UjpKo92iZ25M05kgSOw/Ti6VZwpgdlOa73zHj8OcaDk=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "14c333162ba53c02853add87a0000cbd7aa230c2", + "rev": "95c3dfe6ef2e96ddc1ccdd7194e3cda02ca9a8ef", "type": "github" }, "original": { @@ -490,11 +474,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1722221733, - "narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=", + "lastModified": 1725001927, + "narHash": "sha256-eV+63gK0Mp7ygCR0Oy4yIYSNcum2VQwnZamHxYTNi+M=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "12bf09802d77264e441f48e25459c10c93eada2e", + "rev": "6e99f2a27d600612004fbd2c3282d614bfee6421", "type": "github" }, "original": { @@ -549,11 +533,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1722353133, - "narHash": "sha256-16GH5+2ctcKLSAG8cxtR6YZwAemymIJGKHatLfMWA7I=", + "lastModified": 1725107436, + "narHash": "sha256-84Rz+GeFifzaJHnyMlkz4TdnrWxQdryTQKU3XVFQR1Q=", "owner": "nix-community", "repo": "nixvim", - "rev": "d69fb1bd7114a56532e666dc450c46cb42d382e0", + "rev": "7cae6d0202140ec322e18b65b63d03b423d595f7", "type": "github" }, "original": { @@ -603,11 +587,11 @@ }, "nur": { "locked": { - "lastModified": 1722362301, - "narHash": "sha256-e60ThtBCc2Y0jNSEgADr0/zZ2R4elsdMAGVGGNejF4c=", + "lastModified": 1725177927, + "narHash": "sha256-l6Wu5dnme8LpkdLYe+/WxKzK5Pgi96Iiuge9wfnzb4E=", "owner": "nix-community", "repo": "NUR", - "rev": "c96302f9ba5788dd1913ea4d001c29ed29101bd2", + "rev": "90b3a926d1c4d52c2d3851702be75cbde4e13a0f", "type": "github" }, "original": { @@ -619,7 +603,6 @@ "root": { "inputs": { "disko": "disko", - "displaylinknixpkgs": "displaylinknixpkgs", "flake-utils": "flake-utils", "home-manager": "home-manager", "nix-on-droid": "nix-on-droid", @@ -627,7 +610,8 @@ "nixpkgs": "nixpkgs", "nixvim": "nixvim", "nur": "nur", - "stylix": "stylix" + "stylix": "stylix", + "unixpkgs": "unixpkgs" } }, "scss-reset": { @@ -700,11 +684,11 @@ ] }, "locked": { - "lastModified": 1722330636, - "narHash": "sha256-uru7JzOa33YlSRwf9sfXpJG+UAV+bnBEYMjrzKrQZFw=", + "lastModified": 1724833132, + "narHash": "sha256-F4djBvyNRAXGusJiNYInqR6zIMI3rvlp6WiKwsRISos=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "768acdb06968e53aa1ee8de207fd955335c754b7", + "rev": "3ffd842a5f50f435d3e603312eefa4790db46af5", "type": "github" }, "original": { @@ -712,6 +696,21 @@ "repo": "treefmt-nix", "type": "github" } + }, + "unixpkgs": { + "locked": { + "lastModified": 1725183711, + "narHash": "sha256-gkjg8FfjL92azt3gzZUm1+v+U4y+wbQE630uIf4Aybo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a2c345850e5e1d96c62e7fa8ca6c9d77ebad1c37", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "master", + "type": "indirect" + } } }, "root": "root",