summaryrefslogtreecommitdiff
path: root/lib/renderers/x11/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/renderers/x11/window.c')
-rw-r--r--lib/renderers/x11/window.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/lib/renderers/x11/window.c b/lib/renderers/x11/window.c
new file mode 100644
index 0000000..0b4c94a
--- /dev/null
+++ b/lib/renderers/x11/window.c
@@ -0,0 +1,224 @@
+#include "x11.h"
+
+#include <stdlib.h>
+#include <cairo-xlib.h>
+#include <X11/extensions/Xinerama.h>
+
+static void
+destroy_buffer(struct buffer *buffer)
+{
+ bm_cairo_destroy(&buffer->cairo);
+ memset(buffer, 0, sizeof(struct buffer));
+}
+
+static bool
+create_buffer(struct window *window, struct buffer *buffer, int32_t width, int32_t height)
+{
+ cairo_surface_t *surf;
+ if (!(surf = cairo_xlib_surface_create(window->display, window->drawable, DefaultVisual(window->display, window->screen), width, height)))
+ goto fail;
+
+ cairo_xlib_surface_set_size(surf, width, height);
+
+ if (!bm_cairo_create_for_surface(&buffer->cairo, surf)) {
+ cairo_surface_destroy(surf);
+ goto fail;
+ }
+
+ buffer->width = width;
+ buffer->height = height;
+ buffer->created = true;
+ return true;
+
+fail:
+ destroy_buffer(buffer);
+ return false;
+}
+
+static struct buffer*
+next_buffer(struct window *window)
+{
+ assert(window);
+
+ struct buffer *buffer = &window->buffer;
+
+ if (!buffer)
+ return NULL;
+
+ if (window->width != buffer->width || window->height != buffer->height)
+ destroy_buffer(buffer);
+
+ if (!buffer->created && !create_buffer(window, buffer, window->width, window->height))
+ return NULL;
+
+ return buffer;
+}
+
+void
+bm_x11_window_render(struct window *window, const struct bm_menu *menu)
+{
+ assert(window && menu);
+ uint32_t oldw = window->width, oldh = window->height;
+
+ struct buffer *buffer;
+ for (int32_t tries = 0; tries < 2; ++tries) {
+ if (!(buffer = next_buffer(window))) {
+ fprintf(stderr, "could not get next buffer");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!window->notify.render)
+ break;
+
+ cairo_push_group(buffer->cairo.cr);
+ struct cairo_paint_result result;
+ window->notify.render(&buffer->cairo, buffer->width, buffer->height, window->max_height, menu, &result);
+ window->displayed = result.displayed;
+ cairo_pop_group_to_source(buffer->cairo.cr);
+
+ if (window->height == result.height)
+ break;
+
+ window->height = result.height;
+ destroy_buffer(buffer);
+ }
+
+ if (oldw != window->width || oldh != window->height) {
+ if (window->bottom) {
+ XMoveResizeWindow(window->display, window->drawable, window->x, window->max_height - window->height, window->width, window->height);
+ } else {
+ XResizeWindow(window->display, window->drawable, window->width, window->height);
+ }
+ }
+
+ if (buffer->created) {
+ cairo_paint(buffer->cairo.cr);
+ cairo_surface_flush(buffer->cairo.surface);
+ }
+}
+
+void
+bm_x11_window_key_press(struct window *window, XKeyEvent *ev)
+{
+ KeySym keysym = NoSymbol;
+ XmbLookupString(window->xic, ev, NULL, 0, &keysym, NULL);
+ window->mods = 0;
+ if (ev->state & ControlMask) window->mods |= MOD_CTRL;
+ if (ev->state & ShiftMask) window->mods |= MOD_SHIFT;
+ window->keysym = keysym;
+}
+
+void
+bm_x11_window_destroy(struct window *window)
+{
+ assert(window);
+ destroy_buffer(&window->buffer);
+
+ if (window->display && window->drawable)
+ XDestroyWindow(window->display, window->drawable);
+}
+
+void
+bm_x11_window_set_monitor(struct window *window, uint32_t monitor)
+{
+ if (window->monitor == monitor)
+ return;
+
+ Window root = DefaultRootWindow(window->display);
+
+ {
+ /* xinerama logic straight from dmenu */
+#define INTERSECT(x,y,w,h,r) (fmax(0, fmin((x)+(w),(r).x_org+(r).width) - fmax((x),(r).x_org)) * fmax(0, fmin((y)+(h),(r).y_org+(r).height) - fmax((y),(r).y_org)))
+
+ int32_t n;
+ XineramaScreenInfo *info;
+ if ((info = XineramaQueryScreens(window->display, &n))) {
+ int32_t x, y, a, j, di, i = 0, area = 0;
+ uint32_t du;
+ Window w, pw, dw, *dws;
+ XWindowAttributes wa;
+
+ XGetInputFocus(window->display, &w, &di);
+ if (monitor > 0)
+ i = ((int32_t)monitor > n ? n : (int32_t)monitor) - 1;
+
+ if (monitor == 0 && w != root && w != PointerRoot && w != None) {
+ /* find top-level window containing current input focus */
+ do {
+ if (XQueryTree(window->display, (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(window->display, 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 (monitor == 0 && !area && XQueryPointer(window->display, root, &dw, &dw, &x, &y, &di, &di, &du)) {
+ for (i = 0; i < n; i++) {
+ if (INTERSECT(x, y, 1, 1, info[i]))
+ break;
+ }
+ }
+
+ window->x = info[i].x_org;
+ window->y = info[i].y_org + (window->bottom ? info[i].height - window->height : 0);
+ window->width = info[i].width;
+ window->max_height = info[i].height;
+ XFree(info);
+ } else {
+ window->max_height = DisplayHeight(window->display, window->screen);
+ window->x = 0;
+ window->y = (window->bottom ? window->max_height - window->height : 0);
+ window->width = DisplayWidth(window->display, window->screen);
+ }
+
+#undef INTERSECT
+ }
+
+ window->monitor = monitor;
+ XMoveResizeWindow(window->display, window->drawable, window->x, window->y, window->width, window->height);
+ XFlush(window->display);
+}
+
+void
+bm_x11_window_set_bottom(struct window *window, bool bottom)
+{
+ if (window->bottom == bottom)
+ return;
+
+ window->bottom = bottom;
+ bm_x11_window_set_monitor(window, window->monitor);
+}
+
+bool
+bm_x11_window_create(struct window *window, Display *display)
+{
+ assert(window);
+
+ window->display = display;
+ window->screen = DefaultScreen(display);
+ window->width = window->height = 1;
+ window->monitor = -1;
+
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+ .event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask
+ };
+
+ window->drawable = XCreateWindow(display, DefaultRootWindow(display), 0, 0, window->width, window->height, 0, DefaultDepth(display, window->screen), CopyFromParent, DefaultVisual(display, window->screen), CWOverrideRedirect | CWBackPixel | CWEventMask, &wa);
+
+ XSelectInput(display, window->drawable, ButtonPressMask | KeyPressMask);
+ XMapRaised(display, window->drawable);
+ window->xim = XOpenIM(display, NULL, NULL, NULL);
+ window->xic = XCreateIC(window->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window->drawable, XNFocusWindow, window->drawable, NULL);
+ return true;
+}
+
+/* vim: set ts=8 sw=4 tw=0 :*/