/* xormenu: override-redirect focus-stealing menu probe. * * Exercises the compositor's X11 override-redirect focus handoff: a managed * frame window (its own tab) plus a separate override-redirect popup with * input hints, so mapping the popup makes the compositor stash the frame's * tab as its focus owner. Closing the frame's tab while the popup is open * and then dismissing the popup must drop keyboard focus, not activate * freed memory (regression test for the or_focus_owner use-after-free). * * usage: * xormenu frame -- map a managed 400x300 frame, pump events * xormenu menu [move] * -- map an OR input-hint popup, paint it * continuously (XWM only maps the wl surface * once the X window has buffer damage), keep * it up seconds, unmap, exit. * With "move", shift it +15px every second so * the compositor repositions it (geometry). * xormenu probe -- exit 0 if an X display opens, 1 otherwise * (used to discover the compositor's lazy * Xwayland display) * * The frame and the menu are separate processes so the driver can kill the * frame (freeing the owner tab) while the menu stays mapped. */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #define MENU_W 300 #define MENU_H 200 static void set_pid(Display *d, Window w) { pid_t pid = getpid(); Atom a = XInternAtom(d, "_NET_WM_PID", False); XChangeProperty(d, w, a, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); } static int frame_main(void) { Display *d = XOpenDisplay(NULL); if (!d) { fprintf(stderr, "xormenu: frame: cannot open display\n"); return 1; } int scr = DefaultScreen(d); Window f = XCreateSimpleWindow(d, RootWindow(d, scr), 0, 0, 400, 300, 0, BlackPixel(d, scr), 0x404080UL); XStoreName(d, f, "xormenu-frame"); set_pid(d, f); XMapWindow(d, f); XFlush(d); for (;;) { XEvent ev; XNextEvent(d, &ev); } return 0; } static int menu_main(int hold, bool move) { Display *d = XOpenDisplay(NULL); if (!d) { fprintf(stderr, "xormenu: menu: cannot open display\n"); return 1; } int scr = DefaultScreen(d); XSetWindowAttributes attr = { .override_redirect = True }; Window m = XCreateWindow(d, RootWindow(d, scr), 100, 100, MENU_W, MENU_H, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect, &attr); XStoreName(d, m, "xormenu-menu"); set_pid(d, m); XWMHints *h = XAllocWMHints(); h->flags = InputHint; h->input = True; XSetWMHints(d, m, h); XFree(h); XFlush(d); /* Let the X server manager see the window before mapping: a back-to-back create+map can deliver MapNotify before management, losing the map. */ sleep(2); XMapWindow(d, m); XFlush(d); /* Keep it painted: no buffer damage means no wl-surface map. */ GC gc = XCreateGC(d, m, 0, NULL); XSetForeground(d, gc, 0x00C000UL); time_t t0 = time(NULL); struct timespec tick = { 0, 300 * 1000000L }; int step = 0; while (time(NULL) - t0 < hold) { XFillRectangle(d, m, gc, 0, 0, MENU_W, MENU_H); if (move && ++step % 3 == 0) { /* Nudge the popup so the compositor repositions it. */ XMoveWindow(d, m, 100 + step * 5, 100 + step * 5); } XFlush(d); nanosleep(&tick, NULL); } XUnmapWindow(d, m); XFlush(d); sleep(1); return 0; } int main(int argc, char **argv) { if (argc > 1 && strcmp(argv[1], "frame") == 0) return frame_main(); if (argc > 1 && strcmp(argv[1], "menu") == 0) return menu_main(argc > 2 ? atoi(argv[2]) : 8, argc > 3 && strcmp(argv[3], "move") == 0); if (argc > 1 && strcmp(argv[1], "probe") == 0 && argc > 2) { Display *d = XOpenDisplay(argv[2]); if (!d) return 1; XCloseDisplay(d); return 0; } fprintf(stderr, "usage: xormenu frame | menu | probe \n"); return 2; }