winpl

LD_PRELOAD-based X11 window manipulator
git clone https://git.sinitax.com/sinitax/winpl
Log | Files | Refs | LICENSE | sfeed.txt

winpl.c (11245B)


      1/* original: https://www.mail-archive.com/devel@xfree86.org/msg05806.html */
      2
      3#include <X11/Xutil.h>
      4#include <X11/Xlib.h>
      5#include <X11/X.h>
      6#include <X11/Xatom.h>
      7#include <X11/extensions/Xinerama.h>
      8
      9#include <sys/types.h>
     10#include <sys/stat.h>
     11#include <sys/utsname.h>
     12
     13#include <dlfcn.h>
     14#include <unistd.h>
     15#include <stdio.h>
     16#include <string.h>
     17#include <stdarg.h>
     18#include <stdbool.h>
     19#include <stdlib.h>
     20
     21#define MAX(a,b) ((a > b) ? a : b)
     22#define MIN(a,b) ((a < b) ? a : b)
     23
     24extern char **environ;
     25
     26static const char prefix[] = "WINPL_";
     27static const int prefixlen = sizeof(prefix) - 1;
     28
     29static void *lib_xlib = NULL;
     30static void (*setwmname)(Display *, Window, XTextProperty *) = NULL;
     31static int (*changeprop)(Display *, Window, Atom, Atom, int, int, const unsigned char *, int) = NULL;
     32
     33static bool hook_name = false;
     34
     35static void
     36warn(const char *fmt, ...)
     37{
     38	va_list ap;
     39
     40	va_start(ap, fmt);
     41	fprintf(stderr, "winpl: warn: ");
     42	vfprintf(stderr, fmt, ap);
     43	fprintf(stderr, "\n");
     44	va_end(ap);
     45}
     46
     47static void
     48err(const char *fmt, ...)
     49{
     50	va_list ap;
     51
     52	va_start(ap, fmt);
     53	fprintf(stderr, "winpl: ");
     54	vfprintf(stderr, fmt, ap);
     55	fprintf(stderr, "\n");
     56	va_end(ap);
     57
     58	exit(1);
     59}
     60
     61static char **
     62iter_args(char **env, const char **key, size_t *keylen, const char **val)
     63{
     64	const char *tok;
     65
     66	for (; *env; env++) {            \
     67		if (strncmp(*env, prefix, prefixlen))
     68			continue;
     69		*key = *env + prefixlen;
     70		tok = strchr(*env, '=');
     71		if (!tok) continue;
     72		*keylen = tok - *env - prefixlen;
     73		*val = tok + 1;
     74		return env + 1;
     75	}
     76
     77	return NULL;
     78}
     79
     80static int
     81intersection_area(XineramaScreenInfo *info, XWindowAttributes *wa)
     82{
     83	int dx, dy;
     84
     85	dx = MIN(wa->x + wa->width, info->x_org + info->width);
     86	dx -= MAX(wa->x, info->x_org);
     87
     88	dy = MIN(wa->y + wa->height, info->y_org + info->height);
     89	dy -= MAX(wa->y, info->y_org);
     90
     91	return MAX(0, dx) * MAX(0, dy);
     92}
     93
     94static int
     95monitor_from_pointer(XineramaScreenInfo *info, int mcount,
     96		Display *display, Window window)
     97{
     98	Window dummy;
     99	int x = 0, y = 0, di, i, screen;
    100	unsigned int dui;
    101
    102	screen = -1;
    103	XQueryPointer(display, window, &dummy, &dummy,
    104		&x, &y, &di, &di, &dui);
    105
    106	for (i = 0; i < mcount; i++) {
    107		if (x >= info[i].x_org && y >= info[i].y_org
    108				&& x < info[i].x_org + info[i].width
    109				&& y < info[i].y_org + info[i].height) {
    110			screen = i;
    111			break;
    112		}
    113	}
    114
    115	return screen;
    116}
    117
    118static int
    119monitor_from_focussed(Display *display, XineramaScreenInfo *info, int mcount)
    120{
    121	Window w, root, *dws, dw, pw;
    122	XWindowAttributes wa;
    123	int di, area, maxarea, i;
    124	unsigned du;
    125	int screen;
    126
    127	screen = -1;
    128	root = XDefaultRootWindow(display);
    129
    130	/* check if a focussed window exists.. */
    131	XGetInputFocus(display, &w, &di);
    132
    133	/* modified snippet from dmenu-4.9 source */
    134	if (w != root && w != PointerRoot && w != None) {
    135		do {
    136			if (XQueryTree(display, (pw = w), &dw, &w, &dws, &du) && dws)
    137				XFree(dws);
    138		} while (w != root && w != pw);
    139
    140		/* find xinerama screen with largest screen intersection */
    141		if (XGetWindowAttributes(display, pw, &wa)) {
    142			for (i = 0; i < mcount; i++) {
    143				area = intersection_area(&info[i], &wa);
    144				if (area > maxarea) {
    145					maxarea = area;
    146					screen = i;
    147				}
    148			}
    149		}
    150	}
    151
    152	return screen;
    153}
    154
    155static void
    156set_prop(Display *display, Window window, const char *name,
    157	int type, size_t size, void *val, int count)
    158{
    159	int atom;
    160
    161	atom = XInternAtom(display, name, False);
    162	changeprop(display, window, atom, type, size,
    163			PropModeReplace, val, count);
    164}
    165
    166static void
    167set_props(Display *display, Window window)
    168{
    169	int wx, wy, mx, my;
    170	unsigned int ww, wh, mw, mh;
    171	const char *key, *val;
    172	unsigned int border, depth;
    173	XWMHints hints;
    174	size_t keylen;
    175	char **env;
    176	Window root;
    177	Atom atom;
    178	pid_t pid, ppid;
    179	uid_t uid;
    180
    181	uid = getuid();
    182	pid = getpid();
    183	ppid = getppid();
    184
    185	if (!XGetGeometry(display, window, &root,
    186			&wx, &wy, &ww, &wh, &border, &depth))
    187		err("Failed to get window geometry");
    188
    189	if (!XGetGeometry(display, root, &root,
    190			&mx, &my, &mw, &mh, &border, &depth))
    191		err("Failed to get screen geometry");
    192
    193	if (XineramaIsActive(display)) {
    194		XineramaScreenInfo *info;
    195		int mcount, mon;
    196
    197		if (!(info = XineramaQueryScreens(display, &mcount)))
    198			err("Failed to query xinerama");
    199
    200		mon = -1;
    201
    202		env = environ;
    203		while ((env = iter_args(env, &key, &keylen, &val))) {
    204			if (!strncmp("MON_NUM", key, keylen)) {
    205				mon = strtoul(val, NULL, 0);
    206				if (mon < 0 || mon >= mcount)
    207					err("Monitor number OOB");
    208			} else if (!strncmp("MON_PTR", key, keylen)) {
    209				mon = monitor_from_pointer(info,
    210					mcount, display, window);
    211			} else if (!strncmp("MON_FOCUS", key, keylen)) {
    212				mon = monitor_from_focussed(display,
    213					info, mcount);
    214			} else if (keylen >= 4 && !strncmp(key, "MON_", 4)) {
    215				err("Unsupported env var: WINPL_%.*s",
    216					(int) keylen, key);
    217			}
    218		}
    219
    220		if (mon == -1)
    221			mon = monitor_from_focussed(display, info, mcount);
    222
    223		if (mon == -1)
    224			mon = monitor_from_pointer(info, mcount,
    225				display, window);
    226
    227		if (mon == -1)
    228			err("Failed to get monitor");
    229
    230		mx = info[mon].x_org;
    231		my = info[mon].y_org;
    232		mw = info[mon].width;
    233		mh = info[mon].height;
    234	} else {
    235		env = environ;
    236		while ((env = iter_args(env, &key, &keylen, &val))) {
    237			if (keylen >= 4 && !strncmp(key, "MON_", 4)) {
    238				warn("Xinerama is not active");
    239				break;
    240			}
    241		}
    242	}
    243
    244	env = environ;
    245	while ((env = iter_args(env, &key, &keylen, &val))) {
    246		if (!strncmp(key, "WX", keylen)) {
    247			/* absolute window x */
    248			wx = strtoul(val, NULL, 0);
    249		} else if (!strncmp(key, "WY", keylen)) {
    250			/* absolute window y */
    251			wy = strtoul(val, NULL, 0);
    252		} else if (!strncmp(key, "RWX", keylen)) {
    253			/* window x relative to monitor size */
    254			wx = mw * strtof(val, NULL);
    255		} else if (!strncmp(key, "RWY", keylen)) {
    256			/* window y relative to monitor size */
    257			wy = mh * strtof(val, NULL);
    258		} else if (!strncmp(key, "MWX", keylen)) {
    259			/* window x from monitor top left */
    260			wx = mx + strtoul(val, NULL, 0);
    261		} else if (!strncmp(key, "MWY", keylen)) {
    262			/* window y from monitor top left */
    263			wy = my + strtoul(val, NULL, 0);
    264		} else if (!strncmp(key, "WW", keylen)) {
    265			/* window width */
    266			ww = strtoul(val, NULL, 0);
    267		} else if (!strncmp(key, "WH", keylen)) {
    268			/* window height */
    269			wh = strtoul(val, NULL, 0);
    270		} else if (!strncmp(key, "RWW", keylen)) {
    271			/* window width relative to monitor size */
    272			ww = mw * strtof(val, NULL);
    273		} else if (!strncmp(key, "RWH", keylen)) {
    274			/* window height relative to monitor size */
    275			wh = mh * strtof(val, NULL);
    276		} else if (!strncmp(key, "CENTER", keylen)) {
    277			/* window centered in monitor */
    278			wx = mx + (mw - ww) / 2.f;
    279			wy = my + (mh - wh) / 2.f;
    280		} else if (!strncmp(key, "FLOAT", keylen)) {
    281			/* window 'floating' in tiled WM */
    282			atom = XInternAtom(display,
    283				"_NET_WM_WINDOW_TYPE_DIALOG", False);
    284			set_prop(display, window, "_NET_WM_WINDOW_TYPE",
    285				XA_ATOM, 32, &atom, 1);
    286		} else if (!strncmp(key, "NAME", keylen)) {
    287			/* window name / title */
    288			hook_name = true;
    289			XTextProperty name;
    290			XStringListToTextProperty((char**)&val, 1, &name);
    291			setwmname(display, window, &name);
    292			set_prop(display, window, "_NET_WM_NAME",
    293				XInternAtom(display, "UTF8_STRING", False),
    294				8, (void*)val, strlen(val));
    295			set_prop(display, window, "WM_NAME",
    296				XA_STRING, 8, (void*)val, strlen(val));
    297		} else if (!strncmp(key, "NO_INPUT", keylen)) {
    298			/* never take input (focus) */
    299			hints.flags = InputHint;
    300			hints.input = false;
    301			XSetWMHints(display, window, &hints);
    302		} else if (keylen < 4 || strncmp(key, "MON_", 4)) {
    303			err("Unsupported env var: WINPL_%.*s",
    304				(int) keylen, key);
    305		}
    306	}
    307
    308	/* set basic properties */
    309	set_prop(display, window, "_NET_WM_UID", XA_CARDINAL, 32, &uid, 1);
    310	set_prop(display, window, "_NET_WM_PID", XA_CARDINAL, 32, &pid, 1);
    311	set_prop(display, window, "_NET_WM_PPID", XA_CARDINAL, 32, &ppid, 1);
    312
    313	/* update window pos and geometry */
    314	XMoveResizeWindow(display, window, wx, wy, ww, wh);
    315}
    316
    317Window
    318XCreateWindow(Display *display, Window parent, int x, int y,
    319	unsigned int width, unsigned int height, unsigned int border_width,
    320	int depth, unsigned int class, Visual *visual, unsigned long valuemask,
    321	XSetWindowAttributes *attributes)
    322{
    323	static Window (*func)(Display *display, Window parent, int x, int y,
    324		unsigned int width, unsigned int height,
    325		unsigned int border_width, int depth, unsigned int class,
    326		Visual *visual, unsigned long valuemask,
    327		XSetWindowAttributes *attributes) = NULL;
    328	Window window;
    329	int i;
    330
    331	if (!func) func = dlsym(lib_xlib, "XCreateWindow");
    332
    333	for (i = 0; i < ScreenCount(display); i++) {
    334		/* for toplevel windows */
    335		if (parent == RootWindow(display, i)) {
    336			window = (*func) (display, parent, x, y, width, height,
    337				border_width, depth, class, visual, valuemask, attributes);
    338			set_props(display, window);
    339			return window;
    340		}
    341	}
    342
    343	/* create window as usual */
    344	return (*func) (display, parent, x, y, width, height, border_width, depth,
    345			class, visual, valuemask, attributes);
    346}
    347
    348Window
    349XCreateSimpleWindow(Display *display, Window parent, int x, int y,
    350	unsigned int width, unsigned int height, unsigned int border_width,
    351	unsigned long border, unsigned long background)
    352{
    353	static Window (*func)(Display *display, Window parent, int x, int y,
    354		unsigned int width, unsigned int height,
    355		unsigned int border_width, unsigned long border,
    356		unsigned long background) = NULL;
    357	Window window;
    358	int i;
    359
    360	if (!func) func = dlsym(lib_xlib, "XCreateSimpleWindow");
    361
    362	for (i = 0; i < ScreenCount(display); i++) {
    363		/* for toplevel windows */
    364		if (parent == RootWindow(display, i)) {
    365			window = (*func) (display, parent, x, y, width, height, 
    366				border_width, border, background);
    367			set_props(display, window);
    368			return window;
    369		}
    370	}
    371
    372	/* create window as usual */
    373	return (*func) (display, parent, x, y, width, height,
    374		border_width, border, background);
    375}
    376
    377int
    378XReparentWindow(Display *display, Window window, Window parent, int x, int y)
    379{
    380	static int (*func)(Display *display, Window window, Window parent,
    381		int x, int y) = NULL;
    382	int i;
    383
    384	if (!func) func = dlsym(lib_xlib, "XReparentWindow");
    385
    386	for (i = 0; i < ScreenCount(display); i++) {
    387		/* for toplevel windows */
    388		if (parent == RootWindow(display, i)) {
    389			set_props(display, window);
    390			return (*func)(display, window, parent, x, y);
    391		}
    392	}
    393
    394	/* reparent as usual */
    395	return (*func) (display, window, parent, x, y);
    396}
    397
    398void
    399XSetWMName(Display *display, Window window, XTextProperty *text)
    400{
    401	if (!hook_name) setwmname(display, window, text);
    402}
    403
    404int
    405XChangeProperty(Display *display, Window window, Atom property, Atom type, int format, int mode, const unsigned char *data, int n)
    406{
    407	static Atom name_atom = 0;
    408	static Atom net_name_atom = 0;
    409
    410	if (!name_atom) name_atom = XInternAtom(display, "WM_NAME", False);
    411	if (!net_name_atom) net_name_atom = XInternAtom(display, "_NET_WM_NAME", False);
    412	if (hook_name && property == name_atom) return 0;
    413	if (hook_name && property == net_name_atom) return 0;
    414
    415	return changeprop(display, window, property, type, format, mode, data, n);
    416}
    417
    418void
    419winpl_init(void)
    420{
    421	if (!lib_xlib) lib_xlib = dlopen("libX11.so", RTLD_GLOBAL | RTLD_LAZY);
    422	if (!setwmname) setwmname = dlsym(lib_xlib, "XSetWMName");
    423	if (!changeprop) changeprop = dlsym(lib_xlib, "XChangeProperty");
    424}