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