Пример #1
0
class FullscreenTrigger(Trigger):
    def __init__(self):
        if os.environ.get("WAYLAND_DISPLAY") is None:
            self._ewmh = EWMH()
        else:
            logger.info("Running on Wayland; fullscreen trigger won't work.")
            self._ewmh = None

    def run(self) -> DesiredState:
        """Determine if a fullscreen application is running."""
        inhibit = False

        if self._ewmh:
            window = self._ewmh.getActiveWindow()

            # ewmh.getWmState(window) returns None is scenarios where
            # ewmh.getWmState(window, str=True) throws an exception
            # (it's a bug in pyewmh):
            if window and self._ewmh.getWmState(window):
                wm_state = self._ewmh.getWmState(window, True)
                inhibit = "_NET_WM_STATE_FULLSCREEN" in wm_state

        if inhibit:
            logger.info("Fullscreen window detected.")
            return DesiredState.INHIBIT_ALL
        else:
            return DesiredState.UNINHIBITED
Пример #2
0
    def __attempt_autoactivation(self):
        """Attempts to auto-activate inhibition by verifying if any of the
        whitelisted processes is running OR if there's a fullscreen app.
        """

        if self.get_activated() and not self.__auto_activated:
            logging.debug("Inhibition manually activated. Won't attempt to " +
                          "auto-activate")
            return True

        process_running = False

        # Determine if one of the whitelisted processes is running.
        for proc in self.__process_manager.get_process_list():
            if utils.isProcessRunning(proc):
                process_running = True

                if self.__auto_activated:
                    logging.info(
                        "Process {} detected but was already ".format(proc) +
                        "auto-activated")
                elif not self.get_activated():
                    logging.info(
                        "Process {} detected. Inhibiting.".format(proc))

        # Determine if a fullscreen application is running
        ewmh = EWMH()
        window = ewmh.getActiveWindow()
        if window:
            # ewmh.getWmState(window) returns None is scenarios where
            # ewmh.getWmState(window, str=True) throws an exception
            # (it's a bug in pyewmh):
            fullscreen = ewmh.getWmState(window) and \
                "_NET_WM_STATE_FULLSCREEN" in ewmh.getWmState(window, str=True)
        else:
            fullscreen = False

        if fullscreen:
            if self.__auto_activated:
                logging.debug("Fullscreen app detected, but was already " +
                              "auto-activated")
            elif not self.get_activated():
                logging.info("Fullscreen app detected. Inhibiting.")

        # Disable auto-activation?
        if not process_running and not fullscreen and self.__auto_activated:
            logging.info("Was auto-inhibited, but there's no fullscreen or " +
                         "whitelisted process now. De-activating.")
            self.__auto_activated = False
            self.__set_activated(False)
        elif process_running or fullscreen and not self.__auto_activated:
            self.__auto_activated = True
            self.__set_activated(True)

        return True
Пример #3
0
class FullscreenTrigger:
    def __init__(self):
        self._ewmh = EWMH()

    def run(self) -> DesiredState:
        """Determine if a fullscreen application is running."""
        inhibit = False

        window = self._ewmh.getActiveWindow()

        # ewmh.getWmState(window) returns None is scenarios where
        # ewmh.getWmState(window, str=True) throws an exception
        # (it's a bug in pyewmh):
        if window and self._ewmh.getWmState(window):
            wm_state = self._ewmh.getWmState(window, True)
            inhibit = "_NET_WM_STATE_FULLSCREEN" in wm_state

        if inhibit:
            logger.info("Fullscreen app detected.")
            return DesiredState.INHIBIT_ALL

        return DesiredState.UNINHIBITED
