sample x11 popup window

This commit is contained in:
2026-09-09 00:19:36 +02:00
parent aadc1fd5b9
commit 492893f1f1
+361
View File
@@ -0,0 +1,361 @@
#define _GNU_SOURCE
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/extensions/Xrandr.h>
#include <stdio.h>
#include <stdlib.h>
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;
}