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.

2013-10-23

How to fix ugly text when printing a Google document

This blog post explains how to fix the ugliness of letter spacing in text printed from a Google document (text document, presentation etc.). An example ugly output (some letters are too close to each other):

To fix it, export the document to PDF (in the File / Download as / PDF menu item), and print the PDF. Don't choose File / Print / Save as PDF, that produces the ugly output above.

2013-10-21

How to start a Unix process in the foreground instead

This blog post explains how to run a Unix process in the foreground if it automatically puts itself into the background. TL;DR Run it like this:

$ prog 3>&1 | cat

Let's suppose you have two shell scripts:

$ cat >fore.sh <<'END'
#! /bin/sh
echo foo; sleep 1; echo bar
END
$ chmod +x fore.sh
$ cat >back.sh <<'END'
#! /bin/sh
(echo foo; sleep 1; echo bar) &
END
$ _

When running ./fore.sh you have to wait a second for it to finish, because it runs in the foreground. If you want to run it in the background, run it as: ./fore.sh & . You'll get back your prompt instantly, and you'll get the bar message one second later. The & is a standard Bourne shell feature to start processes in the background.

Doing the other way round is a bit trickier. When running ./back.sh, you get your prompt back almost instantly, because the sleeping happens in the background. There is also a race condition: sometimes you get foo first, and then the prompt, sometimes the other way round, depending on whether the kernel schedules the interactive shells or the back.sh script first. If you want to run a command in the foreground, but you don't want to change it in the implementation, a simple solution is this:

$ ./back.sh | cat
foo
bar
$ ./fore.sh | cat
foo
bar
$ _

So appending | cat works no matter the program wants to run in the foreground or in the background. The reason why it works is that you don't get your prompt back until cat has finished, and | cat waits for an EOF on the program's stdout, and there is no EOF on a pipe until all processes writing to that pipe have closed it, i.e. (most of the time) until the entire program terminates.

The | cat solution above breaks if the program is smart and closes its stdout early. In this case you get the prompt back immediately. For example, below bar is displayed only about a second after the $ in front of it was displayed.

$ cat >smart.sh <<'END'
#! /bin/sh
(exec >/dev/null; echo foo >&2; exec sh -c 'sleep 1; echo bar >&2') &
END
$ ./smart.sh | cat
foo
$ bar
_

To make it work for even such a program (in addition to back.sh and fore.sh), run it like this:

$ ./smart.sh 3>&1 | cat

The reason why this works is that 3>&1 instructs the shell to make a copy of the file descriptor 1 (stdout) as file descriptor 3. (Any number between 3 and 9 would do. For smarter shells numbers larger than 9 also work.) So even when the program explicitly closes (or redirects) its own stdout, it will still have file descriptor 3 open until it exits, thus preventing the EOF cat is waiting for, thus making the shell continue waiting for cat, thus not giving back the prompt. Most programs tend not to bother explicitly closing file descriptors larger than 2. The kernel closes file descriptors with the close-on-exec bit automatically closed upon an exec* call. Fortunately that doesn't apply to our file descriptor 3, because the shell has created it using the dup2 system call, which creates file descriptor 3 with the close-on-exec flag off.