Пример #4
0
class WindowManager(object):
    def __init__(self, prefs, session_info):
        self.prefs = prefs
        self.session_info = session_info
        self.ewmh = EWMH()
        self.dpy = display.Display()
        self.screen = self.dpy.screen()
        self.dpy_root = self.screen.root
        self.colormap = self.screen.default_colormap
        self.pixel_palette = PixelPalette(self.colormap)

        self.display_dimensions = self.get_display_geometry()
        self.window_resize_options = [
            "center", "maximize", "left", "right", "top", "bottom"
        ]

        self.managed_windows = []
        self.exposed_windows = []
        self.last_raised_window = None
        self.active_window_title = self.session_info.session_name
        self.window_order = -1

        self.key_alias = {}
        self.keys_down = set()

        self.start = None
        self.attr = None

        self.wm_window_type = self.dpy.intern_atom('_NET_WM_WINDOW_TYPE')
        self.wm_state = self.dpy.intern_atom('_NET_WM_STATE')
        self.wm_window_types = {
            "dock": self.dpy.intern_atom('_NET_WM_WINDOW_TYPE_DOCK'),
            "normal": self.dpy.intern_atom('_NET_WM_WINDOW_TYPE_NORMAL'),
            "dialog": self.dpy.intern_atom('_NET_WM_WINDOW_TYPE_DIALOG'),
            "utility": self.dpy.intern_atom('_NET_WM_WINDOW_TYPE_UTILITY'),
            "toolbar": self.dpy.intern_atom('_NET_WM_WINDOW_TYPE_TOOLBAR'),
            "menu": self.dpy.intern_atom('_NET_WM_WINDOW_TYPE_MENU'),
            "splash": self.dpy.intern_atom('_NET_WM_WINDOW_TYPE_SPLASH')
        }
        self.wm_window_status = {
            "active":
            self.dpy.intern_atom('_NET_ACTIVE_WINDOW'),
            "desktop":
            self.dpy.intern_atom('_NET_WM_DESKTOP'),
            "above":
            self.dpy.intern_atom('_NET_WM_STATE_ABOVE'),
            "skip_taskbar":
            self.dpy.intern_atom('_NET_WM_STATE_SKIP_TASKBAR'),
            "maximize_vertical":
            self.dpy.intern_atom('_NET_WM_STATE_MAXIMIZED_VERT'),
            "maximize_horizontal":
            self.dpy.intern_atom('_NET_WM_STATE_MAXIMIZED_HORIZ')
        }

        self.wm_window_cyclical = [
            self.wm_window_types["normal"], self.wm_window_types["dialog"],
            self.wm_window_types["utility"], self.wm_window_types["toolbar"]
        ]

        self.deskbar = None
        self.display_corners = None

        self.update_active_window_title_rt = RepeatedTimer(
            interval=1, function=self.update_active_window_title)
        self.update_active_window_title_rt.stop()

        self.set_cursor(self.dpy_root)
        XK.load_keysym_group('xf86')
        self.set_key_aliases()

    ### QUERY METHODS

    def get_display_geometry(self):
        return self.dpy_root.get_geometry()

    def window_list(self):
        return self.dpy_root.query_tree().children

    def is_managed_window(self, window):
        return window in self.managed_windows

    def is_alive_window(self, window):
        windows = self.dpy_root.query_tree().children
        return window in windows

    def is_dock(self, window):
        result = None
        try:
            result = window.get_full_property(self.wm_window_type, Xatom.ATOM)
        except error.BadWindow or RuntimeError:
            print("Failed to detect if window is dock")
            pass
        if result is not None and result.value[0] == self.wm_window_types[
                "dock"]:
            return True
        return False

    def is_popup_window(self, window):
        result = None
        try:
            result = window.get_full_property(self.wm_window_type, Xatom.ATOM)
        except error.BadWindow or RuntimeError:
            print("Failed to detect if window is dock")
            pass
        if result is not None and (
                result.value[0] == self.wm_window_types["menu"]
                or result.value[0] == self.wm_window_types["splash"]):
            return True
        return False

    def is_cyclical_window(self, window):
        result = None
        try:
            result = window.get_full_property(self.wm_window_type, Xatom.ATOM)
        except error.BadWindow or RuntimeError:
            print("Failed to detect if window is dock")
            pass
        if result is not None and result.value[0] in self.wm_window_cyclical:
            return True
        return False

    def is_active(self, atom):
        if atom == self.wm_window_status["active"]:
            return True
        return False

    def get_active_window(self):
        window = None
        try:
            window = self.dpy_root.get_full_property(
                self.wm_window_status["active"], Xatom.ATOM)
        except:
            print("Failed to get active window")
            pass
        return window

    def get_window_class(self, window):
        try:
            cmd, cls = window.get_wm_class()
        except:
            return ''
        if cls is not None:
            return cls
        else:
            return ''

    def get_window_geometry(self, window):
        try:
            return window.get_geometry()
        except:
            return None

    def get_maximum_available_geometry(self):
        window_width = self.display_dimensions.width
        window_height = self.display_dimensions.height
        if self.deskbar is not None:
            window_height -= self.deskbar.real_height
        return window_width, window_height, self.deskbar is not None

    def get_window_attributes(self, window):
        try:
            return window.get_attributes()
        except:
            return None

    def get_window_state(self, window):
        return self.ewmh.getWmState(window, str=True)

    def get_window_shortname(self, window):
        return '0x{:x} [{}]'.format(window.id, self.get_window_class(window))

    def get_window_title(self, window):
        result = None
        try:
            result = window.get_wm_name()
        except:
            pass
        if result is None:
            return self.session_info.session_name
        return result

    def set_active_window_title(self, window=None, custom_title=None):
        window_title = None
        if window is not None:
            window_title = self.get_window_title(window)
            if window_title is None:
                self.active_window_title = self.session_info.session_name
        elif custom_title is not None:
            window_title = custom_title
        else:
            self.active_window_title = window_title
        if self.prefs.deskbar["enabled"] == 1:
            self.deskbar.set_active_window_title(self.active_window_title)

    def update_active_window_title(self):
        if self.last_raised_window is not None:
            self.set_active_window_title(self.last_raised_window)

    def update_window_count(self):
        if self.deskbar is not None:
            self.deskbar.set_window_count(len(self.managed_windows))

    ### WINDOW CONTROLS

    def manage_window(self, window):
        attributes = self.get_window_attributes(window)
        if attributes is None:
            return
        if attributes.override_redirect:
            return
        if self.is_managed_window(window):
            return

        if self.prefs.dev["debug"] == 1:
            print("Found window: %s", self.get_window_shortname(window))
        self.managed_windows.append(window)
        self.exposed_windows.append(window)
        self.window_order = len(self.managed_windows) - 1
        self.update_window_count()

        window.map()
        mask = X.EnterWindowMask | X.LeaveWindowMask
        window.change_attributes(event_mask=mask)

        self.decorate_window(window)

    def unmanage_window(self, window):
        if self.is_managed_window(window):
            if self.prefs.dev["debug"] == 1:
                print("Unmanaging window: %s",
                      self.get_window_shortname(window))
            if window in self.managed_windows:
                self.managed_windows.remove(window)
                self.window_order = len(self.managed_windows) - 1
                self.update_window_count()
            if window in self.exposed_windows:
                self.exposed_windows.remove(window)

    def destroy_window(self, window):
        if self.is_dock(window) is False:
            if self.prefs.dev["debug"] == 1:
                print("Destroy window: %s", self.get_window_shortname(window))
            if self.is_managed_window(window):
                window.destroy()
                self.unmanage_window(window)

    def raise_window(self, window):
        if not self.is_dock(window):
            if not self.is_managed_window(window):
                return
            window.raise_window()
            self.last_raised_window = window
            self.set_active_window_title(window)
            if self.deskbar is not None:
                self.deskbar.update()

    def focus_window(self, window):
        if self.is_dock(window) or not self.is_managed_window(
                window) or not self.is_alive_window(window):
            return
        window.set_input_focus(X.RevertToParent, 0)
        self.set_focus_window_border(window)

    def cycle_windows(self):
        if len(self.managed_windows) > 0:
            self.window_order += 1
            if self.window_order > len(self.managed_windows) - 1:
                self.window_order = 0
            window = self.managed_windows[self.window_order]
            if self.is_cyclical_window(window) is False:
                if self.window_order >= len(self.managed_windows) - 1:
                    self.window_order = 0
                else:
                    self.window_order += 1
                window = self.managed_windows[self.window_order]
            self.focus_window(window)
            self.raise_window(window)
        else:
            self.window_order = -1

    ### WINDOW DECORATION

    def is_window_maximized(self, window):
        states = self.get_window_state(window)
        print(states)

    def move_window(self, xdiff, ydiff):
        window_dimensions = self.get_window_geometry(self.start.child)
        if self.deskbar is not None and ydiff < 0 and window_dimensions.y <= self.deskbar.real_height:
            y = self.deskbar.real_height
        else:
            y = self.attr.y + (self.start.detail == 1 and ydiff or 0)
        self.start.child.configure(
            x=self.attr.x + (self.start.detail == 1 and xdiff or 0),
            y=y,
            width=max(
                1, self.attr.width + (self.start.detail == 3 and xdiff or 0)),
            height=max(
                1, self.attr.height + (self.start.detail == 3 and ydiff or 0)))

    def resize_window(self, window, position):
        if self.is_dock(window) is False:
            if self.prefs.dev["debug"] == 1:
                print("Triggered window resize")
            if position in self.window_resize_options:
                window_x, window_y, window_width, window_height = None, None, None, None
                if position == "center":
                    window_dimensions = self.get_window_geometry(window)
                    window_width, window_height = window_dimensions.width, window_dimensions.height
                    window_x = (self.display_dimensions.width -
                                window_width) // 2
                    window_y = (self.display_dimensions.height -
                                window_height) // 2
                elif position == "maximize":
                    window_width, window_height, has_deskbar = self.get_maximum_available_geometry(
                    )
                    window_x = -self.prefs.appearance["window_border_width"]
                    window_y = -self.prefs.appearance[
                        "window_border_width"] if not has_deskbar else (
                            -self.prefs.appearance["window_border_width"] +
                            self.deskbar.real_height)
                elif position == "left" or position == "right":
                    window_width = self.display_dimensions.width // 2
                    window_height = self.display_dimensions.height + (
                        0 if self.deskbar is None else self.deskbar.height)
                    if position == "left":
                        window_x = -self.prefs.appearance["window_border_width"]
                    elif position == "right":
                        window_x = window_width - self.prefs.appearance[
                            "window_border_width"]
                    window_y = -self.prefs.appearance[
                        "window_border_width"] if self.deskbar is None else (
                            -self.prefs.appearance["window_border_width"] +
                            self.deskbar.height + self.deskbar.border_width)
                elif position == "top" or position == "bottom":
                    window_width = self.display_dimensions.width
                    window_height = (self.display_dimensions.height +
                                     (0 if self.deskbar is None else
                                      self.deskbar.height)) // 2
                    if position == "top":
                        window_y = -self.prefs.appearance["window_border_width"]
                    elif position == "bottom":
                        window_y = window_height + self.prefs.appearance[
                            "window_border_width"]
                    window_x = -self.prefs.appearance["window_border_width"]

                if position == "maximize":
                    self.ewmh.setWmState(window, 1,
                                         "_NET_WM_STATE_MAXIMIZED_VERT")
                    self.ewmh.setWmState(window, 1,
                                         "_NET_WM_STATE_MAXIMIZED_HORIZ")
                else:
                    self.ewmh.setWmState(window, 0,
                                         "_NET_WM_STATE_MAXIMIZED_VERT")
                    self.ewmh.setWmState(window, 0,
                                         "_NET_WM_STATE_MAXIMIZED_HORIZ")

                window.configure(x=window_x,
                                 y=window_y,
                                 width=window_width,
                                 height=window_height)
            else:
                print("Invalid window position: " + position)

    def decorate_window(self, window):
        self.set_cursor(window)
        if self.is_dock(window) is False:
            window_dimensions = self.get_window_geometry(window)
            window_width, window_height = window_dimensions.width, window_dimensions.height
            window_x = 5
            window_y = 25
            if self.prefs.placement["auto_window_placement"] == 1:
                # Move new window out of the way of the deskbar
                if self.prefs.placement["auto_window_fit"] == 1:
                    # Resize window to fit the screen
                    if window_dimensions.width + window_x >= self.display_dimensions.width:
                        window_width -= window_x * 2
                    if window_dimensions.height + window_y >= self.display_dimensions.height:
                        window_height -= window_y * 2
                if self.prefs.placement["center_window_placement"] == 1:
                    window_x = (self.display_dimensions.width -
                                window_width) // 2
                    window_y = (self.display_dimensions.height -
                                window_height) // 2
                window.configure(x=window_x,
                                 y=window_y,
                                 width=window_width,
                                 height=window_height)
            self.set_unfocus_window_border(window)

    def set_unfocus_window_border(self, window):
        if not self.is_dock(window):
            border_color = self.pixel_palette.get_named_pixel("lightgray")
            if self.prefs.appearance[
                    "inactive_window_border_color"] in self.pixel_palette.hex_map.keys(
                    ):
                border_color = self.pixel_palette.get_named_pixel(
                    self.prefs.appearance["inactive_window_border_color"])
            elif self.pixel_palette.is_color_hex(
                    self.prefs.appearance["inactive_window_border_color"]
            ) is True:
                border_color = self.pixel_palette.get_hex_pixel(
                    self.prefs.appearance["inactive_window_border_color"])
            window.configure(
                border_width=self.prefs.appearance["window_border_width"])
            window.change_attributes(None, border_pixel=border_color)

    def set_focus_window_border(self, window):
        if not self.is_dock(window):
            border_color = self.pixel_palette.get_named_pixel("sienna")
            if self.prefs.appearance[
                    "active_window_border_color"] in self.pixel_palette.hex_map.keys(
                    ):
                border_color = self.pixel_palette.get_named_pixel(
                    self.prefs.appearance["active_window_border_color"])
            elif self.pixel_palette.is_color_hex(
                    self.prefs.appearance["active_window_border_color"]
            ) is True:
                border_color = self.pixel_palette.get_hex_pixel(
                    self.prefs.appearance["active_window_border_color"])
            window.change_attributes(None, border_pixel=border_color)

    def set_cursor(self, window):
        font = self.dpy.open_font('cursor')
        cursor = font.create_glyph_cursor(font, Xcursorfont.left_ptr,
                                          Xcursorfont.left_ptr + 1,
                                          (65535, 65535, 65535), (0, 0, 0))
        window.change_attributes(cursor=cursor)

    def set_background_color(self):
        background_color = self.pixel_palette.hex_map["slategray"]
        if self.pixel_palette.is_color_hex(
                self.prefs.appearance["background_color"]) is True:
            background_color = self.prefs.appearance["background_color"]
        elif self.prefs.appearance[
                "background_color"] in self.pixel_palette.hex_map.keys():
            background_color = self.pixel_palette.hex_map[
                self.prefs.appearance["background_color"]]
        os.system('xsetroot -solid "' + background_color + '"')

    # DEBUG

    def print_event_type(self, ev):
        if ev.type in recognised_events.keys():
            print(recognised_events[ev.type] + " event")

    # SPECIAL

    def start_terminal(self):
        run_command('x-terminal-emulator')

    # EVENT HANDLING

    def keycode_to_string(self, detail):
        return XK.keysym_to_string(self.dpy.keycode_to_keysym(detail, 0))

    # From: https://stackoverflow.com/a/43880743
    def keycode_to_key(self, keycode, state):
        i = 0
        if state & X.ShiftMask:
            i += 1
        if state & X.Mod1Mask:
            i += 2
        return self.dpy.keycode_to_keysym(keycode, i)

    # From: https://stackoverflow.com/a/43880743
    def key_to_string(self, key):
        keys = []
        for name in dir(XK):
            if name.startswith("XK_") and getattr(XK, name) == key:
                keys.append(
                    name.lstrip("XK_").replace("_L", "").replace("_R", ""))
        if keys:
            return " or ".join(keys)
        return "[%d]" % key

    # From: https://stackoverflow.com/a/43880743
    def keycode_to_string_mod(self, keycode, state):
        return self.key_to_string(self.keycode_to_key(keycode, state))

    def set_key_aliases(self):
        keystrings = [
            "x", "q", "minus", "equal", "bracketleft", "bracketright",
            "backslash", "slash", "F1", "Tab", "Escape", "space", "Return",
            "BackSpace"
        ]
        for keystring in keystrings:
            self.key_alias[keystring] = self.dpy.keysym_to_keycode(
                XK.string_to_keysym(keystring))

    def launcher_bindings(self, ev):
        if ev.detail == self.key_alias["Escape"]:
            self.deskbar.toggle_launcher(state=False)
        elif ev.detail == self.key_alias["BackSpace"] and len(
                self.deskbar.command_string) > 0:
            self.deskbar.command_string = self.deskbar.command_string[:-1]
            self.deskbar.update()
        elif ev.detail == self.key_alias["Return"]:
            run_command(self.deskbar.command_string)
            self.deskbar.toggle_launcher(state=False)
        else:
            key_string = self.keycode_to_string(ev.detail)
            if key_string:
                self.deskbar.command_string += key_string
                self.deskbar.update()

    def action_bindings(self, ev):
        if ev.detail in self.key_alias.values():
            if ev.child != X.NONE:
                if ev.detail == self.key_alias["q"]:
                    self.destroy_window(ev.child)
                elif ev.detail == self.key_alias["minus"]:
                    self.resize_window(ev.child, "center")
                elif ev.detail == self.key_alias["equal"]:
                    self.resize_window(ev.child, "maximize")
                elif ev.detail == self.key_alias["bracketleft"]:
                    self.resize_window(ev.child, "left")
                elif ev.detail == self.key_alias["bracketright"]:
                    self.resize_window(ev.child, "right")
                elif ev.detail == self.key_alias["backslash"]:
                    self.resize_window(ev.child, "top")
                elif ev.detail == self.key_alias["slash"]:
                    self.resize_window(ev.child, "bottom")
                elif ev.detail == self.key_alias["F1"]:
                    self.focus_window(ev.window)
                    self.raise_window(ev.window)

            if ev.detail == self.key_alias["x"]:
                self.start_terminal()
            elif ev.detail == self.key_alias["Tab"]:
                self.cycle_windows()
            elif ev.detail == self.key_alias["space"]:
                if self.deskbar is not None:
                    self.deskbar.toggle_launcher(state=True)
                    self.deskbar.command_string = ''
                    self.deskbar.update()
                    self.launcher_bindings(ev)
                else:
                    self.start_terminal()
            elif ev.detail == self.key_alias["Escape"]:
                self.end_session()

    def keypress_handler(self, ev):
        key_string = self.keycode_to_string_mod(ev.detail, ev.state)
        if key_string:
            try:
                # ev.state == 24 for Alt
                # ev.state == 25 for Alt + Shift
                self.keys_down.add(key_string)
                print("Pressed: " + key_string + " - " + str(self.keys_down))
            except:
                print("Unable to add pressed key")

    def keyrelease_handler(self, ev):
        key_string = self.keycode_to_string_mod(ev.detail, ev.state)
        if key_string:
            print(ev.state)
            try:
                self.keys_down.remove(key_string)
                print("Released: " + key_string + " - " + str(self.keys_down))
            except:
                print("Unable to remove released key")

    def event_handler(self):
        while 1:
            ev = self.dpy.next_event()
            if self.prefs.dev["debug"] == 1:
                self.print_event_type(ev)

            if ev.type in [X.EnterNotify, X.LeaveNotify, X.MapNotify]:
                self.set_active_window_title(ev.window)

            if ev.type == X.KeyPress:
                self.keypress_handler(ev)
                if self.deskbar is not None and self.deskbar.launcher_is_running(
                ) is True:
                    self.launcher_bindings(ev)
                else:
                    self.action_bindings(ev)
            elif ev.type == X.KeyRelease:
                self.keyrelease_handler(ev)
            elif ev.type == X.MapNotify:
                if self.is_cyclical_window(ev.window):
                    try:
                        self.manage_window(ev.window)
                        self.focus_window(ev.window)
                        self.raise_window(ev.window)
                    except AttributeError:
                        print("Unable to handle new window")
                        pass
            elif ev.type == X.DestroyNotify:
                try:
                    self.destroy_window(ev.window)
                except AttributeError:
                    print("Unable to unhandle new window")
                    pass
            elif ev.type == X.EnterNotify:
                self.focus_window(ev.window)
                if self.prefs.placement["auto_window_raise"] == 1:
                    self.raise_window(ev.window)
            elif ev.type == X.LeaveNotify:
                self.set_unfocus_window_border(ev.window)
            elif ev.type == X.ButtonPress and ev.child != X.NONE:
                if not self.is_dock(ev.child):
                    self.raise_window(ev.child)
                    self.set_focus_window_border(ev.child)
                    self.attr = ev.child.get_geometry()
                    self.start = ev
                elif self.deskbar is not None and ev.child == self.deskbar.deskbar:
                    if ev.detail == 1:
                        self.cycle_windows()
                    elif ev.detail == 3:
                        self.deskbar.toggle_window_count()
                        self.deskbar.update()
            elif ev.type == X.MotionNotify and self.start:
                xdiff = ev.root_x - self.start.root_x
                ydiff = ev.root_y - self.start.root_y
                self.move_window(xdiff, ydiff)
            elif ev.type == X.ButtonRelease:
                self.start = None
                self.attr = None
                if ev.child != X.NONE and self.is_dock(ev.child) is False:
                    self.ewmh.setWmState(ev.window, 0,
                                         "_NET_WM_STATE_MAXIMIZED_VERT")
                    self.ewmh.setWmState(ev.window, 0,
                                         "_NET_WM_STATE_MAXIMIZED_HORIZ")

            if self.display_corners is not None:
                self.display_corners.update()
            self.dpy.flush()

    # SESSION HANDLER

    def end_session(self):
        self.update_active_window_title_rt.stop()
        if self.prefs.deskbar["enabled"] == 1:
            self.deskbar.stop_repeated_events()
        if self.prefs.xround["enabled"] == 1:
            self.display_corners.stop()
        self.dpy.close()
        sys.exit(0)

    def main(self):
        # Register keyboard and mouse events
        self.dpy_root.grab_key(X.AnyKey, X.AnyModifier, 1, X.GrabModeAsync,
                               X.GrabModeAsync)
        self.dpy_root.grab_button(
            1, X.Mod1Mask | X.Mod2Mask, 1,
            X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask,
            X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE)
        self.dpy_root.grab_button(
            3, X.Mod1Mask | X.Mod2Mask, 1,
            X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask,
            X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE)
        self.dpy_root.change_attributes(event_mask=X.SubstructureNotifyMask)

        self.set_background_color()

        children = self.window_list()
        for child in children:
            if child.get_attributes().map_state:
                self.manage_window(child)

        # Draw deskbar
        if self.prefs.deskbar["enabled"] == 1:
            self.deskbar = Deskbar(self.ewmh, self.dpy, self.dpy_root,
                                   self.screen, self.display_dimensions,
                                   self.wm_window_type, self.wm_window_types,
                                   self.wm_state, self.wm_window_status,
                                   self.prefs, self.session_info)
            self.deskbar.draw()

        # Draw display corners
        if self.prefs.xround["enabled"] == 1:
            self.display_corners = DisplayCorners(
                self.ewmh, self.dpy, self.dpy_root, self.screen,
                self.display_dimensions, self.wm_window_type,
                self.wm_window_types, self.wm_state, self.wm_window_status)
            self.display_corners.draw()

        try:
            self.event_handler()
        except KeyboardInterrupt or error.ConnectionClosedError:
            self.end_session()
            sys.exit(0)
