362 lines
7.2 KiB
C
362 lines
7.2 KiB
C
#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;
|
|
}
|