From f7cd45464fd7c037f6a60098ae77760998b3b4b6 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 10 May 2019 15:25:43 +0200 Subject: Initial commit --- src/x11util.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 src/x11util.c (limited to 'src/x11util.c') diff --git a/src/x11util.c b/src/x11util.c new file mode 100644 index 0000000..18e2fe4 --- /dev/null +++ b/src/x11util.c @@ -0,0 +1,405 @@ +#include "x11util.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static XErrorHandler old_handler = 0; + +static int runClient( const char *display, int fd ); + +static void getLockState( Display *dpy, struct x11state *state ); + +static Window getScreenSaverWindow( Display *dpy ); + +static Window find_screensaver_window( Display *dpy, char **version ); + +bool getX11IdleTimes( const char *xauth, const char *display, struct x11state *state ) +{ + int fds[2]; + if ( pipe( fds ) == -1 ) { + perror( "Could not create pipe for X11 stuff" ); + return false; + } + if ( doublefork() ) { + // The child + close( fds[0] ); + if ( xauth[0] != '\0' && strcmp( xauth, "-" ) != 0 ) { + setenv( "XAUTHORITY", xauth, 1 ); + } + int ret = runClient( display, fds[1] ); + close( fds[1] ); + exit( ret ); + } + // Parent -- wait + bool ok = false; + close( fds[1] ); + if ( ! waitRead( fds[0], 1000 ) ) { + fprintf( stderr, "X11 child didn't write to pipe in time\n" ); + } else { + // We should be able to read a reply + if ( read( fds[0], state, sizeof(*state) ) != sizeof(*state) ) { + fprintf( stderr, "X11 child wrote partial data\n" ); + } else { + ok = true; + } + } + close( fds[0] ); + return ok; +} + +static int printErrorHandler( Display *dpy, XErrorEvent *error ) +{ + char msg[500] = {0}; + + XGetErrorText( dpy, error->error_code, msg, sizeof(msg) ); + fprintf( stderr, "X11 Error %d: %s\n", (int)error->error_code, msg ); + + if( !old_handler ) { + abort(); + } + return ( *old_handler )( dpy, error ); +} + +static void setScreenDpmsFork( const char *xauth, const char *display, int mode ) +{ + Display *dpy; + int dummy; + + if ( xauth[0] != '\0' && strcmp( xauth, "-" ) != 0 ) { + setenv( "XAUTHORITY", xauth, 1 ); + } + dpy = XOpenDisplay( display ); + if ( dpy == NULL ) { + fprintf( stderr, "setScreenDpms: Cannot open display\n" ); + return; + } + if ( ! DPMSQueryExtension( dpy, &dummy, &dummy ) || ! DPMSCapable( dpy ) ) { + fprintf( stderr, "%s doesn't support DPMS.\n", display ); + return; + } + //DPMSInfo( dpy, &dummy, &onoff ); + old_handler = XSetErrorHandler( printErrorHandler ); + DPMSForceLevel( dpy, mode == SCREEN_ON ? DPMSModeOn : DPMSModeSuspend ); + XSync( dpy, False ); + XSetErrorHandler( old_handler ); + old_handler = 0; + XCloseDisplay( dpy ); +} + +void setScreenDpms( const char *xauth, const char *display, int mode ) +{ + if ( _testmode ) { + printf( "Testmode: Not setting screen to %d\n", mode ); + return; + } + if ( ! doublefork() ) + return; + sleep( 2 ); // Sleep a bit in case this was called right after enabling the screen saver + setScreenDpmsFork( xauth, display, mode ); + exit( 0 ); +} + +static void enableScreenSaverFork( const char *xauth, const char *display, const int state ) +{ + if ( xauth[0] != '\0' && strcmp( xauth, "-" ) != 0 ) { + setenv( "XAUTHORITY", xauth, 1 ); + } + Display *dpy; + Atom XA_SCREENSAVER; + Atom XA_ACTION; + + dpy = XOpenDisplay( display ); + if ( dpy == NULL ) { + fprintf( stderr, "enableScreenSaver: Cannot open display %s\n", display ); + return; + } + + Window window = getScreenSaverWindow( dpy ); + if( ! window ) + return; + + XA_SCREENSAVER = XInternAtom( dpy, "SCREENSAVER", False ); + if ( state == SAVER_LOCKED ) { + XA_ACTION = XInternAtom( dpy, "LOCK", False ); + } else { + XA_ACTION = XInternAtom( dpy, "ACTIVATE", False ); + } + /* + XEvent event = { + .xany.type = ClientMessage, + .xclient.display = dpy, + .xclient.window = window, + .xclient.message_type = XA_SCREENSAVER, + .xclient.format = 32, + .xclient.data.l[0] = (long) XA_ACTION, + }; + */ + XEvent event; + event.xany.type = ClientMessage; + event.xclient.display = dpy; + event.xclient.window = window; + event.xclient.message_type = XA_SCREENSAVER; + event.xclient.format = 32; + memset (&event.xclient.data, 0, sizeof(event.xclient.data)); + event.xclient.data.l[0] = (long) XA_ACTION; + event.xclient.data.l[1] = 0; + event.xclient.data.l[2] = 0; + old_handler = XSetErrorHandler( printErrorHandler ); + if ( ! XSendEvent( dpy, window, False, 0, &event ) ) { + fprintf( stderr, "enableScreenSaver: XSendEvent to window failed.\n" ); + } + XSync( dpy, False ); + XSetErrorHandler( old_handler ); + old_handler = 0; + XCloseDisplay( dpy ); +} + +void enableScreenSaver( const char *xauth, const char *display, const int state ) +{ + if ( state != SAVER_ACTIVE && state != SAVER_LOCKED ) { + fprintf( stderr, "enableScreenSaver: Invalid state %d requested.\n", state ); + return; + } + if ( _testmode ) { + printf( "Testmode: Not setting screen saver to %d\n", state ); + return; + } + if ( ! doublefork() ) + return; + enableScreenSaverFork( xauth, display, state ); + exit( 0 ); +} + +static int runClient( const char *display, int fd ) +{ + struct x11state buffer = {0}; + XScreenSaverInfo *ssi; + Display *dpy; + int dummy; + + dpy = XOpenDisplay( display ); + if ( dpy == NULL ) { + fprintf( stderr, "Cannot open display\n" ); + return 1; + } + if ( !XScreenSaverQueryExtension( dpy, &dummy, &dummy ) ) { + fprintf( stderr, "screen saver extension not supported\n" ); + return 1; + } + ssi = XScreenSaverAllocInfo(); + if ( ssi == NULL ) { + fprintf( stderr, "Cannot allocate screen saver info\n" ); + return 1; + } + if ( ! XScreenSaverQueryInfo(dpy, DefaultRootWindow( dpy ), ssi ) ) { + fprintf( stderr, "Cannot query screen saver info\n" ); + return 1; + } + + buffer.idleSeconds = ssi->idle / 1000; + XFree( ssi ); + getLockState( dpy, &buffer ); + if ( DPMSQueryExtension( dpy, &dummy, &dummy ) && DPMSCapable( dpy ) ) { + CARD16 state; + BOOL onoff; + if ( DPMSInfo( dpy, &state, &onoff ) && onoff ) { + if ( state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff ) { + buffer.screenStandby = SCREEN_OFF; + } else if ( state == DPMSModeOn ) { + buffer.screenStandby = SCREEN_ON; + } + } + } + + if ( write( fd, &buffer, sizeof(buffer) ) != sizeof(buffer) ) { + fprintf( stderr, "Error writing to pipe from child\n" ); + } + return 0; +} + +static Window getScreenSaverWindow( Display *dpy ) +{ + XWindowAttributes xgwa; + + char *v = NULL; + Window window = find_screensaver_window( dpy, &v ); + if( ! window ) { + return 0; + } + if( ! v || ! *v ) { + fprintf( stderr, "version property not set on window 0x%x?\n", + ( unsigned int ) window ); + return 0; + } + + /* Select for property change events, so that we can read the response. */ + XGetWindowAttributes( dpy, window, &xgwa ); + XSelectInput( dpy, window, xgwa.your_event_mask | PropertyChangeMask ); + + XClassHint hint = {0}; + XGetClassHint( dpy, window, &hint ); + if( ! hint.res_class ) { + fprintf( stderr, "class hints not set on window 0x%x?\n", + ( unsigned int ) window ); + return 0; + } + return window; +} + +static void getLockState( Display *dpy, struct x11state *state ) +{ + Atom XA_LOCK, XA_BLANK; + Atom XA_SCREENSAVER_STATUS; + XA_SCREENSAVER_STATUS = XInternAtom( dpy, "_SCREENSAVER_STATUS", False ); + XA_LOCK = XInternAtom( dpy, "LOCK", False ); + XA_BLANK = XInternAtom( dpy, "BLANK", False ); + + Window window = getScreenSaverWindow( dpy ); + if ( ! window ) + return; + + Atom type; + int format; + unsigned long nitems, bytesafter; + unsigned char *dataP = 0; + + if( XGetWindowProperty( dpy, + RootWindow( dpy, 0 ), + XA_SCREENSAVER_STATUS, + 0, 999, False, XA_INTEGER, + &type, &format, &nitems, &bytesafter, + &dataP ) + != Success ) { + fprintf( stderr, "Foof foof! No property\n" ); + return; + } + if ( ! type || dataP == NULL ) + return; + + Atom *data = ( Atom * ) dataP; + if( type != XA_INTEGER || nitems < 3 ) { + if( data ) { + free( data ); + } + fprintf( stderr, "bad status format on root window.\n" ); + return; + } + + Atom blanked = ( Atom ) data[0]; + time_t tt = ( time_t ) data[1]; + + if( tt <= ( time_t ) 666000000L ) { /* early 1991 */ + fprintf( stderr, "Bad lock time reported\n" ); + return; + } + + if( blanked == XA_LOCK ) { + // Got valid lock time + state->lockTimestamp = tt; + state->saverState = SAVER_LOCKED; + } else if ( blanked == XA_BLANK ) { + state->lockTimestamp = tt; + state->saverState = SAVER_ACTIVE; + } +} + +static Bool got_badwindow = False; +static int BadWindow_ehandler( Display *dpy, XErrorEvent *error ) +{ + if( error->error_code == BadWindow ) { + got_badwindow = True; + return 0; + } + if( !old_handler ) { + abort(); + } + return ( *old_handler )( dpy, error ); +} + +static Window find_screensaver_window( Display *dpy, char **version ) +{ + Window root = RootWindowOfScreen( DefaultScreenOfDisplay( dpy ) ); + Window root2, parent, *kids; + unsigned int nkids; + Atom XA_SCREENSAVER_VERSION; + XA_SCREENSAVER_VERSION = XInternAtom( dpy, "_SCREENSAVER_VERSION", False ); + + if( version ) { + *version = 0; + } + + if( ! XQueryTree( dpy, root, &root2, &parent, &kids, &nkids ) ) { + fprintf( stderr, "fssw: Cannot query tree\n" ); + return 0; + } + if( root != root2 ) { + fprintf( stderr, "fssw: root != root2\n" ); + return 0; + } + if( parent ) { + fprintf( stderr, "fssw: Got parent!\n" ); + return 0; + } + if( !( kids && nkids ) ) { + return 0; + } + for( unsigned int i = 0; i < nkids; i++ ) { + Atom type; + int format; + unsigned long nitems, bytesafter; + unsigned char *v; + int status; + + /* We're walking the list of root-level windows and trying to find + the one that has a particular property on it. We need to trap + BadWindows errors while doing this, because it's possible that + some random window might get deleted in the meantime. (That + window won't have been the one we're looking for.) + */ + XSync( dpy, False ); + if( old_handler ) { + abort(); + } + got_badwindow = False; + old_handler = XSetErrorHandler( BadWindow_ehandler ); + status = XGetWindowProperty( dpy, kids[i], + XA_SCREENSAVER_VERSION, + 0, 200, False, XA_STRING, + &type, &format, &nitems, &bytesafter, + &v ); + XSync( dpy, False ); + XSetErrorHandler( old_handler ); + old_handler = 0; + + if( got_badwindow ) { + status = BadWindow; + got_badwindow = False; + } + + if( status == Success && type != None ) { + Window ret = kids[i]; + if( version ) { + *version = ( char* )v; + } + XFree( kids ); + return ret; + } + } + + if( kids ) { + XFree( kids ); + } + return 0; +} + -- cgit v1.2.3-55-g7522