2023-05-23 22:14:24 +02:00
|
|
|
/* See LICENSE file for copyright and license details. */
|
2023-06-02 18:37:51 +02:00
|
|
|
void setupdisplay_x11(void) {
|
2023-05-21 23:23:43 +02:00
|
|
|
int x, y, i;
|
|
|
|
unsigned int du;
|
2023-05-21 23:52:28 +02:00
|
|
|
|
2023-05-21 23:23:43 +02:00
|
|
|
Window w, dw, *dws;
|
|
|
|
XWindowAttributes wa;
|
2023-05-21 23:52:28 +02:00
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
prepare_window_size_x11();
|
2023-05-21 23:23:43 +02:00
|
|
|
|
|
|
|
// resize client to image height if deemed necessary
|
|
|
|
#if USEIMAGE
|
2023-06-24 04:20:51 +02:00
|
|
|
if (image) resizetoimageheight(img.imageheight);
|
2023-05-21 23:23:43 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// set prompt width based on prompt size
|
2023-06-23 03:38:21 +02:00
|
|
|
sp.promptw = (prompt && *prompt)
|
|
|
|
? pango_prompt ? TEXTWM(prompt) : TEXTW(prompt) - sp.lrpad / 4 : 0; // prompt width
|
2023-05-21 23:23:43 +02:00
|
|
|
|
|
|
|
// init xinerama screens
|
|
|
|
#if USEXINERAMA
|
2023-06-02 18:37:51 +02:00
|
|
|
XineramaScreenInfo *info;
|
|
|
|
Window pw;
|
|
|
|
int a, n, area = 0;
|
|
|
|
int j, di;
|
|
|
|
|
2023-05-21 23:23:43 +02:00
|
|
|
i = 0;
|
|
|
|
if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
|
|
|
|
XGetInputFocus(dpy, &w, &di);
|
|
|
|
if (mon >= 0 && mon < n)
|
|
|
|
i = mon;
|
|
|
|
else if (w != root && w != PointerRoot && w != None) {
|
|
|
|
// find top-level window containing current input focus
|
|
|
|
do {
|
|
|
|
if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
|
|
|
|
XFree(dws);
|
|
|
|
} while (w != root && w != pw);
|
|
|
|
// find xinerama screen with which the window intersects most
|
|
|
|
if (XGetWindowAttributes(dpy, pw, &wa))
|
|
|
|
for (j = 0; j < n; j++)
|
|
|
|
if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
|
|
|
|
area = a;
|
|
|
|
i = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// no focused window is on screen, so use pointer location instead
|
|
|
|
if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
if (INTERSECT(x, y, 1, 1, info[i]))
|
|
|
|
break;
|
|
|
|
|
2023-06-24 17:13:17 +02:00
|
|
|
mo.output_width = info[i].width;
|
|
|
|
mo.output_height = info[i].height;
|
2023-05-21 23:23:43 +02:00
|
|
|
|
|
|
|
XFree(info);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if (!XGetWindowAttributes(dpy, parentwin, &wa))
|
|
|
|
die("spmenu: could not get embedding window attributes: 0x%lx",
|
|
|
|
parentwin); // die because unable to get attributes for the parent window
|
|
|
|
|
2023-06-24 17:13:17 +02:00
|
|
|
mo.output_width = wa.width;
|
|
|
|
mo.output_height = wa.height;
|
|
|
|
}
|
|
|
|
|
2023-07-14 01:16:35 +02:00
|
|
|
get_mh();
|
|
|
|
|
2023-06-24 17:13:17 +02:00
|
|
|
if (menuposition == 2) { // centered
|
2023-07-14 01:54:48 +02:00
|
|
|
sp.mw = MIN(MAX(max_textw() + sp.promptw, centerwidth), mo.output_width);
|
2023-06-24 17:13:17 +02:00
|
|
|
x = (mo.output_width - sp.mw) / 2 + xpos;
|
|
|
|
y = (mo.output_height - sp.mh) / 2 - ypos;
|
|
|
|
} else { // top or bottom
|
|
|
|
x = 0;
|
2023-07-06 04:02:23 +02:00
|
|
|
y = menuposition ? 0 : mo.output_height - sp.mh - ypos;
|
2023-06-24 17:13:17 +02:00
|
|
|
sp.mw = (menuwidth > 0 ? menuwidth : mo.output_width);
|
2023-05-21 23:23:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// create menu window and set properties for it
|
2023-06-02 18:37:51 +02:00
|
|
|
create_window_x11(
|
2023-06-23 03:38:21 +02:00
|
|
|
x + sp.sp,
|
|
|
|
y + sp.vp - (menuposition == 1 ? 0 : menuposition == 2 ? borderwidth : borderwidth * 2),
|
|
|
|
sp.mw - 2 * sp.sp - borderwidth * 2,
|
|
|
|
sp.mh
|
2023-05-21 23:44:02 +02:00
|
|
|
);
|
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
set_window_x11();
|
|
|
|
set_prop_x11();
|
2023-05-21 23:23:43 +02:00
|
|
|
|
|
|
|
#if USEIMAGE
|
|
|
|
setimageopts();
|
|
|
|
#endif
|
|
|
|
|
2023-05-21 23:40:19 +02:00
|
|
|
open_xim(); // open xim
|
2023-05-21 23:23:43 +02:00
|
|
|
|
|
|
|
XMapRaised(dpy, win);
|
|
|
|
XSync(dpy, False);
|
|
|
|
XGetWindowAttributes(dpy, win, &wa);
|
|
|
|
|
2023-05-21 23:44:02 +02:00
|
|
|
if (wa.map_state == IsViewable) { // must be viewable, otherwise we get a BadMatch error
|
2023-05-21 23:23:43 +02:00
|
|
|
XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
|
2023-05-21 23:44:02 +02:00
|
|
|
}
|
2023-05-21 23:23:43 +02:00
|
|
|
|
|
|
|
// embed spmenu inside parent window
|
2023-06-23 03:38:21 +02:00
|
|
|
if (x11.embed) {
|
2023-05-21 23:23:43 +02:00
|
|
|
XReparentWindow(dpy, win, parentwin, x, y);
|
|
|
|
XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
|
2023-05-21 23:44:02 +02:00
|
|
|
|
2023-05-21 23:23:43 +02:00
|
|
|
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
|
|
|
|
for (i = 0; i < du && dws[i] != win; ++i)
|
|
|
|
XSelectInput(dpy, dws[i], FocusChangeMask);
|
|
|
|
XFree(dws);
|
|
|
|
}
|
2023-05-21 23:44:02 +02:00
|
|
|
|
2023-08-07 06:23:56 +02:00
|
|
|
grabfocus_x11();
|
2023-05-21 23:23:43 +02:00
|
|
|
}
|
|
|
|
|
2023-05-21 23:44:02 +02:00
|
|
|
// resize window and draw
|
2023-06-23 03:49:23 +02:00
|
|
|
draw_resize(draw, sp.mw - 2 * sp.sp - borderwidth * 2, sp.mh);
|
2023-05-21 23:44:02 +02:00
|
|
|
|
|
|
|
match();
|
2023-05-21 23:23:43 +02:00
|
|
|
drawmenu();
|
|
|
|
}
|
2023-05-23 22:31:33 +02:00
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
void prepare_window_size_x11(void) {
|
2023-06-23 03:38:21 +02:00
|
|
|
sp.sp = menupaddingh;
|
|
|
|
sp.vp = (menuposition == 1) ? menupaddingv : - menupaddingv;
|
2023-06-02 18:37:51 +02:00
|
|
|
|
2023-06-23 03:49:23 +02:00
|
|
|
sp.bh = MAX(draw->font->h, draw->font->h + 2 + lineheight);
|
2023-06-02 18:37:51 +02:00
|
|
|
lines = MAX(lines, 0);
|
2023-06-23 03:38:21 +02:00
|
|
|
#if USEIMAGE
|
|
|
|
img.setlines = lines;
|
|
|
|
#endif
|
2023-06-02 18:37:51 +02:00
|
|
|
|
2023-06-23 03:49:23 +02:00
|
|
|
sp.lrpad = draw->font->h + textpadding;
|
2023-06-11 16:46:36 +02:00
|
|
|
get_mh();
|
2023-06-02 18:37:51 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Display * opendisplay_x11(char *disp) {
|
2023-05-23 22:31:33 +02:00
|
|
|
return XOpenDisplay(disp);
|
|
|
|
}
|
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
void set_screen_x11(Display *disp) {
|
2023-06-23 03:38:21 +02:00
|
|
|
x11.screen = DefaultScreen(disp);
|
|
|
|
root = RootWindow(disp, x11.screen);
|
2023-05-23 22:31:33 +02:00
|
|
|
}
|
2023-05-23 22:40:17 +02:00
|
|
|
|
|
|
|
void handle_x11(void) {
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
|
|
|
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
|
|
|
|
fputs("warning: no locale support\n", stderr); // invalid locale, so notify the user about it
|
|
|
|
|
|
|
|
if (!XSetLocaleModifiers(""))
|
|
|
|
fputs("warning: no locale modifiers support\n", stderr);
|
|
|
|
|
2023-06-02 18:37:51 +02:00
|
|
|
if (!(dpy = opendisplay_x11(NULL)))
|
2023-05-23 22:40:17 +02:00
|
|
|
die("spmenu: cannot open display"); // failed to open display
|
|
|
|
|
|
|
|
// set screen and root window
|
2023-06-02 18:37:51 +02:00
|
|
|
set_screen_x11(dpy);
|
2023-05-23 22:40:17 +02:00
|
|
|
|
|
|
|
// parent window is the root window (ie. window manager) because we're not embedding
|
2023-06-23 03:38:21 +02:00
|
|
|
if (!x11.embed || !(parentwin = strtol(x11.embed, NULL, 0)))
|
2023-05-23 22:40:17 +02:00
|
|
|
parentwin = root;
|
|
|
|
|
|
|
|
if (!XGetWindowAttributes(dpy, parentwin, &wa)) {
|
|
|
|
die("spmenu: could not get embedding window attributes: 0x%lx", parentwin);
|
|
|
|
}
|
|
|
|
|
|
|
|
xinitvisual(); // init visual and create drawable after
|
2023-06-23 03:49:23 +02:00
|
|
|
draw = draw_create_x11(dpy, x11.screen, root, wa.width, wa.height, x11.visual, x11.depth, x11.cmap, protocol);
|
2023-05-23 22:40:17 +02:00
|
|
|
}
|
2023-05-24 07:36:40 +02:00
|
|
|
|
|
|
|
void cleanup_x11(Display *disp) {
|
|
|
|
XUngrabKey(disp, AnyKey, AnyModifier, root);
|
|
|
|
XSync(disp, False);
|
|
|
|
XCloseDisplay(disp);
|
|
|
|
}
|