Source code for FabLabKasse.libs.pxss.pxss

#!/usr/bin/env python
# Copyright 2009 Yu-Jie Lin
# Copyright 2003 David McClosky
#
# This code is licensed under the GPLv3
#
# This Python code does not require any Python binding library, such as
# python-xlib or PyXSS. It directly accesses libXss via ctypes.
#
# It was written with intention of using PyXSS' IdleTracker but without PyXSS.
# You don't need to install PyXSS.
#
# Note:
# 1. The code isn't tested/finished completely, you might hit something if you
#    have two displays and/or your default screen isn't Screen 0.
# 2. For the information we do not need and their types are pointers, all are
#    set with c_void_p.
# 3. No error handling.
# 4. This is not a replacement of PyXSS.
#
# PyXSS' author is David McClosky and this script use a big chuck of code from
# it. You can download the PyXSS code at http://bebop.bigasterisk.com/python
#
# The rest of this script is written by Yu-Jie Lin ( http://livibetter.mp/ )
# modifications by Patrick Kanzler <patrick.kanzler@fablab.fau.de> in 2015.


from ctypes import *
import logging

normalmode = True  # this variable is set to False if libXss.so is not found
try:
    libXss = CDLL('libXss.so.1')
except OSError:
    # the system does not provide libXss.so
    logging.info("libXss.so not found, IdleState will not be available")
    normalmode = False


