tgraphics_xlib.c - ltk - Socket-based GUI for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tgraphics_xlib.c (19307B)
---
1 /*
2 * Copyright (c) 2022 lumidify <nobody@lumidify.org>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <stdint.h>
19
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <X11/extensions/Xdbe.h>
23 #include <X11/XKBlib.h>
24 #include <X11/extensions/XKBrules.h>
25
26 #include "color.h"
27 #include "rect.h"
28 #include "util.h"
29 #include "memory.h"
30 #include "compat.h"
31 #include "xlib_shared.h"
32
33 struct ltk_surface {
34 int w, h;
35 ltk_renderdata *renderdata;
36 Drawable d;
37 #if USE_XFT == 1
38 XftDraw *xftdraw;
39 #endif
40 char resizable;
41 };
42
43 ltk_surface *
44 ltk_surface_create(ltk_renderdata *renderdata, int w, int h) {
45 ltk_surface *s = ltk_malloc(sizeof(ltk_surface));
46 if (w <= 0)
47 w = 1;
48 if (h <= 0)
49 h = 1;
50 s->w = w;
51 s->h = h;
52 s->renderdata = renderdata;
53 s->d = XCreatePixmap(renderdata->dpy, renderdata->xwindow, w, h, renderdata->depth);
54 #if USE_XFT == 1
55 s->xftdraw = XftDrawCreate(renderdata->dpy, s->d, renderdata->vis, renderdata->cm);
56 #endif
57 s->resizable = 1;
58 return s;
59 }
60
61 ltk_surface *
62 ltk_surface_from_window(ltk_renderdata *renderdata, int w, int h) {
63 ltk_surface *s = ltk_malloc(sizeof(ltk_surface));
64 s->w = w;
65 s->h = h;
66 s->renderdata = renderdata;
67 s->d = renderdata->drawable;
68 #if USE_XFT == 1
69 s->xftdraw = XftDrawCreate(renderdata->dpy, s->d, renderdata->vis, renderdata->cm);
70 #endif
71 s->resizable = 0;
72 return s;
73 }
74
75 void
76 ltk_surface_destroy(ltk_surface *s) {
77 #if USE_XFT == 1
78 XftDrawDestroy(s->xftdraw);
79 #endif
80 if (s->resizable)
81 XFreePixmap(s->renderdata->dpy, (Pixmap)s->d);
82 ltk_free(s);
83 }
84
85 void
86 ltk_surface_update_size(ltk_surface *s, int w, int h) {
87 /* FIXME: maybe return directly if surface is resizable? */
88 s->w = w;
89 s->h = h;
90 }
91
92 int
93 ltk_surface_resize(ltk_surface *s, int w, int h) {
94 if (!s->resizable)
95 return 1;
96 s->w = w;
97 s->h = h;
98 XFreePixmap(s->renderdata->dpy, (Pixmap)s->d);
99 s->d = XCreatePixmap(s->renderdata->dpy, s->renderdata->xwindow, w, h, s->renderdata->depth);
100 #if USE_XFT == 1
101 XftDrawChange(s->xftdraw, s->d);
102 #endif
103 return 0;
104 }
105
106 void
107 ltk_surface_get_size(ltk_surface *s, int *w, int *h) {
108 *w = s->w;
109 *h = s->h;
110 }
111
112 void
113 ltk_surface_draw_rect(ltk_surface *s, ltk_color *c, ltk_rect rect, int line_width) {
114 XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
115 XSetLineAttributes(s->renderdata->dpy, s->renderdata->gc, line_width, LineSolid, CapButt, JoinMiter);
116 XDrawRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y, rect.w, rect.h);
117 }
118
119 void
120 ltk_surface_draw_border(ltk_surface *s, ltk_color *c, ltk_rect rect, int line_width, ltk_border_sides border_sides) {
121 /* drawn as rectangles to have proper control over line width - I'm not sure how exactly
122 XDrawLine handles even line widths (i.e. on which side the extra pixel will be) */
123 XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
124 if (border_sides & LTK_BORDER_TOP)
125 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y, rect.w, line_width);
126 if (border_sides & LTK_BORDER_BOTTOM)
127 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y + rect.h - line_width, rect.w, line_width);
128 if (border_sides & LTK_BORDER_LEFT)
129 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y, line_width, rect.h);
130 if (border_sides & LTK_BORDER_RIGHT)
131 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x + rect.w - line_width, rect.y, line_width, rect.h);
132 }
133
134 void
135 ltk_surface_draw_border_clipped(ltk_surface *s, ltk_color *c, ltk_rect rect, ltk_rect clip_rect, int line_width, ltk_border_sides border_sides) {
136 if (line_width <= 0)
137 return;
138 XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
139 int width;
140 ltk_rect final_rect = ltk_rect_intersect(rect, clip_rect);
141 if (border_sides & LTK_BORDER_TOP) {
142 width = rect.y - final_rect.y;
143 if (width > -line_width) {
144 width = line_width + width;
145 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, final_rect.x, final_rect.y, final_rect.w, width);
146 }
147 }
148 if (border_sides & LTK_BORDER_BOTTOM) {
149 width = (final_rect.y + final_rect.h) - (rect.y + rect.h);
150 if (width > -line_width) {
151 width = line_width + width;
152 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, final_rect.x, final_rect.y + final_rect.h - width, final_rect.w, width);
153 }
154 }
155 if (border_sides & LTK_BORDER_LEFT) {
156 width = rect.x - final_rect.x;
157 if (width > -line_width) {
158 width = line_width + width;
159 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, final_rect.x, final_rect.y, width, final_rect.h);
160 }
161 }
162 if (border_sides & LTK_BORDER_RIGHT) {
163 width = (final_rect.x + final_rect.w) - (rect.x + rect.w);
164 if (width > -line_width) {
165 width = line_width + width;
166 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, final_rect.x + final_rect.w - width, final_rect.y, width, final_rect.h);
167 }
168 }
169 }
170
171 void
172 ltk_surface_fill_rect(ltk_surface *s, ltk_color *c, ltk_rect rect) {
173 XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
174 XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y, rect.w, rect.h);
175 }
176
177 void
178 ltk_surface_fill_polygon(ltk_surface *s, ltk_color *c, ltk_point *points, size_t npoints) {
179 /* FIXME: maybe make this static since this won't be threaded anyways? */
180 XPoint tmp_points[6]; /* to avoid extra allocations when not necessary */
181 /* FIXME: this is ugly and inefficient */
182 XPoint *final_points;
183 if (npoints <= 6) {
184 final_points = tmp_points;
185 } else {
186 final_points = ltk_reallocarray(NULL, npoints, sizeof(XPoint));
187 }
188 /* FIXME: how to deal with ints that don't fit in short? */
189 for (size_t i = 0; i < npoints; i++) {
190 final_points[i].x = (short)points[i].x;
191 final_points[i].y = (short)points[i].y;
192 }
193 XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
194 XFillPolygon(s->renderdata->dpy, s->d, s->renderdata->gc, final_points, (int)npoints, Complex, CoordModeOrigin);
195 if (npoints > 6)
196 ltk_free(final_points);
197 }
198
199 static inline void
200 swap_ptr(void **ptr1, void **ptr2) {
201 void *tmp = *ptr1;
202 *ptr1 = *ptr2;
203 *ptr2 = tmp;
204 }
205
206 #define check_size(cond) if (!(cond)) ltk_fatal("Unable to perform polygon clipping. This is a bug, tell lumidify about it.\n")
207
208 /* FIXME: this can probably be optimized */
209 /* This is basically Sutherland-Hodgman, but only the special case for clipping rectangles. */
210 void
211 ltk_surface_fill_polygon_clipped(ltk_surface *s, ltk_color *c, ltk_point *points, size_t npoints, ltk_rect clip) {
212 /* FIXME: is this even more efficient? */
213 XPoint tmp_points1[12]; /* to avoid extra allocations when not necessary */
214 XPoint tmp_points2[12];
215 XPoint *points1;
216 XPoint *points2;
217 /* FIXME: be a bit smarter about this */
218 if (npoints <= 6) {
219 points1 = tmp_points1;
220 points2 = tmp_points2;
221 } else {
222 /* FIXME: I'm pretty sure there can never be more points than this
223 since we're only clipping against a rectangle, right?
224 If I can be sure about that, I can remove all the check_size's below. */
225 points1 = ltk_reallocarray(NULL, npoints, sizeof(XPoint) * 2);
226 points2 = ltk_reallocarray(NULL, npoints, sizeof(XPoint) * 2);
227 }
228
229 size_t num1 = npoints;
230 size_t num2 = 0;
231 for (size_t i = 0; i < npoints; i++) {
232 points1[i].x = (short)points[i].x;
233 points1[i].y = (short)points[i].y;
234 }
235
236 for (size_t i = 0; i < num1; i++) {
237 XPoint p1 = points1[i];
238 XPoint p2 = points1[(i + 1) % num1];
239 if (p1.x >= clip.x) {
240 check_size(num2 < npoints * 2);
241 points2[num2++] = p1;
242 if (p2.x < clip.x) {
243 check_size(num2 < npoints * 2);
244 points2[num2++] = (XPoint){.x = (short)clip.x, .y = (short)(p1.y + (p2.y - p1.y) * (float)(clip.x - p1.x) / (p2.x - p1.x))};
245 }
246 } else if (p2.x >= clip.x) {
247 check_size(num2 < npoints * 2);
248 points2[num2++] = (XPoint){.x = (short)clip.x, .y = (short)(p1.y + (p2.y - p1.y) * (float)(clip.x - p1.x) / (p2.x - p1.x))};
249 }
250 }
251 num1 = num2;
252 num2 = 0;
253 swap_ptr((void**)&points1, (void**)&points2);
254
255 for (size_t i = 0; i < num1; i++) {
256 XPoint p1 = points1[i];
257 XPoint p2 = points1[(i + 1) % num1];
258 if (p1.x <= clip.x + clip.w) {
259 check_size(num2 < npoints * 2);
260 points2[num2++] = p1;
261 if (p2.x > clip.x + clip.w) {
262 check_size(num2 < npoints * 2);
263 points2[num2++] = (XPoint){.x = (short)(clip.x + clip.w), .y = (short)(p1.y + (p2.y - p1.y) * (float)(clip.x + clip.w - p1.x) / (p2.x - p1.x))};
264 }
265 } else if (p2.x <= clip.x + clip.w) {
266 check_size(num2 < npoints * 2);
267 points2[num2++] = (XPoint){.x = (short)(clip.x + clip.w), .y = (short)(p1.y + (p2.y - p1.y) * (float)(clip.x + clip.w - p1.x) / (p2.x - p1.x))};
268 }
269 }
270 num1 = num2;
271 num2 = 0;
272 swap_ptr((void**)&points1, (void**)&points2);
273
274 for (size_t i = 0; i < num1; i++) {
275 XPoint p1 = points1[i];
276 XPoint p2 = points1[(i + 1) % num1];
277 if (p1.y >= clip.y) {
278 check_size(num2 < npoints * 2);
279 points2[num2++] = p1;
280 if (p2.y < clip.y) {
281 check_size(num2 < npoints * 2);
282 points2[num2++] = (XPoint){.y = (short)clip.y, .x = (short)(p1.x + (p2.x - p1.x) * (float)(clip.y - p1.y) / (p2.y - p1.y))};
283 }
284 } else if (p2.y >= clip.y) {
285 check_size(num2 < npoints * 2);
286 points2[num2++] = (XPoint){.y = (short)clip.y, .x = (short)(p1.x + (p2.x - p1.x) * (float)(clip.y - p1.y) / (p2.y - p1.y))};
287 }
288 }
289 num1 = num2;
290 num2 = 0;
291 swap_ptr((void**)&points1, (void**)&points2);
292
293 for (size_t i = 0; i < num1; i++) {
294 XPoint p1 = points1[i];
295 XPoint p2 = points1[(i + 1) % num1];
296 if (p1.y <= clip.y + clip.h) {
297 check_size(num2 < npoints * 2);
298 points2[num2++] = p1;
299 if (p2.y > clip.y + clip.h) {
300 check_size(num2 < npoints * 2);
301 points2[num2++] = (XPoint){.y = (short)clip.y + clip.h, .x = (short)(p1.x + (p2.x - p1.x) * (float)(clip.y + clip.h - p1.y) / (p2.y - p1.y))};
302 }
303 } else if (p2.y <= clip.y + clip.h) {
304 check_size(num2 < npoints * 2);
305 points2[num2++] = (XPoint){.y = (short)clip.y + clip.h, .x = (short)(p1.x + (p2.x - p1.x) * (float)(clip.y + clip.h - p1.y) / (p2.y - p1.y))};
306 }
307 }
308
309 if (num2 > 0) {
310 XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
311 XFillPolygon(s->renderdata->dpy, s->d, s->renderdata->gc, points2, (int)num2, Complex, CoordModeOrigin);
312 }
313 if (npoints > 6) {
314 ltk_free(points1);
315 ltk_free(points2);
316 }
317 }
318
319 void
320 ltk_surface_copy(ltk_surface *src, ltk_surface *dst, ltk_rect src_rect, int dst_x, int dst_y) {
321 XCopyArea(
322 src->renderdata->dpy, src->d, dst->d, src->renderdata->gc,
323 src_rect.x, src_rect.y, src_rect.w, src_rect.h, dst_x, dst_y
324 );
325 }
326
327 /* TODO */
328 /*
329 void
330 ltk_surface_draw_arc(ltk_surface *s, ltk_color *c, int x, int y, int w, int h, int angle1, int angle2, int line_width) {
331 }
332
333 void
334 ltk_surface_fill_arc(ltk_surface *s, ltk_color *c, int x, int y, int w, int h, int angle1, int angle2) {
335 }
336
337 void
338 ltk_surface_draw_circle(ltk_surface *s, ltk_color *c, int xc, int yc, int r, int line_width) {
339 }
340
341 void
342 ltk_surface_fill_circle(ltk_surface *s, ltk_color *c, int xc, int yc, int r) {
343 }
344 */
345
346 #if USE_XFT == 1
347 XftDraw *
348 ltk_surface_get_xft_draw(ltk_surface *s) {
349 return s->xftdraw;
350 }
351 #endif
352
353 Drawable
354 ltk_surface_get_drawable(ltk_surface *s) {
355 return s->d;
356 }
357
358 /* FIXME: move this to a file where it makes more sense */
359 /* blatantly stolen from st */
360 static void ximinstantiate(Display *dpy, XPointer client, XPointer call);
361 static void ximdestroy(XIM xim, XPointer client, XPointer call);
362 static int xicdestroy(XIC xim, XPointer client, XPointer call);
363 static int ximopen(ltk_renderdata *renderdata, Display *dpy);
364
365 static void
366 ximdestroy(XIM xim, XPointer client, XPointer call) {
367 (void)xim;
368 (void)call;
369 ltk_renderdata *renderdata = (ltk_renderdata *)client;
370 renderdata->xim = NULL;
371 XRegisterIMInstantiateCallback(
372 renderdata->dpy, NULL, NULL, NULL, ximinstantiate, (XPointer)renderdata
373 );
374 XFree(renderdata->spotlist);
375 }
376
377 static int
378 xicdestroy(XIC xim, XPointer client, XPointer call) {
379 (void)xim;
380 (void)call;
381 ltk_renderdata *renderdata = (ltk_renderdata *)client;
382 renderdata->xic = NULL;
383 return 1;
384 }
385
386 static int
387 ximopen(ltk_renderdata *renderdata, Display *dpy) {
388 XIMCallback imdestroy = { .client_data = (XPointer)renderdata, .callback = ximdestroy };
389 XICCallback icdestroy = { .client_data = (XPointer)renderdata, .callback = xicdestroy };
390
391 renderdata->xim = XOpenIM(dpy, NULL, NULL, NULL);
392 if (renderdata->xim == NULL)
393 return 0;
394
395 if (XSetIMValues(renderdata->xim, XNDestroyCallback, &imdestroy, NULL))
396 ltk_warn("XSetIMValues: Could not set XNDestroyCallback.\n");
397
398 renderdata->spotlist = XVaCreateNestedList(0, XNSpotLocation, &renderdata->spot, NULL);
399
400 if (renderdata->xic == NULL) {
401 renderdata->xic = XCreateIC(
402 renderdata->xim, XNInputStyle,
403 XIMPreeditNothing | XIMStatusNothing,
404 XNClientWindow, renderdata->xwindow,
405 XNDestroyCallback, &icdestroy, NULL
406 );
407 }
408 if (renderdata->xic == NULL)
409 ltk_warn("XCreateIC: Could not create input context.\n");
410
411 return 1;
412 }
413
414 static void
415 ximinstantiate(Display *dpy, XPointer client, XPointer call) {
416 (void)call;
417 ltk_renderdata *renderdata = (ltk_renderdata *)client;
418 if (ximopen(renderdata, dpy)) {
419 XUnregisterIMInstantiateCallback(
420 dpy, NULL, NULL, NULL, ximinstantiate, (XPointer)renderdata
421 );
422 }
423 }
424
425 void
426 renderer_set_imspot(ltk_renderdata *renderdata, int x, int y) {
427 if (renderdata->xic == NULL)
428 return;
429 /* FIXME! */
430 renderdata->spot.x = x;
431 renderdata->spot.y = y;
432 XSetICValues(renderdata->xic, XNPreeditAttributes, renderdata->spotlist, NULL);
433 }
434
435 ltk_renderdata *
436 renderer_create_window(const char *title, int x, int y, unsigned int w, unsigned int h) {
437 XSetWindowAttributes attrs;
438 ltk_renderdata *renderdata = ltk_malloc(sizeof(ltk_renderdata));
439
440 renderdata->dpy = XOpenDisplay(NULL);
441 renderdata->screen = DefaultScreen(renderdata->dpy);
442 /* based on http://wili.cc/blog/xdbe.html */
443 int major, minor, found = 0;
444 if (XdbeQueryExtension(renderdata->dpy, &major, &minor)) {
445 int num_screens = 1;
446 Drawable screens[] = {DefaultRootWindow(renderdata->dpy)};
447 XdbeScreenVisualInfo *info = XdbeGetVisualInfo(
448 renderdata->dpy, screens, &num_screens
449 );
450 if (!info || num_screens < 1 || info->count < 1) {
451 ltk_fatal("No visuals support Xdbe.");
452 }
453 XVisualInfo xvisinfo_templ;
454 /* we know there's at least one */
455 xvisinfo_templ.visualid = info->visinfo[0].visual;
456 /* FIXME: proper screen number? */
457 xvisinfo_templ.screen = 0;
458 xvisinfo_templ.depth = info->visinfo[0].depth;
459 int matches;
460 XVisualInfo *xvisinfo_match = XGetVisualInfo(
461 renderdata->dpy,
462 VisualIDMask | VisualScreenMask | VisualDepthMask,
463 &xvisinfo_templ, &matches
464 );
465 if (!xvisinfo_match || matches < 1) {
466 ltk_fatal("Couldn't match a Visual with double buffering.\n");
467 }
468 renderdata->vis = xvisinfo_match->visual;
469 /* FIXME: is it legal to free this while keeping the visual? */
470 XFree(xvisinfo_match);
471 XdbeFreeVisualInfo(info);
472 found = 1;
473 } else {
474 renderdata->vis = DefaultVisual(renderdata->dpy, renderdata->screen);
475 ltk_warn("No Xdbe support.\n");
476 }
477 renderdata->cm = DefaultColormap(renderdata->dpy, renderdata->screen);
478 renderdata->wm_delete_msg = XInternAtom(renderdata->dpy, "WM_DELETE_WINDOW", False);
479
480 memset(&attrs, 0, sizeof(attrs));
481 attrs.background_pixel = BlackPixel(renderdata->dpy, renderdata->screen);
482 attrs.colormap = renderdata->cm;
483 attrs.border_pixel = WhitePixel(renderdata->dpy, renderdata->screen);
484 /* this causes the window contents to be kept
485 * when it is resized, leading to less flicker */
486 attrs.bit_gravity = NorthWestGravity;
487 attrs.event_mask =
488 ExposureMask | KeyPressMask | KeyReleaseMask |
489 ButtonPressMask | ButtonReleaseMask |
490 StructureNotifyMask | PointerMotionMask;
491 renderdata->depth = DefaultDepth(renderdata->dpy, renderdata->screen);
492 /* FIXME: set border width */
493 renderdata->xwindow = XCreateWindow(
494 renderdata->dpy, DefaultRootWindow(renderdata->dpy), x, y,
495 w, h, 0, renderdata->depth,
496 InputOutput, renderdata->vis,
497 CWBackPixel | CWColormap | CWBitGravity | CWEventMask | CWBorderPixel, &attrs
498 );
499
500 if (found) {
501 renderdata->back_buf = XdbeAllocateBackBufferName(
502 renderdata->dpy, renderdata->xwindow, XdbeBackground
503 );
504 } else {
505 renderdata->back_buf = renderdata->xwindow;
506 }
507 renderdata->drawable = renderdata->back_buf;
508 renderdata->gc = XCreateGC(renderdata->dpy, renderdata->xwindow, 0, 0);
509 XSetStandardProperties(
510 renderdata->dpy, renderdata->xwindow,
511 title, NULL, None, NULL, 0, NULL
512 );
513 XSetWMProtocols(renderdata->dpy, renderdata->xwindow, &renderdata->wm_delete_msg, 1);
514
515 renderdata->xim = NULL;
516 renderdata->xic = NULL;
517 if (!ximopen(renderdata, renderdata->dpy)) {
518 XRegisterIMInstantiateCallback(
519 renderdata->dpy, NULL, NULL, NULL,
520 ximinstantiate, (XPointer)renderdata
521 );
522 }
523
524 XClearWindow(renderdata->dpy, renderdata->xwindow);
525 XMapRaised(renderdata->dpy, renderdata->xwindow);
526
527 renderdata->xkb_supported = 1;
528 renderdata->xkb_event_type = 0;
529 if (!XkbQueryExtension(renderdata->dpy, 0, &renderdata->xkb_event_type, NULL, &major, &minor)) {
530 ltk_warn("XKB not supported.\n");
531 renderdata->xkb_supported = 0;
532 } else {
533 /* This should select the events when the keyboard mapping changes.
534 * When e.g. 'setxkbmap us' is executed, two events are sent, but I
535 * haven't figured out how to change that. When the xkb layout
536 * switching is used (e.g. 'setxkbmap -option grp:shifts_toggle'),
537 * this issue does not occur because only a state event is sent. */
538 XkbSelectEvents(
539 renderdata->dpy, XkbUseCoreKbd,
540 XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask
541 );
542 XkbSelectEventDetails(
543 renderdata->dpy, XkbUseCoreKbd, XkbStateNotify,
544 XkbAllStateComponentsMask, XkbGroupStateMask
545 );
546 }
547
548 return renderdata;
549 }
550
551 void
552 renderer_destroy_window(ltk_renderdata *renderdata) {
553 XFreeGC(renderdata->dpy, renderdata->gc);
554 if (renderdata->spotlist)
555 XFree(renderdata->spotlist);
556 XDestroyWindow(renderdata->dpy, renderdata->xwindow);
557 XCloseDisplay(renderdata->dpy);
558 ltk_free(renderdata);
559 }
560
561 /* FIXME: this is a completely random collection of properties and should be
562 changed to a more sensible list */
563 void
564 renderer_set_window_properties(ltk_renderdata *renderdata, ltk_color *bg) {
565 XSetWindowBackground(renderdata->dpy, renderdata->xwindow, bg->xcolor.pixel);
566 }
567
568 void
569 renderer_swap_buffers(ltk_renderdata *renderdata) {
570 XdbeSwapInfo swap_info;
571 swap_info.swap_window = renderdata->xwindow;
572 swap_info.swap_action = XdbeBackground;
573 if (!XdbeSwapBuffers(renderdata->dpy, &swap_info, 1))
574 ltk_fatal("Unable to swap buffers.\n");
575 XFlush(renderdata->dpy);
576 }
577
578 unsigned long
579 renderer_get_window_id(ltk_renderdata *renderdata) {
580 return (unsigned long)renderdata->xwindow;
581 }