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}