fix: only start systems once
This commit is contained in:
+2
-2
@@ -20,7 +20,7 @@ from sims.services.screenrec import ScreenrecService
|
||||
from sims.services.smart_corners import get_smart_corners_service
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.circularprogressbar import CircularProgressBar
|
||||
from sims.services.system_stats import SystemStatsService
|
||||
from sims.services.system_stats import get_system_stats_service
|
||||
|
||||
from sims.config import BATTERY, BAR_HEIGHT, WINDOW_TITLE, NOTMUCH, BUDDY, ORG
|
||||
|
||||
@@ -175,7 +175,7 @@ class StatusBar(Window):
|
||||
)
|
||||
self.children = self.inner
|
||||
|
||||
self.system_stats_service = SystemStatsService(update_interval=3000)
|
||||
self.system_stats_service = get_system_stats_service()
|
||||
self.system_stats_service.connect("stats-changed", self.update_progress_bars)
|
||||
|
||||
self.set_size_request(-1, BAR_HEIGHT)
|
||||
|
||||
@@ -2,7 +2,7 @@ from gi.repository import GLib
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.image import Image
|
||||
from sims.services.battery import BatteryService
|
||||
from sims.services.battery import get_battery_service
|
||||
|
||||
|
||||
class Battery(Box):
|
||||
@@ -15,8 +15,9 @@ class Battery(Box):
|
||||
|
||||
self.bat_label = Label(name="bat-label", label="100%")
|
||||
|
||||
# Create battery service with signal-based updates
|
||||
self.battery_service = BatteryService(update_interval=10000) # Check every 10 seconds
|
||||
# Shared battery service with signal-based updates (one poller for the
|
||||
# whole process, including the buddy's battery checks)
|
||||
self.battery_service = get_battery_service()
|
||||
self.battery_service.connect("battery-changed", self.update_battery)
|
||||
|
||||
self.children = [self.bat_icon, self.bat_label]
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
from fabric.widgets.box import Box
|
||||
|
||||
from sims.widgets.buddy import BuddyWidget
|
||||
from sims.services.buddy import BuddyService
|
||||
from sims.services.buddy import get_buddy_service
|
||||
|
||||
|
||||
class Buddy(Box):
|
||||
def __init__(self, notmuch_service=None, **kwargs):
|
||||
super().__init__(name="buddy-container", v_align="center", **kwargs)
|
||||
self.service = BuddyService(notmuch_service=notmuch_service)
|
||||
self.service = get_buddy_service(notmuch_service=notmuch_service)
|
||||
self.sprite = BuddyWidget(on_pet=self._on_pet)
|
||||
self.service.connect("mood-changed", self._on_mood_changed)
|
||||
self.children = [self.sprite]
|
||||
# The shared service only emits on *changes* and may already sit in a
|
||||
# non-idle mood when this output's sprite is built: adopt it now.
|
||||
self.sprite.update_mood(self.service.mood)
|
||||
|
||||
def _on_mood_changed(self, _service, mood: str):
|
||||
self.sprite.update_mood(mood)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from fabric.audio.service import Audio
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.label import Label
|
||||
from sims.services.audio import get_audio_service
|
||||
|
||||
MIC_ICON = ""
|
||||
MIC_MUTED_ICON = ""
|
||||
@@ -17,7 +17,7 @@ class MicrophoneWidget(Box):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.audio = Audio()
|
||||
self.audio = get_audio_service()
|
||||
|
||||
self.icon_label = Label(
|
||||
name="microphone-icon",
|
||||
|
||||
+18
-2
@@ -155,7 +155,7 @@ class NotmuchWidget(Button):
|
||||
)
|
||||
|
||||
# Initialize the service
|
||||
self.service = NotmuchService()
|
||||
self.service = get_notmuch_service()
|
||||
self.service.connect("counts-changed", self.update_display)
|
||||
|
||||
logger.info("[Notmuch] Notmuch widget initialized")
|
||||
@@ -205,4 +205,20 @@ class NotmuchWidget(Button):
|
||||
|
||||
logger.info(
|
||||
f"[Notmuch] Updated display: {unread} unread, {debt} aging — classes={classes}"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
_service: "NotmuchService | None" = None
|
||||
|
||||
|
||||
def get_notmuch_service(update_interval: int = 60000) -> "NotmuchService":
|
||||
"""Return the process-wide notmuch service, creating it on first use.
|
||||
|
||||
Each poll shells out to `notmuch count` twice. Every bar widget used to own
|
||||
a service, so one `refresh-notmuch` (or one timer tick) opened the database
|
||||
2 x bars times over; now all widgets share a single poller.
|
||||
"""
|
||||
global _service
|
||||
if _service is None:
|
||||
_service = NotmuchService(update_interval=update_interval)
|
||||
return _service
|
||||
@@ -14,7 +14,7 @@ from fabric.widgets.overlay import Overlay
|
||||
from fabric.widgets.stack import Stack
|
||||
from ..widgets.circle_image import CircleImage
|
||||
import sims.modules.icons as icons
|
||||
from sims.services.mpris import MprisPlayerManager, MprisPlayer
|
||||
from sims.services.mpris import MprisPlayer, get_mpris_player_manager
|
||||
from fabric import Fabricator
|
||||
|
||||
# from sims.modules.cavalcade import SpectrumRender
|
||||
@@ -434,7 +434,7 @@ class Player(Box):
|
||||
)
|
||||
self.switcher.set_stack(self.player_stack)
|
||||
self.switcher.set_halign(Gtk.Align.CENTER)
|
||||
self.mpris_manager = MprisPlayerManager()
|
||||
self.mpris_manager = get_mpris_player_manager()
|
||||
players = self.mpris_manager.players
|
||||
if players:
|
||||
for p in players:
|
||||
@@ -623,7 +623,7 @@ class PlayerSmall(Box):
|
||||
|
||||
self.add(self.mpris_small)
|
||||
|
||||
self.mpris_manager = MprisPlayerManager()
|
||||
self.mpris_manager = get_mpris_player_manager()
|
||||
self.mpris_player = None
|
||||
self._players = {}
|
||||
self._player_handlers = {}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
"""Shared audio service.
|
||||
|
||||
fabric's `Audio` wraps a libcvc (`Cvc.MixerControl`) client: constructing one
|
||||
opens a PulseAudio/PipeWire connection, subscribes to every stream event and
|
||||
enumerates sinks/sources. Widgets were creating one per output, so keep a
|
||||
single process-wide instance instead.
|
||||
"""
|
||||
|
||||
from fabric.audio.service import Audio
|
||||
|
||||
_service: "Audio | None" = None
|
||||
|
||||
|
||||
def get_audio_service() -> "Audio":
|
||||
"""Return the process-wide Audio service, creating it on first use."""
|
||||
global _service
|
||||
if _service is None:
|
||||
_service = Audio()
|
||||
return _service
|
||||
@@ -69,4 +69,19 @@ class BatteryService(Service):
|
||||
@property
|
||||
def charging(self):
|
||||
"""Get current charging status"""
|
||||
return self._charging
|
||||
return self._charging
|
||||
|
||||
|
||||
_service: "BatteryService | None" = None
|
||||
|
||||
|
||||
def get_battery_service(update_interval: int = 10000) -> "BatteryService":
|
||||
"""Return the process-wide battery service, creating it on first use.
|
||||
|
||||
Only the first caller's `update_interval` wins, so the bar widget (10s) and
|
||||
the buddy (15s) share one psutil poll instead of running their own.
|
||||
"""
|
||||
global _service
|
||||
if _service is None:
|
||||
_service = BatteryService(update_interval=update_interval)
|
||||
return _service
|
||||
+27
-6
@@ -10,9 +10,9 @@ from datetime import datetime
|
||||
from fabric.core.service import Service, Signal
|
||||
from fabric.utils import invoke_repeater
|
||||
|
||||
from sims.services.system_stats import SystemStatsService
|
||||
from sims.services.battery import BatteryService
|
||||
from sims.services.mpris import MprisPlayerManager, MprisPlayer
|
||||
from sims.services.battery import get_battery_service
|
||||
from sims.services.mpris import MprisPlayer, get_mpris_player_manager
|
||||
from sims.services.system_stats import get_system_stats_service
|
||||
from sims.config import BATTERY, NOTMUCH
|
||||
|
||||
|
||||
@@ -43,19 +43,19 @@ class BuddyService(Service):
|
||||
self._excited_until = 0.0
|
||||
self._last_unread = None
|
||||
|
||||
self._stats = SystemStatsService(update_interval=3000)
|
||||
self._stats = get_system_stats_service()
|
||||
self._stats.connect("stats-changed", self._on_stats)
|
||||
|
||||
self._battery = None
|
||||
if BATTERY.get("enable", False):
|
||||
self._battery = BatteryService(update_interval=15000)
|
||||
self._battery = get_battery_service()
|
||||
self._battery.connect("battery-changed", self._on_battery)
|
||||
|
||||
self._notmuch = notmuch_service
|
||||
if self._notmuch is not None:
|
||||
self._notmuch.connect("counts-changed", self._on_notmuch)
|
||||
|
||||
self._mpris_manager = MprisPlayerManager()
|
||||
self._mpris_manager = get_mpris_player_manager()
|
||||
self._mpris_players: dict[str, MprisPlayer] = {}
|
||||
for p in self._mpris_manager.players or []:
|
||||
self._attach_player(p)
|
||||
@@ -67,6 +67,11 @@ class BuddyService(Service):
|
||||
|
||||
# External triggers ---------------------------------------------------
|
||||
|
||||
@property
|
||||
def mood(self) -> str:
|
||||
"""Currently arbitrated mood."""
|
||||
return self._mood
|
||||
|
||||
def pet(self):
|
||||
"""Called when the buddy is clicked."""
|
||||
self._petted_until = time.monotonic() + PETTED_DURATION_S
|
||||
@@ -146,3 +151,19 @@ class BuddyService(Service):
|
||||
self._mood = mood
|
||||
self.mood_changed(mood)
|
||||
return
|
||||
|
||||
|
||||
_service: "BuddyService | None" = None
|
||||
|
||||
|
||||
def get_buddy_service(notmuch_service=None) -> "BuddyService":
|
||||
"""Return the process-wide buddy service, creating it on first use.
|
||||
|
||||
`notmuch_service` only applies to the first caller (the bar that owns the
|
||||
notmuch widget). One arbitrated mood and one re-arbitration ticker are
|
||||
shared by every output instead of one per bar.
|
||||
"""
|
||||
global _service
|
||||
if _service is None:
|
||||
_service = BuddyService(notmuch_service=notmuch_service)
|
||||
return _service
|
||||
|
||||
@@ -287,3 +287,19 @@ class MprisPlayerManager(Service):
|
||||
@Property(object, "readable")
|
||||
def players(self):
|
||||
return self._manager.get_property("players") # type: ignore
|
||||
|
||||
|
||||
_service: "MprisPlayerManager | None" = None
|
||||
|
||||
|
||||
def get_mpris_player_manager() -> "MprisPlayerManager":
|
||||
"""Return the process-wide MPRIS manager, creating it on first use.
|
||||
|
||||
`Playerctl.PlayerManager.new()` plus one `Playerctl.Player` per MPRIS name
|
||||
is the expensive part; every widget that wraps players should share this
|
||||
manager and build its own `MprisPlayer` wrappers on top of it.
|
||||
"""
|
||||
global _service
|
||||
if _service is None:
|
||||
_service = MprisPlayerManager()
|
||||
return _service
|
||||
|
||||
@@ -64,3 +64,19 @@ class SystemStatsService(Service):
|
||||
def memory_percent(self):
|
||||
"""Get current memory percentage"""
|
||||
return self._memory_percent / 100
|
||||
|
||||
|
||||
_service: "SystemStatsService | None" = None
|
||||
|
||||
|
||||
def get_system_stats_service(update_interval: int = 3000) -> "SystemStatsService":
|
||||
"""Return the process-wide stats service, creating it on first use.
|
||||
|
||||
`update_interval` only applies to the first caller: one poller feeds every
|
||||
bar, and psutil's non-blocking `cpu_percent` keeps a process-global baseline
|
||||
that extra pollers would keep resetting.
|
||||
"""
|
||||
global _service
|
||||
if _service is None:
|
||||
_service = SystemStatsService(update_interval=update_interval)
|
||||
return _service
|
||||
|
||||
Reference in New Issue
Block a user