init
This commit is contained in:
117
bar/river/service.py
Normal file
117
bar/river/service.py
Normal file
@@ -0,0 +1,117 @@
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List
|
||||
|
||||
from fabric.core.service import Service, Signal, Property
|
||||
from pywayland.client import Display
|
||||
from pywayland.protocol.wayland import WlOutput, WlSeat
|
||||
from ..generated.river_status_unstable_v1 import (
|
||||
ZriverStatusManagerV1,
|
||||
ZriverOutputStatusV1,
|
||||
ZriverSeatStatusV1,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class OutputState:
|
||||
id: int
|
||||
output: WlOutput
|
||||
status: ZriverOutputStatusV1 = None
|
||||
focused_tags: List[int] = field(default_factory=list)
|
||||
view_tags: List[int] = field(default_factory=list)
|
||||
|
||||
|
||||
class River(Service):
|
||||
@Property(bool, "readable", "is-ready", default_value=False)
|
||||
def ready(self) -> bool:
|
||||
return self._ready
|
||||
|
||||
@Signal
|
||||
def ready(self):
|
||||
return self.notify("ready")
|
||||
|
||||
@Signal("event", flags="detailed")
|
||||
def event(self, event: object): ...
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._ready = False
|
||||
self.display = None
|
||||
self.registry = None
|
||||
self.outputs: Dict[int, OutputState] = {}
|
||||
self.status_mgr = None
|
||||
self.seat = None
|
||||
self.seat_status: ZriverSeatStatusV1 = None
|
||||
self.ready.emit()
|
||||
|
||||
def on_start(self):
|
||||
print("✅ River.on_start called")
|
||||
|
||||
logger.info("[RiverService] Starting River service...")
|
||||
self.display = Display()
|
||||
self.display.connect()
|
||||
self.registry = self.display.get_registry()
|
||||
|
||||
self.registry.dispatcher["global"] = self._on_global
|
||||
self.registry.dispatcher["global_remove"] = lambda *_: None
|
||||
|
||||
self.display.roundtrip()
|
||||
|
||||
if self.seat and self.status_mgr:
|
||||
self.seat_status = self.status_mgr.get_river_seat_status(self.seat)
|
||||
|
||||
for id, output_state in self.outputs.items():
|
||||
status = self.status_mgr.get_river_output_status(output_state.output)
|
||||
output_state.status = status
|
||||
status.dispatcher["focused_tags"] = self._make_focused_handler(id)
|
||||
status.dispatcher["view_tags"] = self._make_view_handler(id)
|
||||
|
||||
self.display.roundtrip()
|
||||
|
||||
self._ready = True
|
||||
self.ready.emit()
|
||||
logger.info("[RiverService] Ready. Monitoring tags.")
|
||||
|
||||
def on_tick(self):
|
||||
# Periodic poll
|
||||
self.display.roundtrip()
|
||||
|
||||
def _on_global(self, registry, name, interface, version):
|
||||
if interface == "wl_output":
|
||||
output = registry.bind(name, WlOutput, version)
|
||||
self.outputs[name] = OutputState(id=name, output=output)
|
||||
|
||||
elif interface == "wl_seat":
|
||||
self.seat = registry.bind(name, WlSeat, version)
|
||||
|
||||
elif interface == "zriver_status_manager_v1":
|
||||
self.status_mgr = registry.bind(name, ZriverStatusManagerV1, version)
|
||||
|
||||
def _make_focused_handler(self, output_id):
|
||||
def handler(_, bitfield):
|
||||
tags = self._decode_bitfield(bitfield)
|
||||
self.outputs[output_id].focused_tags = tags
|
||||
logger.debug(f"[RiverService] Output {output_id} focused: {tags}")
|
||||
self.emit(f"event::focused_tags::{output_id}", tags)
|
||||
|
||||
return handler
|
||||
|
||||
def _make_view_handler(self, output_id):
|
||||
def handler(_, array):
|
||||
tags = self._decode_array_bitfields(array)
|
||||
self.outputs[output_id].view_tags = tags
|
||||
logger.debug(f"[RiverService] Output {output_id} views: {tags}")
|
||||
self.emit(f"event::view_tags::{output_id}", tags)
|
||||
|
||||
return handler
|
||||
|
||||
def _decode_bitfield(self, bits: int) -> List[int]:
|
||||
return [i for i in range(32) if bits & (1 << i)]
|
||||
|
||||
def _decode_array_bitfields(self, array) -> List[int]:
|
||||
tags = set()
|
||||
for bits in array:
|
||||
tags.update(self._decode_bitfield(bits))
|
||||
return sorted(tags)
|
||||
Reference in New Issue
Block a user