frobar: Mutli-display support
Freaking finally
This commit is contained in:
parent
e09774c4ca
commit
445c2b8a99
|
@ -1,11 +1,21 @@
|
|||
{ pkgs ? import <nixpkgs> { config = { }; overlays = [ ]; }, ... }:
|
||||
let
|
||||
lemonbar = (pkgs.lemonbar-xft.overrideAttrs (old: {
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "drscream";
|
||||
repo = "lemonbar-xft";
|
||||
rev = "a64a2a6a6d643f4d92f9d7600722710eebce7bdb";
|
||||
sha256 = "sha256-T5FhEPIiDt/9paJwL9Sj84CBtA0YFi1hZz0+87Hd6jU=";
|
||||
# https://github.com/drscream/lemonbar-xft/pull/2
|
||||
};
|
||||
}));
|
||||
in
|
||||
# Tried using pyproject.nix but mpd2 dependency wouldn't resolve,
|
||||
# is called pyton-mpd2 on PyPi but mpd2 in nixpkgs.
|
||||
# is called pyton-mpd2 on PyPi but mpd2 in nixpkgs.
|
||||
pkgs.python3Packages.buildPythonApplication {
|
||||
pname = "frobar";
|
||||
version = "2.0";
|
||||
|
||||
runtimeInputs = with pkgs; [ lemonbar-xft wirelesstools ];
|
||||
propagatedBuildInputs = with pkgs.python3Packages; [
|
||||
coloredlogs
|
||||
notmuch
|
||||
|
@ -15,7 +25,7 @@ pkgs.python3Packages.buildPythonApplication {
|
|||
pulsectl
|
||||
pyinotify
|
||||
];
|
||||
makeWrapperArgs = [ "--prefix PATH : ${pkgs.lib.makeBinPath (with pkgs; [ lemonbar-xft wirelesstools ])}" ];
|
||||
makeWrapperArgs = [ "--prefix PATH : ${pkgs.lib.makeBinPath ([ lemonbar ] ++ (with pkgs; [ wirelesstools ]))}" ];
|
||||
|
||||
src = ./.;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ def run() -> None:
|
|||
Bar.init()
|
||||
Updater.init()
|
||||
|
||||
# Bar.addSectionAll(fp.CpuProvider(), BarGroupType.RIGHT)
|
||||
# Bar.addSectionAll(fp.NetworkProvider(theme=2), BarGroupType.RIGHT)
|
||||
|
||||
WORKSPACE_THEME = 8
|
||||
FOCUS_THEME = 2
|
||||
URGENT_THEME = 0
|
||||
|
|
|
@ -12,7 +12,7 @@ import typing
|
|||
import coloredlogs
|
||||
import i3ipc
|
||||
|
||||
from frobar.notbusy import notBusy
|
||||
from frobar.common import notBusy
|
||||
|
||||
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
|
||||
log = logging.getLogger()
|
||||
|
@ -70,9 +70,19 @@ class Bar:
|
|||
@staticmethod
|
||||
def init() -> None:
|
||||
Bar.running = True
|
||||
Bar.everyone = set()
|
||||
Section.init()
|
||||
|
||||
cmd = ["lemonbar", "-b", "-a", "64"]
|
||||
cmd = [
|
||||
"lemonbar",
|
||||
"-b",
|
||||
"-a",
|
||||
"64",
|
||||
"-F",
|
||||
Section.FGCOLOR,
|
||||
"-B",
|
||||
Section.BGCOLOR,
|
||||
]
|
||||
for font in Bar.FONTS:
|
||||
cmd += ["-f", "{}:size={}".format(font, Bar.FONTSIZE)]
|
||||
Bar.process = subprocess.Popen(
|
||||
|
@ -80,9 +90,11 @@ class Bar:
|
|||
)
|
||||
BarStdoutThread().start()
|
||||
|
||||
# Debug
|
||||
Bar(0)
|
||||
# Bar(1)
|
||||
i3 = i3ipc.Connection()
|
||||
for output in i3.get_outputs():
|
||||
if not output.active:
|
||||
continue
|
||||
Bar(output.name)
|
||||
|
||||
@staticmethod
|
||||
def stop() -> None:
|
||||
|
@ -99,17 +111,15 @@ class Bar:
|
|||
|
||||
def doStop(*args: list) -> None:
|
||||
Bar.stop()
|
||||
print(88)
|
||||
|
||||
try:
|
||||
i3.on("ipc_shutdown", doStop)
|
||||
i3.main()
|
||||
except BaseException:
|
||||
print(93)
|
||||
Bar.stop()
|
||||
|
||||
# Class globals
|
||||
everyone: set["Bar"] = set()
|
||||
everyone: set["Bar"]
|
||||
string = ""
|
||||
process: subprocess.Popen
|
||||
running = False
|
||||
|
@ -137,8 +147,8 @@ class Bar:
|
|||
Bar.process.wait()
|
||||
Bar.stop()
|
||||
|
||||
def __init__(self, screen: int) -> None:
|
||||
self.screen = "%{S" + str(screen) + "}"
|
||||
def __init__(self, output: str) -> None:
|
||||
self.output = output
|
||||
self.groups = dict()
|
||||
|
||||
for groupType in BarGroupType:
|
||||
|
@ -146,27 +156,26 @@ class Bar:
|
|||
self.groups[groupType] = group
|
||||
|
||||
self.childsChanged = False
|
||||
|
||||
self.everyone.add(self)
|
||||
Bar.everyone.add(self)
|
||||
|
||||
@staticmethod
|
||||
def addSectionAll(
|
||||
section: "Section", group: "BarGroupType", screens: None = None
|
||||
section: "Section", group: "BarGroupType"
|
||||
) -> None:
|
||||
"""
|
||||
.. note::
|
||||
Add the section before updating it for the first time.
|
||||
"""
|
||||
# TODO screens selection
|
||||
for bar in Bar.everyone:
|
||||
bar.addSection(section, group=group)
|
||||
section.added()
|
||||
|
||||
def addSection(self, section: "Section", group: "BarGroupType") -> None:
|
||||
self.groups[group].addSection(section)
|
||||
|
||||
def update(self) -> None:
|
||||
if self.childsChanged:
|
||||
self.string = self.screen
|
||||
self.string = "%{Sn" + self.output + "}"
|
||||
self.string += self.groups[BarGroupType.LEFT].string
|
||||
self.string += self.groups[BarGroupType.RIGHT].string
|
||||
|
||||
|
@ -182,9 +191,10 @@ class Bar:
|
|||
# Color for empty sections
|
||||
Bar.string += BarGroup.color(*Section.EMPTY)
|
||||
|
||||
# print(Bar.string)
|
||||
string = Bar.string + "\n"
|
||||
# print(string)
|
||||
assert Bar.process.stdin
|
||||
Bar.process.stdin.write(bytes(Bar.string + "\n", "utf-8"))
|
||||
Bar.process.stdin.write(string.encode())
|
||||
Bar.process.stdin.flush()
|
||||
|
||||
|
||||
|
@ -410,6 +420,9 @@ class Section:
|
|||
for parent in self.parents:
|
||||
parent.addSectionAfter(self, section)
|
||||
|
||||
def added(self) -> None:
|
||||
pass
|
||||
|
||||
def informParentsThemeChanged(self) -> None:
|
||||
for parent in self.parents:
|
||||
parent.childsThemeChanged = True
|
||||
|
|
|
@ -18,7 +18,7 @@ import notmuch
|
|||
import psutil
|
||||
import pulsectl
|
||||
|
||||
from frobar.display import (BarGroup, ColorCountsSection, Element, Section,
|
||||
from frobar.display import (ColorCountsSection, Element, Section,
|
||||
StatefulSection, Text)
|
||||
from frobar.updaters import (I3Updater, InotifyUpdater, MergedUpdater,
|
||||
PeriodicUpdater, ThreadedUpdater, Updater)
|
||||
|
@ -448,10 +448,6 @@ class NetworkProvider(Section, PeriodicUpdater):
|
|||
|
||||
return None
|
||||
|
||||
def addParent(self, parent: BarGroup) -> None:
|
||||
self.parents.add(parent)
|
||||
self.refreshData()
|
||||
|
||||
def __init__(self, theme: int | None = None):
|
||||
PeriodicUpdater.__init__(self)
|
||||
Section.__init__(self, theme)
|
||||
|
@ -670,10 +666,12 @@ class I3WindowTitleProvider(Section, I3Updater):
|
|||
|
||||
class I3WorkspacesProviderSection(Section):
|
||||
def selectTheme(self) -> int:
|
||||
if self.urgent:
|
||||
if self.workspace.urgent:
|
||||
return self.parent.themeUrgent
|
||||
elif self.focused:
|
||||
elif self.workspace.focused:
|
||||
return self.parent.themeFocus
|
||||
elif self.workspace.visible:
|
||||
return self.parent.themeVisible
|
||||
else:
|
||||
return self.parent.themeNormal
|
||||
|
||||
|
@ -682,26 +680,21 @@ class I3WorkspacesProviderSection(Section):
|
|||
|
||||
def show(self) -> None:
|
||||
self.updateTheme(self.selectTheme())
|
||||
self.updateText(self.fullName if self.focused else self.shortName)
|
||||
|
||||
def changeState(self, focused: bool, urgent: bool) -> None:
|
||||
self.focused = focused
|
||||
self.urgent = urgent
|
||||
self.show()
|
||||
|
||||
def setName(self, name: str) -> None:
|
||||
self.shortName = name
|
||||
self.fullName: str = (
|
||||
self.parent.customNames[name] if name in self.parent.customNames else name
|
||||
self.updateText(
|
||||
self.fullName if self.workspace.focused else self.workspace.name
|
||||
)
|
||||
|
||||
def switchTo(self) -> None:
|
||||
self.parent.i3.command("workspace {}".format(self.shortName))
|
||||
self.parent.i3.command("workspace {}".format(self.workspace.name))
|
||||
|
||||
def __init__(self, name: str, parent: "I3WorkspacesProvider"):
|
||||
def updateWorkspace(self, workspace: i3ipc.WorkspaceReply) -> None:
|
||||
self.workspace = workspace
|
||||
self.fullName: str = self.parent.customNames.get(workspace.name, workspace.name)
|
||||
self.show()
|
||||
|
||||
def __init__(self, parent: "I3WorkspacesProvider"):
|
||||
Section.__init__(self)
|
||||
self.parent = parent
|
||||
self.setName(name)
|
||||
self.setDecorators(clickLeft=self.switchTo)
|
||||
self.tempText: Element = None
|
||||
|
||||
|
@ -718,64 +711,50 @@ class I3WorkspacesProviderSection(Section):
|
|||
|
||||
|
||||
class I3WorkspacesProvider(Section, I3Updater):
|
||||
# TODO FEAT Multi-screen
|
||||
|
||||
def initialPopulation(self, parent: BarGroup) -> None:
|
||||
"""
|
||||
Called on init
|
||||
Can't reuse addWorkspace since i3.get_workspaces() gives dict and not
|
||||
ConObjects
|
||||
"""
|
||||
workspaces = self.i3.get_workspaces()
|
||||
lastSection = self.modeSection
|
||||
for workspace in workspaces:
|
||||
# if parent.display != workspace["display"]:
|
||||
# continue
|
||||
|
||||
section = I3WorkspacesProviderSection(workspace.name, self)
|
||||
section.focused = workspace.focused
|
||||
section.urgent = workspace.urgent
|
||||
section.show()
|
||||
parent.addSectionAfter(lastSection, section)
|
||||
self.sections[workspace.num] = section
|
||||
|
||||
lastSection = section
|
||||
|
||||
def on_workspace_init(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None:
|
||||
workspace = e.current
|
||||
i = workspace.num
|
||||
if i in self.sections:
|
||||
section = self.sections[i]
|
||||
def updateWorkspace(self, workspace: i3ipc.WorkspaceReply) -> None:
|
||||
section: Section | None = None
|
||||
lastSectionOnOutput = self.modeSection
|
||||
highestNumOnOutput = -1
|
||||
for sect in self.sections.values():
|
||||
if sect.workspace.num == workspace.num:
|
||||
section = sect
|
||||
break
|
||||
elif (
|
||||
sect.workspace.num > highestNumOnOutput
|
||||
and sect.workspace.num < workspace.num
|
||||
and sect.workspace.output == workspace.output
|
||||
):
|
||||
lastSectionOnOutput = sect
|
||||
highestNumOnOutput = sect.workspace.num
|
||||
else:
|
||||
# Find the section just before
|
||||
while i not in self.sections.keys() and i > 0:
|
||||
i -= 1
|
||||
prevSection = self.sections[i] if i != 0 else self.modeSection
|
||||
|
||||
section = I3WorkspacesProviderSection(workspace.name, self)
|
||||
prevSection.appendAfter(section)
|
||||
section = I3WorkspacesProviderSection(self)
|
||||
self.sections[workspace.num] = section
|
||||
section.focused = workspace.focused
|
||||
section.urgent = workspace.urgent
|
||||
section.show()
|
||||
|
||||
for bargroup in self.parents:
|
||||
if bargroup.parent.output == workspace.output:
|
||||
break
|
||||
else:
|
||||
bargroup = list(self.parents)[0]
|
||||
bargroup.addSectionAfter(lastSectionOnOutput, section)
|
||||
section.updateWorkspace(workspace)
|
||||
|
||||
def updateWorkspaces(self) -> None:
|
||||
workspaces = self.i3.get_workspaces()
|
||||
for workspace in workspaces:
|
||||
self.updateWorkspace(workspace)
|
||||
|
||||
def added(self) -> None:
|
||||
super().added()
|
||||
self.appendAfter(self.modeSection)
|
||||
self.updateWorkspaces()
|
||||
|
||||
def on_workspace_change(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None:
|
||||
self.updateWorkspaces()
|
||||
|
||||
def on_workspace_empty(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None:
|
||||
self.sections[e.current.num].empty()
|
||||
|
||||
def on_workspace_focus(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None:
|
||||
self.sections[e.old.num].focused = False
|
||||
self.sections[e.old.num].show()
|
||||
self.sections[e.current.num].focused = True
|
||||
self.sections[e.current.num].show()
|
||||
|
||||
def on_workspace_urgent(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None:
|
||||
self.sections[e.current.num].urgent = e.current.urgent
|
||||
self.sections[e.current.num].show()
|
||||
|
||||
def on_workspace_rename(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None:
|
||||
self.sections[e.current.num].setName(e.name)
|
||||
self.sections[e.current.num].show()
|
||||
|
||||
def on_mode(self, i3: i3ipc.Connection, e: i3ipc.Event) -> None:
|
||||
if e.change == "default":
|
||||
self.modeSection.updateText(None)
|
||||
|
@ -789,6 +768,7 @@ class I3WorkspacesProvider(Section, I3Updater):
|
|||
def __init__(
|
||||
self,
|
||||
theme: int = 0,
|
||||
themeVisible: int = 4,
|
||||
themeFocus: int = 3,
|
||||
themeUrgent: int = 1,
|
||||
themeMode: int = 2,
|
||||
|
@ -799,24 +779,23 @@ class I3WorkspacesProvider(Section, I3Updater):
|
|||
self.themeNormal = theme
|
||||
self.themeFocus = themeFocus
|
||||
self.themeUrgent = themeUrgent
|
||||
self.themeVisible = themeVisible
|
||||
self.customNames = customNames
|
||||
|
||||
self.sections: dict[str, I3WorkspacesProviderSection] = dict()
|
||||
self.on("workspace::init", self.on_workspace_init)
|
||||
self.on("workspace::focus", self.on_workspace_focus)
|
||||
self.sections: dict[int, I3WorkspacesProviderSection] = dict()
|
||||
# The event object doesn't have the visible property,
|
||||
# so we have to fetch the list of workspaces anyways.
|
||||
# This sacrifices a bit of performance for code simplicity.
|
||||
self.on("workspace::init", self.on_workspace_change)
|
||||
self.on("workspace::focus", self.on_workspace_change)
|
||||
self.on("workspace::empty", self.on_workspace_empty)
|
||||
self.on("workspace::urgent", self.on_workspace_urgent)
|
||||
self.on("workspace::rename", self.on_workspace_rename)
|
||||
self.on("workspace::urgent", self.on_workspace_change)
|
||||
self.on("workspace::rename", self.on_workspace_change)
|
||||
# TODO Un-handled/tested: reload, rename, restored, move
|
||||
|
||||
self.on("mode", self.on_mode)
|
||||
self.modeSection = Section(theme=themeMode)
|
||||
|
||||
def addParent(self, parent: BarGroup) -> None:
|
||||
self.parents.add(parent)
|
||||
parent.addSection(self.modeSection)
|
||||
self.initialPopulation(parent)
|
||||
|
||||
|
||||
class MpdProvider(Section, ThreadedUpdater):
|
||||
# TODO FEAT More informations and controls
|
||||
|
|
|
@ -12,7 +12,7 @@ import i3ipc
|
|||
import pyinotify
|
||||
|
||||
from frobar.display import Element
|
||||
from frobar.notbusy import notBusy
|
||||
from frobar.common import notBusy
|
||||
|
||||
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
|
||||
log = logging.getLogger()
|
||||
|
|
Loading…
Reference in a new issue