Пример #5
0
class Caffeine(GObject.GObject):
    def __init__(self, process_manager):
        GObject.GObject.__init__(self)

        self.__inhibitors = [
            GnomeInhibitor(),
            XdgScreenSaverInhibitor(),
            XdgPowerManagmentInhibitor(),
            XssInhibitor(),
            DpmsInhibitor(),
            XorgInhibitor(),
            XautolockInhibitor(),
            xfceInhibitor()
        ]

        self.__process_manager = process_manager

        # Status string (XXX: Let's double check how well this is working).
        self.status_string = "Caffeine is starting up..."

        # Inhibition has been requested (though it may not yet be active).
        self.__inhibition_manually_requested = False

        # Inhibition has successfully been activated.
        self.__inhibition_successful = False

        self.__auto_activated = False
        self.timer = None
        self.notification = None

        self._ewmh = EWMH()

        # FIXME: add capability to xdg-screensaver to report timeout.
        GLib.timeout_add(10000, self.__attempt_autoactivation)

        logger.info(self.status_string)

    def __attempt_autoactivation(self):
        """
        Determines if we want to auto-activate inhibition by verifying if any
        of the whitelisted processes is running OR if there's a fullscreen app.
        """
        # tr.print_diff()

        if self.get_activated() and not self.__auto_activated:
            logger.debug("Inhibition manually activated. Won't attempt to " +
                         "auto-activate")
            return True

        process_running = False

        # Determine if one of the whitelisted processes is running.
        for proc in self.__process_manager.get_process_list():
            if utils.isProcessRunning(proc):
                process_running = True

                if self.__auto_activated:
                    logger.info("Process %s detected. No change.", proc)
                elif not self.get_activated():
                    logger.info("Process %s detected. Inhibiting.", proc)

        # If none where running, let's look for fullscreen:
        if not process_running:
            # Determine if a fullscreen application is running
            window = self._ewmh.getActiveWindow()
            # ewmh.getWmState(window) returns None is scenarios where
            # ewmh.getWmState(window, str=True) throws an exception
            # (it's a bug in pyewmh):
            if window and self._ewmh.getWmState(window):
                fullscreen = "_NET_WM_STATE_FULLSCREEN" in \
                    self._ewmh.getWmState(window, True)
            else:
                fullscreen = False

            if fullscreen:
                if self.__auto_activated:
                    logger.debug("Fullscreen app detected. No change.")
                elif not self.get_activated():
                    logger.info("Fullscreen app detected. Inhibiting.")

        if (process_running or fullscreen) and not self.__auto_activated:
            self.__auto_activated = True
            # TODO: Check __set_activated
            self.__set_activated(True)
        elif not (process_running or fullscreen) and self.__auto_activated:
            logger.info("Was auto-inhibited, but there's no fullscreen or " +
                        "whitelisted process now. De-activating.")
            self.__auto_activated = False
            # TODO: Check __set_activated
            self.__set_activated(False)

        return True

    def quit(self):
        """
        Cancels any timer thread running so the program can quit right away.
        """
        if self.timer:
            self.timer.cancel()

    def _notify(self, message, icon, title="Caffeine"):
        """Easy way to use pynotify."""

        # try:
        Notify.init("Caffeine")
        if self.notification:
            self.notification.update(title, message, icon)
        else:
            self.notification = Notify.Notification.new(title, message, icon)

        # XXX: Notify OSD doesn't seem to work when sleep is prevented
        # if self.screenSaverCookie is not None and \
        #    self.__inhibition_successful:
        #     self.ssProxy.UnInhibit(self.screenSaverCookie)

        self.notification.show()

        # if self.screenSaverCookie is not None and \
        #    self.__inhibition_successful:
        #     self.screenSaverCookie = \
        #         self.ssProxy.Inhibit("Caffeine",
        #                              "User has requested that Caffeine "+
        #                              "disable the screen saver")

        # except Exception as e:
        #     logger.error("Exception occurred:\n%s", e)
        # finally:
        #     return False

    def timed_activation(self, time, show_notification=True):
        """Calls toggle_activated after the number of seconds
        specified by time has passed.
        """
        message = (_("Timed activation set; ") +
                   _("Caffeine will prevent powersaving for the next ") +
                   str(time))

        logger.info("Timed activation set for " + str(time))

        if self.status_string == "":
            self.status_string = _("Activated for ") + str(time)
            self.emit("activation-toggled", self.get_activated(),
                      self.status_string)

        self.set_activated(True, show_notification)

        if show_notification:
            self._notify(message, full_cup_icon)

        # and deactivate after time has passed.
        # Stop already running timer
        if self.timer:
            logger.info("Previous timed activation cancelled due to a " +
                        "second timed activation request (was set for " +
                        str(self.timer.interval) + " or " + str(time) +
                        " seconds )")
            self.timer.cancel()

        self.timer = Timer(time, self._deactivate, args=[show_notification])
        self.timer.name = "Active"
        self.timer.start()

    def _deactivate(self, show_notification):
        self.timer.name = "Expired"
        self.toggle_activated(show_notification)

    def __set_activated(self, activate):
        """Enables inhibition, but does not mark is as manually enabled.
        """
        if self.get_activated() != activate:
            self.__toggle_activated(activate)

    def get_activated(self):
        """Returns True if inhibition was manually activated.
        """
        return self.__inhibition_manually_requested

    def set_activated(self, activate, show_notification=True):
        """Sets inhibition as manually activated.
        """
        if self.get_activated() != activate:
            self.toggle_activated(show_notification)

    def toggle_activated(self, show_notification=True):
        """ *Manually* toggles inhibition.  """

        self.__auto_activated = False
        self.__toggle_activated(note=show_notification)

    def __toggle_activated(self, note):
        """
        Toggle inhibition.
        """

        if self.__inhibition_manually_requested:
            # sleep prevention was on now turn it off

            self.__inhibition_manually_requested = False
            logger.info("Caffeine is now dormant; powersaving is re-enabled.")
            self.status_string = \
                _("Caffeine is dormant; powersaving is enabled")

            # If the user clicks on the full coffee-cup to disable
            # sleep prevention, it should also
            # cancel the timer for timed activation.

            if self.timer is not None and self.timer.name != "Expired":
                message = (_("Timed activation cancelled (was set for ") +
                           str(self.timer.interval) + ")")

                logger.info("Timed activation cancelled (was set for " +
                            str(self.timer.interval) + ")")

                if note:
                    self._notify(message, empty_cup_icon)

                self.timer.cancel()
                self.timer = None

            elif self.timer is not None and self.timer.name == "Expired":
                message = (str(self.timer.interval) +
                           _(" have elapsed; powersaving is re-enabled"))

                logger.info("Timed activation period (" +
                            str(self.timer.interval) + ") has elapsed")

                if note:
                    self._notify(message, empty_cup_icon)

                self.timer = None

        else:
            self.__inhibition_manually_requested = True

        self._performTogglingActions()

        self.status_string == "Caffeine is preventing powersaving."

        self.emit("activation-toggled", self.get_activated(),
                  self.status_string)
        self.status_string = ""

    def _performTogglingActions(self):
        """This method performs the actions that affect the screensaver and
        powersaving."""

        for inhibitor in self.__inhibitors:
            if inhibitor.applicable:
                logger.info("%s is applicable, running it.", inhibitor)
                Thread(target=inhibitor.toggle).start()

        self.__inhibition_successful = not self.__inhibition_successful
