#define _GNU_SOURCE #include #include #include #include #include #include static int get_active_window(Display *dpy, Window root, Window *active) { Atom prop = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", True); if (!prop) return 0; Atom actual; int format; unsigned long nitems; unsigned long bytes_after; unsigned char *data = NULL; int status = XGetWindowProperty( dpy, root, prop, 0, 1, False, XA_WINDOW, &actual, &format, &nitems, &bytes_after, &data ); if (status != Success || !data || nitems == 0) { if (data) XFree(data); return 0; } *active = *(Window *)data; XFree(data); return 1; } static int get_window_center( Display *dpy, Window window, int *cx, int *cy ) { Window root; Window child; int x; int y; unsigned int width; unsigned int height; unsigned int border; unsigned int depth; if (!XGetGeometry( dpy, window, &root, &x, &y, &width, &height, &border, &depth)) return 0; int root_x; int root_y; if (!XTranslateCoordinates( dpy, window, root, 0, 0, &root_x, &root_y, &child)) return 0; *cx = root_x + (int)width / 2; *cy = root_y + (int)height / 2; return 1; } int main(void) { Display *dpy = XOpenDisplay(NULL); if (!dpy) { fprintf(stderr, "popup: cannot open X display\n"); return EXIT_FAILURE; } int screen = DefaultScreen(dpy); Window root = RootWindow(dpy, screen); /* * ------------------------------------------------------------ * Find the currently focused window. * ------------------------------------------------------------ */ Window active = None; int cursor_x = DisplayWidth(dpy, screen) / 2; int cursor_y = DisplayHeight(dpy, screen) / 2; if (get_active_window(dpy, root, &active)) { get_window_center( dpy, active, &cursor_x, &cursor_y ); } /* * ------------------------------------------------------------ * Find the monitor containing the active window. * ------------------------------------------------------------ */ int monitor_x = 0; int monitor_y = 0; int monitor_width = DisplayWidth(dpy, screen); int monitor_height = DisplayHeight(dpy, screen); int monitor_count = 0; XRRMonitorInfo *monitors = XRRGetMonitors( dpy, root, True, &monitor_count ); if (monitors) { for (int i = 0; i < monitor_count; i++) { XRRMonitorInfo *m = &monitors[i]; if (cursor_x >= m->x && cursor_x < m->x + (int)m->width && cursor_y >= m->y && cursor_y < m->y + (int)m->height) { monitor_x = m->x; monitor_y = m->y; monitor_width = m->width; monitor_height = m->height; break; } } XRRFreeMonitors(monitors); } /* * ------------------------------------------------------------ * Desired geometry. * * Half the monitor width and half the monitor height, * centered on that monitor. * ------------------------------------------------------------ */ int width = monitor_width / 2; int height = monitor_height / 2; int x = monitor_x + (monitor_width - width) / 2; int y = monitor_y + (monitor_height - height) / 2; /* * ------------------------------------------------------------ * Create the window. * * override_redirect means the window manager does not manage * this window. This prevents it from moving/resizing it. * ------------------------------------------------------------ */ XSetWindowAttributes attributes; attributes.override_redirect = True; attributes.background_pixel = BlackPixel(dpy, screen); attributes.event_mask = KeyPressMask | FocusChangeMask | StructureNotifyMask; Window window = XCreateWindow( dpy, root, x, y, width, height, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask, &attributes ); if (!window) { fprintf(stderr, "popup: XCreateWindow failed\n"); XCloseDisplay(dpy); return EXIT_FAILURE; } /* * ------------------------------------------------------------ * Map the window. * ------------------------------------------------------------ */ XMapRaised(dpy, window); XFlush(dpy); /* * ------------------------------------------------------------ * Wait until the X server has actually mapped the window. * * Calling XSetInputFocus() before this can result in: * * BadMatch (invalid parameter attributes) * * ------------------------------------------------------------ */ for (;;) { XEvent event; XNextEvent(dpy, &event); if (event.type == MapNotify && event.xmap.window == window) { break; } } /* * ------------------------------------------------------------ * Now that the window is viewable, take keyboard focus. * ------------------------------------------------------------ */ XSetInputFocus( dpy, window, RevertToParent, CurrentTime ); XRaiseWindow(dpy, window); XFlush(dpy); /* * ------------------------------------------------------------ * Event loop. * * Escape: * exit * * FocusOut: * exit * * The window is override-redirect, so the WM won't resize or * reposition it. * ------------------------------------------------------------ */ for (;;) { XEvent event; XNextEvent(dpy, &event); switch (event.type) { case KeyPress: { KeySym key = XLookupKeysym( &event.xkey, 0 ); if (key == XK_Escape) { XDestroyWindow( dpy, window ); XCloseDisplay(dpy); return EXIT_SUCCESS; } break; } case FocusOut: { /* * Any loss of keyboard focus causes the popup * to immediately terminate. */ XDestroyWindow( dpy, window ); XCloseDisplay(dpy); return EXIT_SUCCESS; } default: break; } } return EXIT_SUCCESS; }