Two previous posts (X11 urgency hint and notifications and Already-running process notifications) talked about ways to notify the user about terminating shell processes. I've been living with this setup for a little while, and I just thought of a better way to do this. Instead of the user asking for notifications about particular processes, why not get notifications about all processes?

As before, I'm using the X11 urgency hint. This hint is automatically removed by the window manager when the hinted window is focused. Thus if you set an urgency hint on an already-focused window, nothing will happen. Thus setting urgency on completion of every single command won't generate too much noise, since most of the time we're in the same terminal window at the start and the stop of the command. You will see a notification when you move to a different window before the process exits, which is exactly what you want here.

zsh has a convenient hook that can be used for this: precmd is called right before the shell prompt is printed. So to notify on all completions, you can put into your .zshrc:

function precmd {
  seturgent
}

This works, with one caveat: as described previously, seturgent is a perl script, and calling it this way one can feel the overhead. It feels slower than it should be. Since seturgent isn't doing any searching here, I rewrote the chunk of it we're using in C. As one would think, it's way quicker:

seturgent_fast.c

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

int main(void)
{
    const char* window_idstring = getenv("WINDOWID");
    if( window_idstring == NULL )
    {
        fprintf(stderr, "No WINDOWID set\n");
        return 1;
    }
    Window w = atoi(window_idstring);
    if( w <= 0 )
    {
        fprintf(stderr, "Couldn't parse window id '%s'\n",
                window_idstring);
        return 1;
    }


    Display* display;
    const char* displaystring = getenv("DISPLAY");
    if( displaystring == NULL )
    {
        fprintf(stderr, "No DISPLAY set\n");
        return 1;
    }

    display = XOpenDisplay(displaystring);
    if( display == NULL )
    {
        fprintf(stderr, "Couldn't open display '%s\n", displaystring);
        return 1;
    }

    XWMHints* hints = XGetWMHints(display, w);
    if( hints == NULL )
    {
        fprintf(stderr, "Couldn't retrieve hints\n");
        return 1;
    }

    hints->flags |= XUrgencyHint;
    XSetWMHints(display, w, hints);

    XFree(hints);
    XFlush(display);
    XCloseDisplay(display);
    return 0;
}

This can be built simply with

gcc -o seturgent_fast{,.c} -lX11

Running this for a bit the main discovery is that it's a bit easier to maintain focus. Previously, I'd start a build or APT update (or whatever), then go do something else, checking on the progress of the long task periodically. This punctuated workflow is fairly inefficient, and the notification system help to minimize it as much as is possible.

So yeah. I'll run this for a bit more, and we'll see if there's more to improve.