Пример #6
0
class Caffeine(GObject.GObject):
    def __init__(self, process_manager):
        GObject.GObject.__init__(self)

        self.__inhibitors = [
            GnomeInhibitor(),
            XdgPowerManagmentInhibitor(),
            XssInhibitor(),
            XorgInhibitor(),
            XautolockInhibitor(),
            XidlehookInhibitor(),
            XdgScreenSaverInhibitor(),
            DpmsInhibitor()
        ]

        self.__process_manager = process_manager

        # Status string (XXX: Let's double check how well this is working).
        self.status_string = "Caffeine is starting up..."

        # Inhibition has been requested (though it may not yet be active).
        self.__inhibition_manually_requested = False

        # Number of procs playing audio but nothing visual. This is a special
        # case where we want the screen to turn off while still preventing
        # the computer from suspending
        self.music_procs = 0

        # Inhibition has successfully been activated.
        self.__inhibition_successful = False

        self.__auto_activated = False
        self.timer = None
        self.notification = None

        self._ewmh = EWMH()

        # FIXME: add capability to xdg-screensaver to report timeout.
        GLib.timeout_add(10000, self.__attempt_autoactivation)

        logger.info(self.status_string)

    def __attempt_autoactivation(self):
        """
        Determines if we want to auto-activate inhibition by verifying if any
        of the whitelisted processes is running OR if there's a fullscreen app.
        """
        # tr.print_diff()

        if self.get_activated() and not self.__auto_activated:
            logger.debug("Inhibition manually activated. Won't attempt to " +
                         "auto-activate")
            return True

        process_running = False

        # Determine if one of the whitelisted processes is running.
        for proc in self.__process_manager.get_process_list():
            if utils.isProcessRunning(proc):
                process_running = True

                if self.__auto_activated:
                    logger.info("Process %s detected. No change.", proc)
                elif not self.get_activated():
                    logger.info("Process %s detected. Inhibiting.", proc)

        # If none where running, let's look for fullscreen:
        if not process_running:
            # Determine if a fullscreen application is running
            window = self._ewmh.getActiveWindow()
            # ewmh.getWmState(window) returns None is scenarios where
            # ewmh.getWmState(window, str=True) throws an exception
            # (it's a bug in pyewmh):
            if window and self._ewmh.getWmState(window):
                fullscreen = "_NET_WM_STATE_FULLSCREEN" in \
                    self._ewmh.getWmState(window, True)
            else:
                fullscreen = False

            if fullscreen:
                if self.__auto_activated:
                    logger.debug("Fullscreen app detected. No change.")
                elif not self.get_activated():
                    logger.info("Fullscreen app detected. Inhibiting.")

        # Let's look for playing audio:
        # Number of supposed audio only streams.  We can turn the screen off
        # for those:
        self.music_procs = 0
        # Number of all audio streams including videos. We keep the screen on
        # here:
        screen_relevant_procs = 0

        if not process_running and not fullscreen:
            # Get all audio playback streams
            # Music players seem to use the music role. We can turn the screen
            # off there. Keep the screen on for audio without music role,
            # as they might be videos
            with Pulse() as pulseaudio:
                for sink in pulseaudio.sink_input_list():
                    sink_state = pulseaudio.sink_info(sink.sink).state
                    if sink_state is PulseStateEnum.running and \
                       sink.proplist.get('media.role') == "music":
                        # seems to be audio only
                        self.music_procs += 1
                    elif sink_state is PulseStateEnum.running:
                        # Video or other audio source
                        screen_relevant_procs += 1

                # Get all audio recording streams
                for source in pulseaudio.source_output_list():
                    source_state = pulseaudio.source_info(source.source).state

                    if source_state is PulseStateEnum.running:
                        # Treat recordings as video because likely you don't
                        # want to turn the screen of while recording
                        screen_relevant_procs += 1

            if self.music_procs > 0 or screen_relevant_procs > 0:
                if self.__auto_activated:
                    logger.debug("Audio playback detected. No change.")
                elif not self.get_activated():
                    logger.info("Audio playback detected. Inhibiting.")

        if (process_running or fullscreen or self.music_procs > 0
                or screen_relevant_procs > 0) and not self.__auto_activated:
            self.__auto_activated = True
            # TODO: Check __set_activated
            self.__set_activated(True)
        elif not (process_running or fullscreen or self.music_procs > 0
                  or screen_relevant_procs > 0) and self.__auto_activated:
            logger.info(
                "Was auto-inhibited, but there's no fullscreen, whitelisted "
                "process or audio playback now. De-activating.")
            # TODO: Check __set_activated
            self.__set_activated(False)
            self.__auto_activated = False

        return True

    def quit(self):
        """
        Cancels any timer thread running so the program can quit right away.
        """
        if self.timer:
            self.timer.cancel()

    def _notify(self, message, icon, title="Caffeine"):
        """Easy way to use pynotify."""

        # try:
        Notify.init("Caffeine")
        if self.notification:
            self.notification.update(title, message, icon)
        else:
            self.notification = Notify.Notification.new(title, message, icon)

        # XXX: Notify OSD doesn't seem to work when sleep is prevented
        # if self.screenSaverCookie is not None and \
        #    self.__inhibition_successful:
        #     self.ssProxy.UnInhibit(self.screenSaverCookie)

        self.notification.show()

        # if self.screenSaverCookie is not None and \
        #    self.__inhibition_successful:
        #     self.screenSaverCookie = \
        #         self.ssProxy.Inhibit("Caffeine",
        #                              "User has requested that Caffeine "+
        #                              "disable the screen saver")

        # except Exception as e:
        #     logger.error("Exception occurred:\n%s", e)
        # finally:
        #     return False

    def timed_activation(self, time, show_notification=True):
        """Calls toggle_activated after the number of seconds
        specified by time has passed.
        """
        message = (_("Timed activation set; ") +
                   _("Caffeine will prevent powersaving for the next ") +
                   str(time))

        logger.info("Timed activation set for " + str(time))

        if self.status_string == "":
            self.status_string = _("Activated for ") + str(time)
            self.emit("activation-toggled", self.get_activated(),
                      self.status_string)

        self.set_activated(True, show_notification)

        if show_notification:
            self._notify(message, full_cup_icon)

        # and deactivate after time has passed.
        # Stop already running timer
        if self.timer:
            logger.info("Previous timed activation cancelled due to a " +
                        "second timed activation request (was set for " +
                        str(self.timer.interval) + " or " + str(time) +
                        " seconds )")
            self.timer.cancel()

        self.timer = Timer(time, self._deactivate, args=[show_notification])
        self.timer.name = "Active"
        self.timer.start()

    def _deactivate(self, show_notification):
        self.timer.name = "Expired"
        self.toggle_activated(show_notification)

    def __set_activated(self, activate):
        """Enables inhibition, but does not mark is as manually enabled.
        """
        if self.get_activated() != activate:
            self.__toggle_activated(activate)

    def get_activated(self):
        """Returns True if inhibition was manually activated.
        """
        return self.__inhibition_manually_requested

    def set_activated(self, activate, show_notification=True):
        """Sets inhibition as manually activated.
        """
        if self.get_activated() != activate:
            self.toggle_activated(show_notification)

    def toggle_activated(self, show_notification=True):
        """ *Manually* toggles inhibition.  """

        self.__auto_activated = False
        self.__toggle_activated(note=show_notification)

    def __toggle_activated(self, note):
        """
        Toggle inhibition.
        """

        if self.__inhibition_manually_requested:
            # sleep prevention was on now turn it off

            self.__inhibition_manually_requested = False
            logger.info("Caffeine is now dormant; powersaving is re-enabled.")
            self.status_string = \
                _("Caffeine is dormant; powersaving is enabled")

            # If the user clicks on the full coffee-cup to disable
            # sleep prevention, it should also
            # cancel the timer for timed activation.

            if self.timer is not None and self.timer.name != "Expired":
                message = (_("Timed activation cancelled (was set for ") +
                           str(self.timer.interval) + ")")

                logger.info("Timed activation cancelled (was set for " +
                            str(self.timer.interval) + ")")

                if note:
                    self._notify(message, empty_cup_icon)

                self.timer.cancel()
                self.timer = None

            elif self.timer is not None and self.timer.name == "Expired":
                message = (str(self.timer.interval) +
                           _(" have elapsed; powersaving is re-enabled"))

                logger.info("Timed activation period (" +
                            str(self.timer.interval) + ") has elapsed")

                if note:
                    self._notify(message, empty_cup_icon)

                self.timer = None

        else:
            self.__inhibition_manually_requested = True

        # decide, if we allow the screen to sleep
        if (self.music_procs > 0 or not self.__inhibition_manually_requested):
            inhibit_screen = False
        else:
            inhibit_screen = True

        self._performTogglingActions(self.__inhibition_manually_requested,
                                     inhibit_screen)
        logger.info("\n\n")
        self.status_string = "Caffeine is preventing powersaving."

        self.emit("activation-toggled", self.get_activated(),
                  self.status_string)
        self.status_string = ""

    def _performTogglingActions(self, suspend, susp_screen):
        """This method performs the actions that affect the screensaver and
        powersaving."""

        for inhibitor in self.__inhibitors:
            if inhibitor.applicable:
                inhibitor.set(susp_screen) if inhibitor.is_screen_inhibitor() \
                                              else inhibitor.set(suspend)
                logger.info("%s is applicable, state: %s" %
                            (inhibitor, inhibitor.running))
        self.__inhibition_successful = not self.__inhibition_successful
