~fnux/monitor-handler

a85cadaa9df3162e5fc4496801a2fcb024f57010 — Timothée Floure 2 years ago
Initial commit: event listener and simple handler
6 files changed, 194 insertions(+), 0 deletions(-)

A .gitignore
A Makefile
A README
A monitor-event-handler
A monitor-event-handler.sh
A monitor-event-listener.c
A  => .gitignore +1 -0
@@ 1,1 @@
monitor-event-listener

A  => Makefile +22 -0
@@ 1,22 @@
CFLAGS += -std=c99 -pedantic -g -D DEBUG

# Stuff you shouldn't change, be super pedantic
CFLAGS += -W -Wall -Wextra -pedantic-errors -Wcast-align -Wcast-qual
CFLAGS += -Wwrite-strings -Wfloat-equal -Wlogical-op -Wpointer-arith -Wformat=2
CFLAGS += -Winit-self -Wuninitialized -Wmaybe-uninitialized -Wshadow
CFLAGS += -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes
CFLAGS += -Wconversion

LDLIBS += -lXrandr -lX11

.PHONY: clean

all: build

build: monitor-event-listener

clean:
	 rm -f *.o monitor-event-listener

style:
	 astyle -A8 *.c

A  => README +14 -0
@@ 1,14 @@
# monitor-handler

I have access (home, work, club room, ...) to various external screen and often
plug/unplug them to my laptop. Since I don't use a full
[Desktop Environment](https://en.wikipedia.org/wiki/Desktop_environment), their configuration is
not automagically handled whatever us shipped with Gnome, KDE or similar nice
but bloated DE. I figured it would not be to difficult to write a few scripts
to handle it myself:

  * `monitor-event-listener`: simple C program listening for X event. It calls
    `monitor-event-handler` with the output name and action (plugged or unplugged)
    when a "monitor event" is detected.
  * monitor-event-handler: simple shell script taking the output name and
    action as arguments and executing preconfigured actions.

A  => monitor-event-handler +29 -0
@@ 1,29 @@
#!/bin/bash

if [ "$#" -lt 4 ]; then
  echo "Missing arguments !"
  exit 1
fi

while [ "$#" -gt 0 ]; do
  case "$1" in
    --output) output="$2"; shift 2;;
    --action) action="$2"; shift 2;;
    *) echo "Unknown parameter passed: $1"; exit 1;;
  esac
done

echo "Marching: $action $output"
case "$output $action" in
  "DP-1 connect")
    xrandr --output DP-1 --mode 2560x1080 --pos 1920x0 --rotate normal
    nitrogen --restore
    ;;
  "DP-1 disconnect")
    xrandr --output DP-1 --off
    nitrogen --restore
    ;;
  *)
    echo "-> Unknown combination."
    ;;
esac

A  => monitor-event-handler.sh +29 -0
@@ 1,29 @@
#!/bin/bash

if [ "$#" -lt 4 ]; then
  echo "Missing arguments !"
  exit 1
fi

while [ "$#" -gt 0 ]; do
  case "$1" in
    --output) output="$2"; shift 2;;
    --action) action="$2"; shift 2;;
    *) echo "Unknown parameter passed: $1"; exit 1;;
  esac
done

echo "Marching: $action $output"
case "$output $action" in
  "DP-1 connect")
    xrandr --output DP-1 --mode 2560x1080 --pos 1920x0 --rotate normal
    nitrogen --restore
    ;;
  "DP-1 disconnect")
    xrandr --output DP-1 --off
    nitrogen --restore
    ;;
  *)
    echo "-> Unknown combination."
    ;;
esac

A  => monitor-event-listener.c +99 -0
@@ 1,99 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>

#define CMD_BASE "monitor-event-handler"
#define CMD_BUFFER_SIZE 255
#define CMD_ARG_BUFF 255

Display *dpy = NULL;
int screen;

static void print_usage(char * program)
{
    printf("Usage: %s\n", program);
}

static int spawn_handler(char* output, char* change)
{
    char cmd[CMD_BUFFER_SIZE];
    memset(cmd, '\0', CMD_BUFFER_SIZE);
    sprintf(cmd, "%s --output %s --action %s", CMD_BASE, output, change);

    printf("  -> Spawning handler: %s\n", cmd);
    int status = system(cmd);

    return status;
}

int main (int argc, char **argv)
{
    char *displayname = NULL;
    Window w = 0;
    int done;

    if (argc > 1) {
        print_usage(*argv);
        exit(EXIT_FAILURE);
    }

    dpy = XOpenDisplay (displayname);
    screen = DefaultScreen (dpy);
    w = RootWindow(dpy, screen);

    int mask = RROutputChangeNotifyMask;
    XRRSelectInput (dpy, w, mask);

    for (done = 0; !done;) {
        XEvent event;
        XRRScreenResources *screen_resources;
        XRROutputChangeNotifyEvent *e = NULL;
        XRROutputInfo *output_info = NULL;
        XRRModeInfo *mode_info = NULL;

        XNextEvent (dpy, &event);
        XRRUpdateConfiguration (&event);
        screen_resources = XRRGetScreenResources (dpy, ((XRRNotifyEvent*) &event)->window);
        e = (XRROutputChangeNotifyEvent *) &event;

        if (screen_resources) {
            output_info = XRRGetOutputInfo (dpy, screen_resources, e->output);
            for (int i = 0; i < screen_resources->nmode; i++)
                if (screen_resources->modes[i].id == e->mode) {
                    mode_info = &screen_resources->modes[i];
                    break;
                }
        }

        char output_name[CMD_ARG_BUFF+1];
        memset(output_name, '\0', CMD_ARG_BUFF+1);
        if (output_info) {
            strncpy(output_name, output_info->name, CMD_ARG_BUFF);
        } else {
            sprintf(output_name, "%lu", e->output);
        }

        char connection[CMD_ARG_BUFF+1];
        memset(connection, '\0', CMD_ARG_BUFF+1);
        switch (e->connection) {
        case RR_Connected:
            strncpy(connection, "connect", CMD_ARG_BUFF);
            break;
        case RR_Disconnected:
            strncpy(connection, "disconnect", CMD_ARG_BUFF);
            break;
        default:
            sprintf(connection, "%d", e->connection);
        }

        printf("New event: %s on %s\n", connection, output_name);

        spawn_handler(output_name, connection);
        XRRFreeOutputInfo (output_info);
    }

    XCloseDisplay (dpy);
    return EXIT_SUCCESS;
}