class _OSDManager(object): """ Manages OSD notifications via DBus. """ def __init__(self): if not ENABLED: self._actions = {} return self._last_id = 0 self.bus = SessionBus() self.notifications = None self.bus.watch_name(BASE_NAME + NOTIFICATION_BUS_NAME, name_appeared=self._register_bus_name, name_vanished=self._deregister_bus_name) self._register_bus_name(None) self._actions = {"default": lambda *args: None} def add_to_action(self, action, action_name, function): """ Register an action to the notification deamon Note: you can override the default action by passing "default" the action argument. Note: by passing the same action argument you override it. Args: action (`str`): The action that you want to invoke. action_name (`str`): The human readable string that describes the action function (`func`): The function that you want to envoke when it is called """ self._actions[action] = (action_name, function) def notify(self, title, body, actions, icon, replace=True): """ Create new or update existing notification. Args: track (`clay.gp.Track`): The track that you want to send the notification for actions (`list`): A list with the actions that you want the notification to react to. """ if not ENABLED: return actions_ = [] for action in actions: if action not in self._actions: logger.error("Can't find action: {}".format(action)) continue actions_.append(action) actions_.append(self._actions[action][0]) self._notify( title, body, replace, actions=actions_, hints={"action-icons": Variant('b', 1)}, # only display icons icon=icon if icon is not None else 'audio-headphones') def _on_action(self, id_, action): if id_ != self._last_id: return if action in self._actions: self._actions[action][1]() else: self._actions.get("default")[1]() def _notify(self, summary, body, replace=True, actions=None, hints=None, expiration=5000, icon='audio-headphones'): """ An implementation of Desktop Notifications Specification 1.2. For a detailed explanation see: https://developer.gnome.org/notification-spec/ Does not fire if notifications were not properly set up Args: summary (`str`): A single line overview of the notification body (`str`): A mutli-line body of the text replace (`bool`): Should the notification be updated or should a new one be made actions (`list`): The actions a notification can perform, might be ignored. Default empty hints (`dict`): Extra information the server might be able to make use of expiration (`int`): The time until the notification automatically closes. -1 to make the server decide and 0 for never. Defaults to 5000. icon (`str`): The string to icon it displays in the notification. Defaults to headbuds. Returns: Nothing. """ if self.notifications is None: return try: self._last_id = self.notifications.Notify( meta.APP_NAME, self._last_id if replace else 0, icon, summary, body, actions if actions is not None else list(), hints if hints is not None else dict(), expiration) except GLib.Error as exception: logger.error('Failed to post notification %s', exception) def _register_bus_name(self, name_owner): """ Registers a bus for sending notifications over dbus Args: name_owner (`str`) (unused): The owner of the bus Returns: Nothing. """ try: self.notifications = self.bus.get(NOTIFICATION_BUS_NAME) self.notifications.onActionInvoked = self._on_action except GLib.Error: # Bus name did not exist logger.error('Attempted bus name registration failed, %s', NOTIFICATION_BUS_NAME) def _deregister_bus_name(self): """ Deregisters a bus for sending notifications over dbus. Once run, notifications cannot be sent Returns: Nothing. """ self.notifications = None
class PowerMonitor(metaclass=Singleton): """ Watches for changes to the system's suspend state and screensaver, signalling devices to suspend if necessary. """ def __init__(self): self._logger = Log.get('uchroma.power') self._name_watchers = [] self._running = False self._sleeping = False self._user_active = False self._session_bus = SessionBus() self._system_bus = SystemBus() self._dm = UChromaDeviceManager() #singleton def _suspend(self, sleeping, fast): if self._sleeping == sleeping: return self._sleeping = sleeping for name, device in self._dm.devices.items(): if sleeping: self._logger.info("Suspending device: %s", name) device.suspend(fast=fast) else: self._logger.info("Resuming device: %s", name) device.resume() def _prepare_for_sleep(self, sleeping): self._suspend(sleeping, True) def _active_changed(self, active): self._suspend(active, False) def _is_user_active(self): user = self._system_bus.get(LOGIN_SERVICE, "/org/freedesktop/login1/user/self") self._logger.info("is_user_active: %s %s", user, user.Display) return user is not None and user.Display[0] != '' def _user_changed(self, changed): is_active = self._is_user_active() if is_active != self._user_active: self._user_active = is_active self._suspend(not self._user_active, True) def start(self): """ Connects to the PrepareForSleep signal from login1 to monitor system suspend, and sets up watches for known screensaver instances. """ if self._running: return for name, path in SCREENSAVERS: def connect_screensaver(*args, bus_name=name, object_path=path): """ Connects the callback when the service appears. """ try: saver = self._session_bus.get(bus_name=bus_name, object_path=object_path) saver.ActiveChanged.connect(self._active_changed) self._logger.info("Connected screensaver: %s:%s %s", bus_name, object_path, args) except Exception: self._logger.warn("Could not connect to %s:%s service", bus_name, object_path) self._name_watchers.append(self._session_bus.watch_name( \ name, name_appeared=connect_screensaver)) def connect_login1(*args): try: login1 = self._system_bus.get(LOGIN_SERVICE) login1.PrepareForSleep.connect(self._prepare_for_sleep) self._logger.info("Connected to %s %s", LOGIN_SERVICE, args) user = self._system_bus.get(LOGIN_SERVICE, "/org/freedesktop/login1/user/self") user.PropertiesChanged.connect(self._user_changed) except Exception: self._logger.warn("Could not connect to login1 service") self._name_watchers.append(self._system_bus.watch_name( \ LOGIN_SERVICE, name_appeared=connect_login1)) self._running = True def stop(self): """ Disable the monitor """ if not self._running: return for watcher in self._name_watchers: watcher.unwatch() self._name_watchers.clear() self._running = False
def on_pidgin_appeared(con=None, name=None, name_owner=None): global purple purple = bus.get("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject") purple.ReceivedImMsg.connect(on_received) purple.SentImMsg.connect(on_sent) def on_pidgin_vanished(con=None, name=None): global purple purple = None dir_filepath = getenv("HOME") + "/.moov_dir" if not isfile(dir_filepath): print("Error: ~/.moov_dir file not found.\nAborting.", file=stderr) exit(1) dir_file = open(dir_filepath, "r") video_dir = dir_file.readline().strip() if not isdir(video_dir): print( "Error: directory path specified in ~/.moov_dir is invalid.\nAborting.", file=stderr) exit(2) bus = SessionBus() bus.watch_name("im.pidgin.purple.PurpleService", name_appeared=on_pidgin_appeared, name_vanished=on_pidgin_vanished) GObject.MainLoop().run()