Пример #7
0
class Caffeine(GObject.GObject):

    def __init__(self, process_manager):
        GObject.GObject.__init__(self)

        self.__inhibitors = [
            GnomeInhibitor(),
            XdgScreenSaverInhibitor(),
            XdgPowerManagmentInhibitor(),
            XssInhibitor(),
            DpmsInhibitor(),
            XorgInhibitor(),
            XautolockInhibitor()
        ]

        self.__process_manager = process_manager

        # Status string (XXX: Let's double check how well this is working).
        self.status_string = "Caffeine is starting up..."

        # Inhibition has been requested (though it may not yet be active).
        self.__inhibition_manually_requested = False

        # Inhibition has successfully been activated.
        self.__inhibition_successful = False

        self.__auto_activated = False
        self.timer = None
        self.notification = None

        self._ewmh = EWMH()

        # FIXME: add capability to xdg-screensaver to report timeout.
        GLib.timeout_add(10000, self.__attempt_autoactivation)

        logger.info(self.status_string)

    def __attempt_autoactivation(self):
        """
        Determines if we want to auto-activate inhibition by verifying if any
        of the whitelisted processes is running OR if there's a fullscreen app.
        """
        # tr.print_diff()

        if self.get_activated() and not self.__auto_activated:
            logger.debug("Inhibition manually activated. Won't attempt to " +
                         "auto-activate")
            return True

        process_running = False

        # Determine if one of the whitelisted processes is running.
        for proc in self.__process_manager.get_process_list():
            if utils.isProcessRunning(proc):
                process_running = True

                if self.__auto_activated:
                    logger.info("Process %s detected. No change.", proc)
                elif not self.get_activated():
                    logger.info("Process %s detected. Inhibiting.", proc)

        # If none where running, let's look for fullscreen:
        if not process_running:
            # Determine if a fullscreen application is running
            window = self._ewmh.getActiveWindow()
            # ewmh.getWmState(window) returns None is scenarios where
            # ewmh.getWmState(window, str=True) throws an exception
            # (it's a bug in pyewmh):
            if window and self._ewmh.getWmState(window):
                fullscreen = "_NET_WM_STATE_FULLSCREEN" in \
                    self._ewmh.getWmState(window, True)
            else:
                fullscreen = False

            if fullscreen:
                if self.__auto_activated:
                    logger.debug("Fullscreen app detected. No change.")
                elif not self.get_activated():
                    logger.info("Fullscreen app detected. Inhibiting.")

        if (process_running or fullscreen) and not self.__auto_activated:
            self.__auto_activated = True
            # TODO: Check __set_activated
            self.__set_activated(True)
        elif not (process_running or fullscreen) and self.__auto_activated:
            logger.info("Was auto-inhibited, but there's no fullscreen or " +
                        "whitelisted process now. De-activating.")
            self.__auto_activated = False
            # TODO: Check __set_activated
            self.__set_activated(False)

        return True

    def quit(self):
        """
        Cancels any timer thread running so the program can quit right away.
        """
        if self.timer:
            self.timer.cancel()

    def _notify(self, message, icon, title="Caffeine"):
        """Easy way to use pynotify."""

        # try:
        Notify.init("Caffeine")
        if self.notification:
            self.notification.update(title, message, icon)
        else:
            self.notification = Notify.Notification.new(title, message, icon)

        # XXX: Notify OSD doesn't seem to work when sleep is prevented
        # if self.screenSaverCookie is not None and \
        #    self.__inhibition_successful:
        #     self.ssProxy.UnInhibit(self.screenSaverCookie)

        self.notification.show()

        # if self.screenSaverCookie is not None and \
        #    self.__inhibition_successful:
        #     self.screenSaverCookie = \
        #         self.ssProxy.Inhibit("Caffeine",
        #                              "User has requested that Caffeine "+
        #                              "disable the screen saver")

        # except Exception as e:
        #     logger.error("Exception occurred:\n%s", e)
        # finally:
        #     return False

    def timed_activation(self, time, show_notification=True):
        """Calls toggle_activated after the number of seconds
        specified by time has passed.
        """
        message = (_("Timed activation set; ") +
                   _("Caffeine will prevent powersaving for the next ") +
                   str(time))

        logger.info("Timed activation set for " + str(time))

        if self.status_string == "":
            self.status_string = _("Activated for ") + str(time)
            self.emit("activation-toggled", self.get_activated(),
                      self.status_string)

        self.set_activated(True, show_notification)

        if show_notification:
            self._notify(message, full_cup_icon)

        # and deactivate after time has passed.
        # Stop already running timer
        if self.timer:
            logger.info("Previous timed activation cancelled due to a " +
                        "second timed activation request (was set for " +
                        str(self.timer.interval) + " or " +
                        str(time)+" seconds )")
            self.timer.cancel()

        self.timer = Timer(time, self._deactivate, args=[show_notification])
        self.timer.name = "Active"
        self.timer.start()

    def _deactivate(self, show_notification):
        self.timer.name = "Expired"
        self.toggle_activated(show_notification)

    def __set_activated(self, activate):
        """Enables inhibition, but does not mark is as manually enabled.
        """
        if self.get_activated() != activate:
            self.__toggle_activated(activate)

    def get_activated(self):
        """Returns True if inhibition was manually activated.
        """
        return self.__inhibition_manually_requested

    def set_activated(self, activate, show_notification=True):
        """Sets inhibition as manually activated.
        """
        if self.get_activated() != activate:
            self.toggle_activated(show_notification)

    def toggle_activated(self, show_notification=True):
        """ *Manually* toggles inhibition.  """

        self.__auto_activated = False
        self.__toggle_activated(note=show_notification)

    def __toggle_activated(self, note):
        """
        Toggle inhibition.
        """

        if self.__inhibition_manually_requested:
            # sleep prevention was on now turn it off

            self.__inhibition_manually_requested = False
            logger.info("Caffeine is now dormant; powersaving is re-enabled.")
            self.status_string = \
                _("Caffeine is dormant; powersaving is enabled")

            # If the user clicks on the full coffee-cup to disable
            # sleep prevention, it should also
            # cancel the timer for timed activation.

            if self.timer is not None and self.timer.name != "Expired":
                message = (_("Timed activation cancelled (was set for ") +
                           str(self.timer.interval) + ")")

                logger.info("Timed activation cancelled (was set for " +
                            str(self.timer.interval) + ")")

                if note:
                    self._notify(message, empty_cup_icon)

                self.timer.cancel()
                self.timer = None

            elif self.timer is not None and self.timer.name == "Expired":
                message = (str(self.timer.interval) +
                           _(" have elapsed; powersaving is re-enabled"))

                logger.info("Timed activation period (" +
                            str(self.timer.interval) +
                            ") has elapsed")

                if note:
                    self._notify(message, empty_cup_icon)

                self.timer = None

        else:
            self.__inhibition_manually_requested = True

        self._performTogglingActions()

        self.status_string == "Caffeine is preventing powersaving."

        self.emit("activation-toggled", self.get_activated(),
                  self.status_string)
        self.status_string = ""

    def _performTogglingActions(self):
        """This method performs the actions that affect the screensaver and
        powersaving."""

        for inhibitor in self.__inhibitors:
            if inhibitor.applicable:
                logger.info("Inhibitor %s is applicable, running it.",
                            inhibitor.__class__)
                Thread(target=inhibitor.toggle).start()

        self.__inhibition_successful = not self.__inhibition_successful