Files
sims/sims/main.py
2026-05-06 23:12:57 +02:00

245 lines
7.2 KiB
Python

from loguru import logger
# Configure logging based on dev flag
from .config import DEV, LOG_LEVEL
if DEV:
# In dev mode, disable fabric logs but keep stylix and bar logs
logger.disable("fabric")
else:
# In production, disable fabric logs but keep bar logs with configurable level
import sys
logger.disable("fabric")
logger.configure(handlers=[{"sink": sys.stderr, "level": LOG_LEVEL, "format": "{time} | {level} | {name}:{function}:{line} - {message}"}])
from fabric import Application
from sims.services.i3 import I3, I3MessageType
from fabric.system_tray.widgets import SystemTray
from fabric.widgets.wayland import WaylandWindow as Window
from fabric.utils import (
get_relative_path,
)
from .modules.bar import StatusBar
from .modules.window_fuzzy import FuzzyWindowFinder
from .modules.launcher.apps import AppLauncher
from .modules.launcher.clipboard import ClipboardMenu
from .modules.launcher.notmuch_search import NotmuchSearchMenu
from .modules.launcher.power import PowerMenu
from .modules.launcher.screenrec import ScreenrecMenu
from .modules.launcher.screenshot import ScreenshotMenu
from .modules.calendar import CalendarService
from .modules.control_center import ControlCenter
from .modules.notifications import NotificationToasts
from .modules.stylix import get_stylix_css_path
from .modules.vinyl import VinylButton
from .config import CALENDAR, NOTIFICATIONS, POWER, SCREENREC, STYLIX, VINYL
from .services.fenster import get_i3_connection
from .services.notification_history import NotificationHistoryService
from .services.screenrec import ScreenrecService
from fabric.notifications import Notifications
tray = SystemTray(name="system-tray", spacing=4)
get_i3_connection()
dummy = Window(visible=False)
finder = FuzzyWindowFinder()
app_launcher = AppLauncher()
clipboard_menu = ClipboardMenu()
power_menu = PowerMenu(lock_command=POWER.get("lock_command", ["waylock"]))
screenshot_menu = ScreenshotMenu()
notmuch_search_menu = NotmuchSearchMenu()
screenrec_service: ScreenrecService | None = None
screenrec_menu = None
if SCREENREC.get("enable", False):
screenrec_service = ScreenrecService(
output_dir=SCREENREC.get("output_dir", "~/Videos/wl-screenrec")
)
screenrec_menu = ScreenrecMenu(screenrec_service)
notifications_service: Notifications | None = None
notification_history: NotificationHistoryService | None = None
notification_toasts: NotificationToasts | None = None
if NOTIFICATIONS.get("enable", False):
notifications_service = Notifications()
notification_history = NotificationHistoryService(
notifications_service,
history_size=NOTIFICATIONS.get("history_size", 50),
image_max_px=NOTIFICATIONS.get("image_max_px", 128),
)
notification_toasts = NotificationToasts(
notifications_service,
monitor=0,
anchor=NOTIFICATIONS.get("anchor", "top center"),
margin=NOTIFICATIONS.get("margin", "8px"),
width=NOTIFICATIONS.get("width", 360),
timeout_ms=NOTIFICATIONS.get("timeout_ms", 10_000),
)
vinyl_button: VinylButton | None = VinylButton() if VINYL.get("enable", False) else None
calendar_service: CalendarService | None = (
CalendarService(update_interval=120000) if CALENDAR.get("enable", True) else None
)
control_center: ControlCenter | None = None
if notification_history is not None:
control_center = ControlCenter(
history=notification_history,
calendar_service=calendar_service,
vinyl_button=vinyl_button,
monitor=0,
width=NOTIFICATIONS.get("center_width", 380),
)
bar_windows = []
notmuch_widget = None
_app_windows = [dummy, finder, app_launcher, clipboard_menu, power_menu, screenshot_menu, notmuch_search_menu]
if screenrec_menu is not None:
_app_windows.append(screenrec_menu)
if notification_toasts is not None:
_app_windows.append(notification_toasts)
if control_center is not None:
_app_windows.append(control_center)
app = Application("sims", *_app_windows)
@Application.action()
def open_finder():
finder.show()
@Application.action()
def open_app_launcher():
app_launcher.show()
@Application.action()
def open_clipboard_menu():
clipboard_menu.show()
@Application.action()
def open_power_menu():
power_menu.show()
@Application.action()
def open_screenshot_menu():
screenshot_menu.show()
@Application.action()
def refresh_notmuch():
if notmuch_widget is not None:
notmuch_widget.service.update_counts()
@Application.action()
def open_notmuch_search():
notmuch_search_menu.show()
@Application.action()
def open_screenrec_menu():
if screenrec_menu is not None:
screenrec_menu.show()
@Application.action()
def screenrec_start_monitor():
if screenrec_service is not None:
screenrec_service.start_monitor("videos")
@Application.action()
def screenrec_start_region():
if screenrec_service is not None:
screenrec_service.start_region("videos")
@Application.action()
def screenrec_stop():
if screenrec_service is not None:
screenrec_service.stop()
@Application.action()
def toggle_control_center():
if control_center is not None:
control_center.toggle()
def _set_all_bars_rounded(rounded: bool):
for bar in bar_windows:
bar.set_corners_rounded(rounded)
@Application.action()
def set_bar_corners_rounded():
_set_all_bars_rounded(True)
@Application.action()
def set_bar_corners_flat():
_set_all_bars_rounded(False)
@Application.action()
def toggle_bar_corners():
new_state = not any(bar.corners_rounded for bar in bar_windows)
_set_all_bars_rounded(new_state)
# Load CSS - use Stylix if enabled, otherwise use default
if STYLIX.get("enable", False):
stylix_css_path = get_stylix_css_path()
if stylix_css_path:
logger.info("[Bar] Using Stylix CSS")
app.set_stylesheet_from_file(get_relative_path("styles/main.css"))
app.set_stylesheet_from_file(stylix_css_path, append=True)
else:
logger.warning("[Bar] Stylix enabled but CSS generation failed, falling back to default")
app.set_stylesheet_from_file(get_relative_path("styles/main.css"))
else:
logger.info("[Bar] Using default CSS")
app.set_stylesheet_from_file(get_relative_path("styles/main.css"))
def spawn_bars():
global notmuch_widget
logger.info("[Bar] Spawning bars")
outputs_reply = I3.send_command("", I3MessageType.GET_OUTPUTS)
if not (outputs_reply.is_ok and isinstance(outputs_reply.reply, list)):
logger.warning("[Bar] Failed to get outputs — skipping bar spawn")
return
outputs = [o for o in outputs_reply.reply if o.get("active")]
if not outputs:
logger.warning("[Bar] No active outputs found — skipping bar spawn")
return
for i, output in enumerate(outputs):
output_name = output.get("name", f"Unknown-{i}")
bar = StatusBar(
display=output_name,
tray=tray if i == 0 else None,
monitor=i,
screenrec_service=screenrec_service if i == 0 else None,
control_center=control_center if i == 0 else None,
)
bar_windows.append(bar)
if i == 0 and bar.notmuch:
notmuch_widget = bar.notmuch
def main():
spawn_bars()
app.run()
if __name__ == "__main__":
main()