aboutsummaryrefslogtreecommitdiff
path: root/xchord.c
blob: b8acc0fa36614e152ce21a365d205ac4c4e1498c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
 *  xchord -- run dwim on button 1 + 3
 *
 *  This is a simple program that runs dwim whenever the right
 *  mouse button is pressed while the left mouse button is held.
 *
 *  It does *not* block the button events.  That means that both
 *  clicks will be registered by other programs.  This may be a
 *  problem in programs that open a context menu on button 3.
 *
 *  On NetBSD, xchord should be compiled with the following flags:
 *
 *     CFLAGS = -I/usr/X11R7/include
 *     LDFLAGS = -L/usr/X11R7/lib -Wl,-R/usr/X11R7/lib -lXi
 *
 *  xchord is written by John Ankarström <john (at) ankarstrom.se>.
 */

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/extensions/XInput2.h>
#include <X11/Xlib.h>

#define die(s, ...) do {			\
	fprintf(stderr, "%s: ", argv[0]);	\
	fprintf(stderr, __VA_ARGS__);		\
	fprintf(stderr, "\n");			\
	exit(s);				\
} while (0)

Display *dpy;
Window rwin;

int
main(int argc, char *argv[])
{
	int p;
	unsigned char mask[(XI_LASTEVENT+7)/8];
	XEvent ev;
	XGenericEventCookie *cookie;
	XIEvent *xiev;
	XIEventMask evmasks[1];
	XIRawEvent *rev;

	dpy = XOpenDisplay(NULL);
	if (!dpy) die(1, "could not open display");
	rwin = DefaultRootWindow(dpy);

	/* select events */
	memset(mask, 0, sizeof(mask));

	XISetMask(mask, XI_RawButtonPress);
	XISetMask(mask, XI_RawButtonRelease);

	evmasks[0].deviceid = XIAllMasterDevices;
	evmasks[0].mask_len = sizeof(mask);
	evmasks[0].mask = mask;

	XISelectEvents(dpy, rwin, evmasks, 1);
	XFlush(dpy);

	cookie = &ev.xcookie;
	p = 0;

	/* watch for events */
	for (;;) {
		XNextEvent(dpy, &ev);
		XPutBackEvent(dpy, &ev);
		if (!XCheckTypedEvent(dpy, GenericEvent, &ev))
			continue;
		if (cookie->type != GenericEvent || !XGetEventData(dpy, cookie))
			continue;

		xiev = (XIEvent*)cookie->data;
		rev = (XIRawEvent*)xiev;

		switch (cookie->evtype) {
		case XI_RawButtonPress:
			/* 1 pressed (first step) */
			if (rev->detail == 1) p = 1;
			/* 1 + 3 pressed (second step) */
			if (p > 0 && rev->detail == 3) p = 2;
			break;
		case XI_RawButtonRelease:
			/* 1 pressed + 3 pressed + 3 released (third step) */
			if (p == 2 && rev->detail == 3) {
				p = 3;
				if (vfork() == 0) {
					execlp("dwim", "dwim", NULL);
					err(1, "execlp");
				}
			}
			/* 1 released (reset) */
			if (p == 3 && rev->detail == 1) p = 0;
			break;
		}

		XFreeEventData(dpy, cookie);
	}
}