summaryrefslogtreecommitdiffstats
path: root/src/x11util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/x11util.c')
-rw-r--r--src/x11util.c405
1 files changed, 405 insertions, 0 deletions
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 <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/dpms.h>
+#include <X11/extensions/scrnsaver.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+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;
+}
+