[docs]class Screen(Structure): _fields_ = [ ('ext_data', c_void_p), # XExtData *ext_data; /* hook for extension to hang data */ ('display', c_void_p), # struct _XDisplay *display;/* back pointer to display structure */ # X.h:101:typedef XID Window; # X.h:71:typedef unsigned long XID; ('root', c_ulong), # Window root; /* Root window id. */ # We only need root, but we also need precise size of Screen to offset array index. ('width', c_int), # int width, height; /* width and height of screen */ ('height', c_int), ('mwidth', c_int), # int mwidth, mheight; /* width and height of in millimeters */ ('mheight', c_int), ('ndepths', c_int), # int ndepths; /* number of depths possible */ ('depths', c_void_p), # Depth *depths; /* list of allowable depths on the screen */ ('root_depth', c_int), # int root_depth; /* bits per pixel */ ('root_visual', c_void_p), # Visual *root_visual; /* root visual */ # GC is structure pointer ('default_gc', c_void_p), # GC default_gc; /* GC for the root root visual */ # X.h:109:typedef XID Colormap; ('cmap', c_ulong), # Colormap cmap; /* default color map */ ('white_pixel', c_ulong), # unsigned long white_pixel; ('black_pixel', c_ulong), # unsigned long black_pixel; /* White and Black pixel values */ ('min_maps', c_int), # int max_maps, min_maps; /* max and min color maps */ ('backing_store', c_int), # int backing_store; /* Never, WhenMapped, Always */ ('save_unders', c_bool), # Bool save_unders; ('root_input_mask', c_long), # long root_input_mask; /* initial root input mask */ ]
[docs]class Display(Structure): _fields_ = [ ('ext_data', c_void_p), # XExtData *ext_data; /* hook for extension to hang data */ ('private1', c_void_p), # struct _XPrivate *private1; ('fd', c_int), # int fd; /* Network socket. */ ('private2', c_int), # int private2; ('proto_major_version', c_int), # int proto_major_version;/* major version of server's X protocol */ ('proto_minor_version', c_int), # int proto_minor_version;/* minor version of servers X protocol */ ('vendor', c_char_p), # char *vendor; /* vendor of the server hardware */ # X.h:71:typedef unsigned long XID; ('private3', c_ulong), # XID private3; ('private4', c_ulong), # XID private4; ('private5', c_ulong), # XID private5; ('private6', c_int), # int private6; # XXX NOT SURE ('resource_alloc', c_long), # XID (*resource_alloc)( /* allocator function */ # struct _XDisplay* # ); ('byte_order', c_int), # int byte_order; /* screen byte order, LSBFirst, MSBFirst */ ('bitmap_unit', c_int), # int bitmap_unit; /* padding and data requirements */ ('bitmap_pad', c_int), # int bitmap_pad; /* padding requirements on bitmaps */ ('bitmap_bit_order', c_int), # int bitmap_bit_order; /* LeastSignificant or MostSignificant */ ('nformats', c_int), # int nformats; /* number of pixmap formats in list */ ('pixmap_format', c_void_p), # ScreenFormat *pixmap_format; /* pixmap format list */ ('private8', c_int), # int private8; ('release', c_int), # int release; /* release of the server */ ('private9', c_void_p), # struct _XPrivate *private9, *private10; ('private10', c_void_p), ('qlen', c_int), # int qlen; /* Length of input event queue */ ('last_request_read', c_ulong), # unsigned long last_request_read; /* seq number of last event read */ ('request', c_long), # unsigned long request; /* sequence number of last request. */ # Xlib.h:87:typedef char *XPointer; ('private11', c_char_p), # XPointer private11; ('private12', c_char_p), # XPointer private12; ('private13', c_char_p), # XPointer private13; ('private14', c_char_p), # XPointer private14; ('max_request_size', c_uint), # unsigned max_request_size; /* maximum number 32 bit words in request*/ ('db', c_void_p), # struct _XrmHashBucketRec *db; ('private15', c_int), # int (*private15)( # struct _XDisplay* # ); ('display_name', c_char_p), # char *display_name; /* "host:display" string used on this connect*/ ('default_screen', c_int), # int default_screen; /* default screen for operations */ ('nscreens', c_int), # int nscreens; /* number of screens on this server*/ ('screens', c_void_p), # Screen *screens; /* pointer to list of screens */ # We do not need the rest # unsigned long motion_buffer; /* size of motion buffer */ # unsigned long private16; # int min_keycode; /* minimum defined keycode */ # int max_keycode; /* maximum defined keycode */ # XPointer private17; # XPointer private18; # int private19; # char *xdefaults; /* contents of defaults from server */ # /* there is more to this structure, but it is private to Xlib */ ]
[docs]class XScreenSaverInfo(Structure): _fields_ = [ # X.h:101:typedef XID Window; # X.h:71:typedef unsigned long XID; ('window', c_ulong), # Window window; /* screen saver window - may not exist */ ('state', c_int), # int state; /* ScreenSaverOff, ScreenSaverOn, ScreenSaverDisabled*/ ('kind', c_int), # int kind; /* ScreenSaverBlanked, ...Internal, ...External */ ('til_or_since', c_ulong), # unsigned long til_or_since; /* time til or since screen saver */ ('idle', c_ulong), # unsigned long idle; /* total time since last user input */ ('eventMask', c_ulong), # unsigned long eventMask; /* currently selected events for this client */ ]
[docs]def get_info(p_display=None, default_root_window=None, p_info=None): if p_display is None: p_display = _p_display if default_root_window is None: default_root_window = _default_root_window if p_info is None: p_info = _p_info libXss.XScreenSaverQueryInfo(p_display, default_root_window, p_info) return p_info.contents
if normalmode: # Specify return types libXss.XOpenDisplay.restype = POINTER(Display) libXss.XScreenSaverAllocInfo.restype = POINTER(XScreenSaverInfo) _p_display = libXss.XOpenDisplay('') try: display = _p_display.contents # Get DefaultRootWindow(display), which is a macro # Xlib.h # define DefaultRootWindow(dpy) (ScreenOfDisplay(dpy,DefaultScreen(dpy))->root) # define ScreenOfDisplay(dpy, scr)(&((_XPrivDisplay)dpy)->screens[scr]) # define DefaultScreen(dpy) (((_XPrivDisplay)dpy)->default_screen) # ==> I found the following expanding version via Google Code Search # ==> #define DefaultRootWindow(dpy) (((dpy)->screens[(dpy)->default_screen]).root) # Convert display.screens from c_void_p to POINTER(Screen * display.nscreens) screens = cast(display.screens, POINTER(Screen * display.nscreens)) _default_root_window = screens.contents[display.default_screen].root _p_info = pointer(XScreenSaverInfo()) del display del screens except ValueError: # null pointer problem, this happens when sphinx generates # documentation on a system without running X11 logging.info("cannot load xss info - not running under X11." "Idle state will not be available.") normalmode = False # The following code are copied directly from PyXSS-2.1/xss/__init__.py without # any modifications.
[docs]class IdleTracker: """Keeps track of idle times, screensaver state, and tells you when you to querying it for the next idle time. All times are in milliseconds. IdleTracker indicates a change in state when your idle time exceeds a certain threshold. See also XSSTracker.""" def __init__(self, when_idle_wait=5000, when_disabled_wait=120000, idle_threshold=60000): """when_idle_wait is the interval at which you should poll when you are already idle. when_disabled_wait is how often you should poll if information is unavailable (default: 2 minutes). idle_threshold is the number of seconds of idle time to constitute being idle.""" self.when_idle_wait = when_idle_wait self.idle_threshold = idle_threshold self.when_disabled_wait = when_disabled_wait # we start with a bogus last_state. this way, the first call to # check_idle will report whether we are idle or not. all subsequent # calls will only tell you if the screensaver state has changed self.last_state = None
[docs] def check_idle(self): """ suggested_time_till_next_check and idle_time is in milliseconds. state_change is one of: - None - No change in state - "idle" - user is idle (idle time is greater than idle threshold) - "unidle" - user is not idle (idle time is less than idle threshold) - "disabled" - idle time not available Note that "disabled" will be returned every time there is an error. :returns: tuple (state_change, suggested_time_till_next_check, idle_time) """ if not normalmode: return ("disabled", self.when_disabled_wait, 0) try: self.info = get_info() except RuntimeError: # XSS can raise a RuntimeError if the # XSS extension cannot be found. return ("disabled", self.when_disabled_wait, 0) idle = self.info.idle if idle > self.idle_threshold: # if we meet the threshold for being # idle, we are now idle current_state = 'idle' # we use the standard polling interval wait_time = self.when_idle_wait else: # otherwise, we are not idle current_state = 'unidle' # wait time is however long it will take for us to go over the # idle threshold wait_time = self.idle_threshold - idle # check to see if our new state is actually a change change = None if self.last_state != current_state: change = current_state self.last_state = current_state return (change, wait_time, self.info.idle)
[docs]class XSSTracker: """Keeps track of idle times, screensaver state, and tells you when you to querying it for the next idle time. All times are in milliseconds. XSSTracker indicates a change in state when your screensaver activates. See also IdleTracker.""" def __init__(self, when_idle_wait=5000, when_disabled_wait=120000): """when_idle_wait is the interval at which you should poll when you are already idle. when_disabled_wait is how often you should poll if the screensaver is disabled and you are using XSS for your idle threshold. If you set idle_threshold to 'xss', it will say that your threshold for becoming idle is whenever the screensaver activates. If you don't use the XScreenSaver extension or otherwise want a different idle threshold, you can specify it in milliseconds.""" self.when_idle_wait = when_idle_wait self.when_disabled_wait = when_disabled_wait # we start by assuming the screen saver is disabled. this way, the # first call to check_idle will report whether the screensaver is # active. all subsequent calls will only tell you if the screensaver # state has changed self.last_state = xss.ScreenSaverDisabled
[docs] def check_idle(self): """suggested_time_till_next_check and idle_time is in milliseconds. state_change is one of: - None - No change in state - "idle" - screensaver has turned on since user is now idle - "unidle" - screensaver has turned off since user is no longer idle - "disabled" - screensaver is disabled or extension not present Note that if the screensaver is disabled, it will return "disabled" every time. :returns: tuple (state_change, suggested_time_till_next_check, idle_time) """ try: self.info = get_info() except RuntimeError: # XSS can raise a RuntimeError if the # XSS extension cannot be found. return ("disabled", self.when_disabled_wait, 0) state = self.info.state # if we're disabled, we tell them that. the polling interval for # disabledness is when_disabled_wait, which defaults to 2 minutes. if state == ScreenSaverDisabled: self.last_state = state return ("disabled", self.when_disabled_wait, 0) # wait_time is how long they should wait before polling again. if state == ScreenSaverOff: # if the screen saver is off, til_or_since will tell us # how long it will take to be activated (whether it will # be activated depends on whether the user is idle for that # period of time) wait_time = self.info.til_or_since else: # otherwise, the screensaver is on. we don't when we will # become unidle, but the when_idle_wait tells us the poll # interval (default is 5 seconds) wait_time = self.when_idle_wait # change is whether or not we have changed. if we have, we use # a string to describe the change if state != self.last_state: if state == ScreenSaverOff: # if we've changed from On to Off # they're not idle anymore change = 'unidle' else: change = 'idle' # if we've changed from Off to On, they've gone # idle else: # otherwise, they haven't changed state change = None self.last_state = state return (change, wait_time, self.info.idle)
if __name__ == "__main__": # this demo shows how you might write a simple poller import time i = IdleTracker(idle_threshold=5000) while 1: info = i.check_idle() print time.asctime(), info, # don't poll more often than 2 seconds wait_time = max(info[1] / 1000, 2) print "wait:", wait_time time.sleep(wait_time)