2013-10-30

How to query the X11 screen saver state

This blog post explains how to query the X11 screen saver state and other information such as idle time. This is useful on Unix systems if your program wants to know e.g how long the user has been idle.

If you need only the idle time, and you have TCL/Tk version 8.5 or later installed, run the following command (without the leading $). It prints the number of seconds the user has been idle.

$ echo 'puts [tk inactive]; exit' | wish

If you need more information about the screen saver state or you need better accuracy (milliseconds), then compile and run the following C program:

#define DUMMY \
    set -ex; \
    gcc -s -O2 -W -Wall -o \
        get_x11_screen_saver_info get_x11_screen_saver_info.c \
        -L/usr/X11R6/lib -lX11 -lXext -lXss; \
    exit 2

/*
 * get_x11_screen_saver_info: Get and print X11 screen saver state and info
 * by pts@fazekas.hu at Wed Oct 30 08:28:35 CET 2013
 */

#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/scrnsaver.h>

int main(int argc, char **argv) {
  static XScreenSaverInfo *info;
  Display *display;
  int screen; 
  (void)argc; (void)argv;
  info = XScreenSaverAllocInfo();
  if ((display=XOpenDisplay(NULL)) == NULL) {
    fprintf(stderr, "error: can't open display\n");
    return 2;
  }
  screen = DefaultScreen(display);
  if (!XScreenSaverQueryInfo(display, RootWindow(display, screen), info)) {
    fprintf(stderr, "error: cannot query screen saver info\n");
    return 3;
  }
  printf("idle for %lu ms\n", (unsigned long)info->idle);
  if (info->state == ScreenSaverDisabled) {
    printf("state disabled\n");
  } else if (info->state == ScreenSaverOn) {
    printf("state on for %lu ms\n", (unsigned long)info->til_or_since);
  } else if (info->state == ScreenSaverOff) {
    printf("state off until %lu ms\n", (unsigned long)info->til_or_since);
  } else {
    printf("state unknown\n");
  }
  if (info->kind == ScreenSaverBlanked) {
    printf("kind blanked\n");
  } else if (info->kind == ScreenSaverInternal) {
    printf("kind internal\n");
  } else if (info->kind == ScreenSaverExternal) {
    printf("kind external\n");
  } else {
    printf("kind unknown\n");
  }
  XFree(info);
  XCloseDisplay(display); 
  return 0;
}

Please note that it's possible to affect the screen saver with the xset s ... command, and it's possible to affect the monitor power saving (sleep) with the xset dpms ... command. There are no such widely available commands for querying the screen saver state. The xset ... commands work differently for different display drivers. You may want to do a sleep .1 && xset ... to put your monitor to sleep right away.

No comments: