127 lines
4.4 KiB
C
127 lines
4.4 KiB
C
/* 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 <hold-s> [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 <hold-s> seconds, unmap, exit.
|
|
* With "move", shift it +15px every second so
|
|
* the compositor repositions it (geometry).
|
|
* xormenu probe <display> -- 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 <X11/Xlib.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xutil.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#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 <hold-s> | probe <display>\n");
|
|
return 2;
|
|
}
|