From 94b30e631a05c5182a833cd353f039939e609ee8 Mon Sep 17 00:00:00 2001 From: JezerM Date: Sun, 6 Feb 2022 15:08:37 -0600 Subject: [PATCH] Migrated from python-xlib to python C binding This fixes screensaver related uses at lockscreen --- requirements.txt | 1 - src/bindings/__init__.py | 1 + src/bindings/screensaver.c | 64 +++++++++++++++++++++++++++++++ src/bindings/screensaver.py | 76 +++++++++++++++++++++++++++++++++++++ src/bridge/Greeter.py | 2 +- src/browser/browser.py | 2 +- src/utils/screensaver.py | 60 ----------------------------- 7 files changed, 143 insertions(+), 63 deletions(-) create mode 100644 src/bindings/__init__.py create mode 100644 src/bindings/screensaver.c create mode 100644 src/bindings/screensaver.py delete mode 100644 src/utils/screensaver.py diff --git a/requirements.txt b/requirements.txt index 45d172f..95c4f99 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,4 @@ PyGObject PyQt5 PyQtWebEngine ruamel.yaml -python-xlib pyinotify diff --git a/src/bindings/__init__.py b/src/bindings/__init__.py new file mode 100644 index 0000000..49313bb --- /dev/null +++ b/src/bindings/__init__.py @@ -0,0 +1 @@ +from .screensaver import screensaver diff --git a/src/bindings/screensaver.c b/src/bindings/screensaver.c new file mode 100644 index 0000000..f055158 --- /dev/null +++ b/src/bindings/screensaver.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +Display* OpenDisplay() { + char *disp = NULL; + Display *display = XOpenDisplay(disp); + return display; +} + +typedef struct { + int timeout; + int interval; + int prefer_blank; + int allow_exp; +} ScreenSaver; + +ScreenSaver *get_screensaver() { + int timeout, interval, prefer_blank, allow_exp; + + Display *display = OpenDisplay(); + if (display == NULL) return NULL; + + XGetScreenSaver(display, &timeout, &interval, &prefer_blank, &allow_exp); + XCloseDisplay(display); + + ScreenSaver *data = malloc(sizeof *data); + data->timeout = timeout; + data->interval = interval; + data->prefer_blank = prefer_blank; + data->allow_exp = allow_exp; + + return data; +} + +void set_screensaver(int timeout, int interval, int prefer_blank, int allow_exp) { + int timeout_i, interval_i, prefer_blank_i, allow_exp_i; + + Display *display = OpenDisplay(); + if (display == NULL) return; + XGetScreenSaver(display, &timeout_i, &interval_i, &prefer_blank_i, &allow_exp_i); + + if (timeout != -1) timeout_i = timeout; + if (interval != -1) interval_i = interval; + if (prefer_blank != -1) prefer_blank_i = prefer_blank; + if (allow_exp != -1) allow_exp_i = allow_exp; + + XSetScreenSaver(display, timeout_i, interval_i, prefer_blank_i, allow_exp_i); + + XCloseDisplay(display); +} + +void force_screensaver(bool value) { + Display *display = OpenDisplay(); + if (display == NULL) return; + + if (value == true) + XForceScreenSaver(display, ScreenSaverActive); + else + XForceScreenSaver(display, ScreenSaverReset); + + XCloseDisplay(display); +} diff --git a/src/bindings/screensaver.py b/src/bindings/screensaver.py new file mode 100644 index 0000000..68a30f3 --- /dev/null +++ b/src/bindings/screensaver.py @@ -0,0 +1,76 @@ +import ctypes +import os +from typing import Union +from logger import logger + + +class ScreenSaverData(ctypes.Structure): + """Screensaver data""" + _fields_ = [ + ("timeout", ctypes.c_int), + ("interval", ctypes.c_int), + ("prefer_blank", ctypes.c_int), + ("allow_exp", ctypes.c_int), + ] + + def __str__(self): + timeout = getattr(self, "timeout") + interval = getattr(self, "interval") + prefer_blank = getattr(self, "prefer_blank") + allow_exp = getattr(self, "allow_exp") + return f"{timeout} {interval} {prefer_blank} {allow_exp}" + +ScreenSaverDataPointer = ctypes.POINTER(ScreenSaverData) + +class ScreenSaver: + """Screensaver wrapper""" + + saved_data: Union[ScreenSaverData, None] + saved: bool = False + + def __init__(self): + dir = os.path.dirname(os.path.realpath(__file__)) + libname = os.path.join(dir, "_screensaver.so") + self.clib = ctypes.CDLL(libname) + self.clib.get_screensaver.restype = ScreenSaverDataPointer + + def get_screensaver(self): + """Gets screensaver data""" + data: ScreenSaverDataPointer = self.clib.get_screensaver() + if data is None: + return + contents: ScreenSaverData = data.contents + return contents + + def set_screensaver(self, timeout = -1, interval = -1, prefer_blank = -1, allow_exp = -1): + """Sets screensaver properties""" + if self.saved: + return + self.saved_data = self.get_screensaver() + self.saved = True + self.clib.set_screensaver( + ctypes.c_int(timeout), + ctypes.c_int(interval), + ctypes.c_int(prefer_blank), + ctypes.c_int(allow_exp) + ) + logger.debug("Screensaver timeout set") + + def reset_screensaver(self): + """Reset screensaver""" + if not self.saved or not self.saved_data: + return + self.clib.set_screensaver( + ctypes.c_int(getattr(self.saved_data, "timeout")), + ctypes.c_int(getattr(self.saved_data, "interval")), + ctypes.c_int(getattr(self.saved_data, "prefer_blank")), + ctypes.c_int(getattr(self.saved_data, "allow_exp")) + ) + self.saved = False + logger.debug("Screensaver reset") + + def force_screensaver(self, value: bool): + """Force screensaver""" + self.clib.force_screensaver(ctypes.c_bool(value)) + +screensaver = ScreenSaver() diff --git a/src/bridge/Greeter.py b/src/bridge/Greeter.py index a4d4ca5..503cf7f 100644 --- a/src/bridge/Greeter.py +++ b/src/bridge/Greeter.py @@ -43,7 +43,7 @@ from browser.bridge import Bridge, BridgeObject from config import web_greeter_config from utils.battery import Battery -from utils.screensaver import screensaver +from bindings.screensaver import screensaver from utils.brightness import BrightnessController from . import ( diff --git a/src/browser/browser.py b/src/browser/browser.py index f6dfe01..49d9bd2 100644 --- a/src/browser/browser.py +++ b/src/browser/browser.py @@ -61,7 +61,7 @@ from browser.window import MainWindow from logger import logger from config import web_greeter_config -from utils.screensaver import screensaver +from bindings.screensaver import screensaver # pylint: disable-next=unused-import # Do not ever remove this import diff --git a/src/utils/screensaver.py b/src/utils/screensaver.py deleted file mode 100644 index 0165599..0000000 --- a/src/utils/screensaver.py +++ /dev/null @@ -1,60 +0,0 @@ -from Xlib import X -from Xlib.display import Display -from Xlib.error import DisplayError -from logger import logger - -class Screensaver: - """Screensaver class""" - - display: Display - available: bool = False - saved_data: dict[str, int] - saved: bool = False - - def __init__(self): - self.init_display() - - def init_display(self): - """Init display""" - try: - self.display = Display() - self.available = True - except DisplayError as err: - logger.error("Xlib error: %s", err) - - def set_screensaver(self, timeout: int): - """Set screensaver timeout""" - if self.saved or not self.available: - return - self.display.sync() - self.display.sync() - # pylint: disable-next=protected-access - data: dict[str, int] = self.display.get_screen_saver()._data or {} - self.saved_data = data - self.saved = True - - self.display.set_screen_saver(timeout, - data["interval"], - data["prefer_blanking"], - data["allow_exposures"]) - self.display.flush() - logger.debug("Screensaver timeout set") - - def reset_screensaver(self): - """Reset screensaver""" - if not self.saved or not self.available: - return - self.display.sync() - self.display.set_screen_saver(self.saved_data["timeout"], - self.saved_data["interval"], - self.saved_data["prefer_blanking"], - self.saved_data["allow_exposures"]) - self.display.flush() - self.saved = False - logger.debug("Screensaver reset") - - def force_screensaver(self, value: bool): - """Force screensaver""" - self.display.force_screen_saver(X.ScreenSaverActive if value else X.ScreenSaverReset) - -screensaver = Screensaver()