refactor! : drop subprocess usage and centralize brightness service initialization
This commit is contained in:
21 files changed
+834
-828
No files matched your search
@@ -1,7 +1,6 @@
|
||||
import subprocess
|
||||
from enum import Enum, auto
|
||||
|
||||
from fabric.utils import Gdk, GLib, exec_shell_command, logger
|
||||
from fabric.utils import Gdk, GLib, Gtk, exec_shell_command, logger
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.centerbox import CenterBox
|
||||
@@ -12,6 +11,7 @@ from fabric.widgets.separator import Separator
|
||||
from services.bluetooth import BluetoothClient, BluetoothDevice
|
||||
from shared.widgets.smooth_switch import SmoothSwitch
|
||||
from shared.window.animated_scrollwindow import AnimatedScrollable
|
||||
from utils.functions import spawn_detached
|
||||
from utils.utils import svg_file
|
||||
|
||||
|
||||
@@ -290,8 +290,6 @@ class BluetoothDeviceSlot(Box):
|
||||
if event.button != 3: # only right-click
|
||||
return False
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
menu = Gtk.Menu()
|
||||
|
||||
if not self.device.paired:
|
||||
@@ -567,7 +565,7 @@ class BluetoothConnections(Box):
|
||||
def open_bluetooth_settings(self, *_):
|
||||
"""Open Blueman bluetooth manager"""
|
||||
try:
|
||||
subprocess.Popen(["blueman-manager"], start_new_session=True)
|
||||
spawn_detached(["blueman-manager"])
|
||||
if self.parent and hasattr(self.parent, "hide_controlcenter"):
|
||||
self.parent.hide_controlcenter()
|
||||
except FileNotFoundError:
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import subprocess
|
||||
|
||||
from fabric.utils import Gdk, GLib, get_relative_path, idle_add, logger
|
||||
from fabric.utils import Gdk, GLib, idle_add, logger
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.centerbox import CenterBox
|
||||
@@ -9,6 +7,7 @@ from fabric.widgets.label import Label
|
||||
from fabric.widgets.overlay import Overlay
|
||||
|
||||
from services.brightness import Brightness
|
||||
from services.modus import is_caffeine_active, toggle_caffeine
|
||||
from services.network import NetworkClient
|
||||
from shared.widgets.flat_scale import FlatScale
|
||||
from shared.window.applet_window import AppletWindow
|
||||
@@ -24,7 +23,14 @@ from window.controlcenter.per_app_volume import PerAppVolumeControl
|
||||
from window.controlcenter.player import PlayerBoxStack, get_shared_mpris_manager
|
||||
from window.controlcenter.wifi import WifiConnections
|
||||
|
||||
brightness_service = Brightness()
|
||||
_brightness_instance: "Brightness | None" = None
|
||||
|
||||
|
||||
def get_brightness_service() -> "Brightness":
|
||||
global _brightness_instance
|
||||
if _brightness_instance is None:
|
||||
_brightness_instance = Brightness()
|
||||
return _brightness_instance
|
||||
|
||||
|
||||
class ModusControlCenter(AppletWindow):
|
||||
@@ -48,8 +54,6 @@ class ModusControlCenter(AppletWindow):
|
||||
# Flight mode and caffeine states
|
||||
self.flight_mode = False
|
||||
self.caffeine_mode = False
|
||||
self._caffeine_process = None
|
||||
|
||||
# Loading flags - music and expanded player are no longer lazy loaded
|
||||
self._per_app_volume_initialized = False
|
||||
self._signals_connected = False # Track if signals are connected
|
||||
@@ -111,10 +115,10 @@ class ModusControlCenter(AppletWindow):
|
||||
h_expand=True,
|
||||
)
|
||||
|
||||
current_brightness = brightness_service.screen_brightness
|
||||
current_brightness = get_brightness_service().screen_brightness
|
||||
brightness_percentage = (
|
||||
int((current_brightness / brightness_service.max_screen) * 100)
|
||||
if brightness_service.max_screen > 0
|
||||
int((current_brightness / get_brightness_service().max_screen) * 100)
|
||||
if get_brightness_service().max_screen > 0
|
||||
else 50
|
||||
)
|
||||
|
||||
@@ -129,7 +133,7 @@ class ModusControlCenter(AppletWindow):
|
||||
)
|
||||
|
||||
# Only connect brightness controls if brightness service is available
|
||||
if brightness_service.max_screen > 0:
|
||||
if get_brightness_service().max_screen > 0:
|
||||
pass
|
||||
else:
|
||||
# Disable brightness scale if no backlight device available
|
||||
@@ -577,13 +581,15 @@ class ModusControlCenter(AppletWindow):
|
||||
)
|
||||
|
||||
# Connect brightness controls if brightness service is available
|
||||
if brightness_service.max_screen > 0:
|
||||
if get_brightness_service().max_screen > 0:
|
||||
self.brightness_scale.connect("value-changed", self.set_brightness)
|
||||
self.brightness_scale.connect("scroll-event", self.on_brightness_scroll)
|
||||
self._signal_connections.append(
|
||||
(
|
||||
brightness_service,
|
||||
brightness_service.connect("screen", self.brightness_changed),
|
||||
get_brightness_service(),
|
||||
get_brightness_service().connect(
|
||||
"screen", self.brightness_changed
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -715,23 +721,8 @@ class ModusControlCenter(AppletWindow):
|
||||
self.per_app_volume_center_box.set_size_request(300, -1)
|
||||
|
||||
def _check_initial_states(self):
|
||||
# Check if caffeine is already running - use faster method
|
||||
try:
|
||||
# Use faster check with timeout
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", "modus-inhibit"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=0.5, # Add timeout to prevent hanging
|
||||
)
|
||||
self.caffeine_mode = bool(result.stdout.strip())
|
||||
except (subprocess.TimeoutExpired, Exception):
|
||||
self.caffeine_mode = False
|
||||
|
||||
# Flight mode starts as False (normal mode)
|
||||
self.caffeine_mode = is_caffeine_active()
|
||||
self.flight_mode = False
|
||||
|
||||
# Update initial labels (will be set after widgets are created)
|
||||
GLib.timeout_add(100, self._update_initial_labels)
|
||||
|
||||
def _update_initial_labels(self):
|
||||
@@ -778,30 +769,13 @@ class ModusControlCenter(AppletWindow):
|
||||
|
||||
def toggle_caffeine(self, *_):
|
||||
try:
|
||||
if self.caffeine_mode:
|
||||
inhibit_script = get_relative_path("../../utils/inhibit.py")
|
||||
subprocess.run(["python3", inhibit_script, "off"], check=False)
|
||||
self.caffeine_mode = False
|
||||
if self._caffeine_process:
|
||||
try:
|
||||
self._caffeine_process.terminate()
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred: {e}")
|
||||
self._caffeine_process = None
|
||||
else:
|
||||
inhibit_script = get_relative_path("../../utils/inhibit.py")
|
||||
self._caffeine_process = subprocess.Popen(
|
||||
["python3", inhibit_script, "on"], start_new_session=True
|
||||
)
|
||||
self.caffeine_mode = True
|
||||
|
||||
self.caffeine_mode = toggle_caffeine()
|
||||
self.caffeine_icon.dynamic_file(
|
||||
"applets/caffeine-on.svg"
|
||||
if self.caffeine_mode
|
||||
else "applets/caffeine-off.svg"
|
||||
)
|
||||
self.caffeine_status_label.set_label("On" if self.caffeine_mode else "Off")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to toggle caffeine: {e}")
|
||||
|
||||
@@ -816,17 +790,17 @@ class ModusControlCenter(AppletWindow):
|
||||
if not self._signals_connected:
|
||||
return
|
||||
self._updating_brightness = True
|
||||
brightness_value = int((brightness / 100) * brightness_service.max_screen)
|
||||
brightness_service.screen_brightness = brightness_value
|
||||
brightness_value = int((brightness / 100) * get_brightness_service().max_screen)
|
||||
get_brightness_service().screen_brightness = brightness_value
|
||||
self._updating_brightness = False
|
||||
|
||||
def brightness_changed(self, _, brightness_value):
|
||||
if not self._signals_connected or self._updating_brightness:
|
||||
return
|
||||
|
||||
if brightness_service.max_screen > 0:
|
||||
if get_brightness_service().max_screen > 0:
|
||||
brightness_percentage = int(
|
||||
(brightness_value / brightness_service.max_screen) * 100
|
||||
(brightness_value / get_brightness_service().max_screen) * 100
|
||||
)
|
||||
|
||||
GLib.idle_add(
|
||||
@@ -1214,22 +1188,7 @@ class ModusControlCenter(AppletWindow):
|
||||
logger.warning(f"Widget cleanup failed: {e}")
|
||||
|
||||
def _cleanup_processes(self):
|
||||
"""Clean up any running processes"""
|
||||
try:
|
||||
# Clean up caffeine process
|
||||
if self._caffeine_process:
|
||||
try:
|
||||
self._caffeine_process.terminate()
|
||||
self._caffeine_process.wait(timeout=1)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to terminate caffeine process: {e}")
|
||||
finally:
|
||||
self._caffeine_process = None
|
||||
|
||||
logger.debug("All processes cleaned up successfully")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Process cleanup failed: {e}")
|
||||
logger.debug("All processes cleaned up successfully")
|
||||
|
||||
def _complete_cleanup(self):
|
||||
"""Perform complete cleanup of all resources"""
|
||||
|
||||
@@ -1,86 +1,30 @@
|
||||
import subprocess
|
||||
|
||||
from fabric.utils import logger
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.label import Label
|
||||
|
||||
from services.modus import (
|
||||
is_night_light_active,
|
||||
toggle_night_light,
|
||||
)
|
||||
from utils.utils import svg_file
|
||||
|
||||
|
||||
class NightLightControl:
|
||||
"""Control night light using hyprsunset"""
|
||||
|
||||
def __init__(self):
|
||||
self.is_active = False
|
||||
self._check_initial_state()
|
||||
|
||||
def _check_initial_state(self):
|
||||
"""Check if hyprsunset is currently running"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", "hyprsunset"], capture_output=True, text=True
|
||||
)
|
||||
self.is_active = bool(result.stdout.strip())
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to check hyprsunset status: {e}")
|
||||
self.is_active = False
|
||||
|
||||
def toggle(self):
|
||||
"""Toggle night light on/off"""
|
||||
try:
|
||||
if self.is_active:
|
||||
# Turn off night light by killing hyprsunset
|
||||
subprocess.run(["pkill", "hyprsunset"], check=False)
|
||||
self.is_active = False
|
||||
logger.debug("Night light turned off")
|
||||
else:
|
||||
# Turn on night light with default temperature (3000K)
|
||||
subprocess.Popen(["hyprsunset", "-t", "4500"], start_new_session=True)
|
||||
self.is_active = True
|
||||
logger.debug("Night light turned on")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to toggle night light: {e}")
|
||||
return False
|
||||
|
||||
def set_temperature(self, temperature: int):
|
||||
"""Set night light temperature (1000-6500K)"""
|
||||
try:
|
||||
# Kill existing process
|
||||
subprocess.run(["pkill", "hyprsunset"], check=False)
|
||||
|
||||
# Start with new temperature
|
||||
subprocess.Popen(
|
||||
["hyprsunset", "-t", str(temperature)], start_new_session=True
|
||||
)
|
||||
self.is_active = True
|
||||
logger.debug(f"Night light temperature set to {temperature}K")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to set night light temperature: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def create_night_light_widget(control_center):
|
||||
"""Create night light widget for control center"""
|
||||
|
||||
# Initialize night light control
|
||||
night_light = NightLightControl()
|
||||
is_active = is_night_light_active()
|
||||
|
||||
# Create icon
|
||||
night_light_icon = svg_file(
|
||||
(
|
||||
"applets/redshift-status-on.svg"
|
||||
if night_light.is_active
|
||||
if is_active
|
||||
else "applets/redshift-status-off.svg"
|
||||
),
|
||||
size=42,
|
||||
)
|
||||
|
||||
# Create status label
|
||||
night_light_status_label = Label(
|
||||
label="On" if night_light.is_active else "Off",
|
||||
label="On" if is_active else "Off",
|
||||
name="nightlight-widget-label",
|
||||
style_classes="status-label",
|
||||
max_chars_width=15,
|
||||
@@ -88,19 +32,16 @@ def create_night_light_widget(control_center):
|
||||
h_align="start",
|
||||
)
|
||||
|
||||
def toggle_night_light(*_):
|
||||
"""Toggle night light and update UI"""
|
||||
if night_light.toggle():
|
||||
# Update icon
|
||||
def toggle_ui(*_):
|
||||
if toggle_night_light():
|
||||
is_active = is_night_light_active()
|
||||
night_light_icon.dynamic_file(
|
||||
"applets/redshift-status-on.svg"
|
||||
if night_light.is_active
|
||||
if is_active
|
||||
else "applets/redshift-status-off.svg"
|
||||
)
|
||||
# Update status label
|
||||
night_light_status_label.set_label("On" if night_light.is_active else "Off")
|
||||
night_light_status_label.set_label("On" if is_active else "Off")
|
||||
|
||||
# Create widget box (similar to bluetooth_widget structure)
|
||||
night_light_widget = Box(
|
||||
name="nightlight-widget",
|
||||
orientation="h",
|
||||
@@ -109,7 +50,7 @@ def create_night_light_widget(control_center):
|
||||
Button(
|
||||
name="nightlight-icon-button",
|
||||
child=night_light_icon,
|
||||
on_clicked=toggle_night_light,
|
||||
on_clicked=toggle_ui,
|
||||
),
|
||||
Button(
|
||||
name="nightlight-info-button",
|
||||
@@ -130,7 +71,7 @@ def create_night_light_widget(control_center):
|
||||
night_light_status_label,
|
||||
],
|
||||
),
|
||||
on_clicked=toggle_night_light,
|
||||
on_clicked=toggle_ui,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -74,6 +74,9 @@ class PerAppVolumeControl(Box):
|
||||
audio_service.connect("stream-removed", self._on_stream_changed)
|
||||
)
|
||||
|
||||
# Track last known app names to avoid unnecessary rebuilds
|
||||
self._last_app_names = frozenset()
|
||||
|
||||
# Initial population
|
||||
self._populate_apps()
|
||||
|
||||
@@ -84,9 +87,15 @@ class PerAppVolumeControl(Box):
|
||||
"""Auto-refresh the application list every 2 seconds"""
|
||||
if self._destroyed:
|
||||
self._refresh_timer = None
|
||||
return False # Remove GLib source
|
||||
self._populate_apps()
|
||||
return True # Continue the timer
|
||||
return False
|
||||
if not audio_service:
|
||||
return True
|
||||
apps = audio_service.applications or []
|
||||
current_names = frozenset(a.name or "" for a in apps)
|
||||
if current_names != self._last_app_names:
|
||||
self._last_app_names = current_names
|
||||
self._populate_apps()
|
||||
return True
|
||||
|
||||
def _on_map(self, *_):
|
||||
if self._refresh_timer is None:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import subprocess
|
||||
|
||||
from fabric.utils import Gdk, GLib, logger
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
@@ -12,7 +10,11 @@ from services.network import NetworkClient
|
||||
from shared.dialogs.wifi_password_dialog import WiFiPasswordDialog
|
||||
from shared.widgets.smooth_switch import SmoothSwitch
|
||||
from shared.window.animated_scrollwindow import AnimatedScrollable
|
||||
from utils.functions import get_wifi_connecting_icon, get_wifi_icon_for_strength
|
||||
from utils.functions import (
|
||||
get_wifi_connecting_icon,
|
||||
get_wifi_icon_for_strength,
|
||||
spawn_detached,
|
||||
)
|
||||
from utils.utils import svg_file
|
||||
|
||||
|
||||
@@ -107,7 +109,7 @@ class WifiNetworkSlot(Box):
|
||||
self.dimage.add_style_class("disconnecting")
|
||||
|
||||
if self.wifi_service and self.wifi_service._device:
|
||||
subprocess.Popen(
|
||||
spawn_detached(
|
||||
[
|
||||
"nmcli",
|
||||
"device",
|
||||
@@ -472,7 +474,7 @@ class WifiConnections(Box):
|
||||
def open_network_settings(self, *_):
|
||||
"""Open NetworkManager connection editor"""
|
||||
try:
|
||||
subprocess.Popen(["nm-connection-editor"], start_new_session=True)
|
||||
spawn_detached(["nm-connection-editor"])
|
||||
if self.parent and hasattr(self.parent, "hide_controlcenter"):
|
||||
self.parent.hide_controlcenter()
|
||||
except FileNotFoundError:
|
||||
|
||||
Reference in new issue
Block a user