graphics_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 }