URI: 
       graphics_xlib.c - ltk - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/ltk.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       graphics_xlib.c (16992B)
       ---
            1 /*
            2  * Copyright (c) 2022-2024 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 <string.h>
           19 
           20 #include <X11/XKBlib.h>
           21 #include <X11/Xlib.h>
           22 #include <X11/Xutil.h>
           23 #include <X11/extensions/XKB.h>
           24 #include <X11/extensions/Xdbe.h>
           25 #include <X11/extensions/dbe.h>
           26 
           27 #if USE_XRANDR
           28 #include <X11/extensions/Xrandr.h>
           29 #endif
           30 
           31 #include "graphics_xlib.h"
           32 #include "color.h"
           33 #include "memory.h"
           34 #include "rect.h"
           35 #include "util.h"
           36 
           37 /* FIXME: kind of ugly to have this duplicated here and in event_xlib.c,
           38    but I don't really want to give these functions linkage */
           39 LTK_ARRAY_INIT_FUNC_DECL_STATIC(moninfo, struct ltk_moninfo)
           40 LTK_ARRAY_INIT_IMPL_STATIC(moninfo, struct ltk_moninfo)
           41 
           42 ltk_surface *
           43 ltk_surface_create(ltk_renderwindow *window, int w, int h) {
           44         ltk_surface *s = ltk_malloc(sizeof(ltk_surface));
           45         if (w <= 0)
           46                 w = 1;
           47         if (h <= 0)
           48                 h = 1;
           49         s->w = w;
           50         s->h = h;
           51         s->window = window;
           52         s->d = XCreatePixmap(window->renderdata->dpy, window->xwindow, w, h, window->renderdata->depth);
           53         #if USE_XFT == 1
           54         s->xftdraw = XftDrawCreate(window->renderdata->dpy, s->d, window->renderdata->vis, window->renderdata->cm);
           55         #endif
           56         s->resizable = 1;
           57         return s;
           58 }
           59 
           60 ltk_surface *
           61 ltk_surface_from_window(ltk_renderwindow *window, int w, int h) {
           62         ltk_surface *s = ltk_malloc(sizeof(ltk_surface));
           63         s->w = w;
           64         s->h = h;
           65         s->window = window;
           66         s->d = window->drawable;
           67         #if USE_XFT == 1
           68         s->xftdraw = XftDrawCreate(window->renderdata->dpy, s->d, window->renderdata->vis, window->renderdata->cm);
           69         #endif
           70         s->resizable = 0;
           71         return s;
           72 }
           73 
           74 void
           75 ltk_surface_destroy(ltk_surface *s) {
           76         #if USE_XFT == 1
           77         XftDrawDestroy(s->xftdraw);
           78         #endif
           79         if (s->resizable)
           80                 XFreePixmap(s->window->renderdata->dpy, (Pixmap)s->d);
           81         ltk_free(s);
           82 }
           83 
           84 void
           85 ltk_surface_update_size(ltk_surface *s, int w, int h) {
           86         /* FIXME: maybe return directly if surface is resizable? */
           87         s->w = w;
           88         s->h = h;
           89         /* FIXME: sort of hacky (this is only used by window surface) */
           90         #if USE_XFT == 1
           91         XftDrawChange(s->xftdraw, s->d);
           92         #endif
           93 }
           94 
           95 int
           96 ltk_surface_resize(ltk_surface *s, int w, int h) {
           97         if (!s->resizable)
           98                 return 1;
           99         s->w = w;
          100         s->h = h;
          101         XFreePixmap(s->window->renderdata->dpy, (Pixmap)s->d);
          102         s->d = XCreatePixmap(s->window->renderdata->dpy, s->window->xwindow, w, h, s->window->renderdata->depth);
          103         #if USE_XFT == 1
          104         XftDrawChange(s->xftdraw, s->d);
          105         #endif
          106         return 0;
          107 }
          108 
          109 void
          110 ltk_surface_get_size(ltk_surface *s, int *w, int *h) {
          111         *w = s->w;
          112         *h = s->h;
          113 }
          114 
          115 void
          116 ltk_surface_draw_rect(ltk_surface *s, ltk_color *c, ltk_rect rect, int line_width) {
          117         XSetForeground(s->window->renderdata->dpy, s->window->gc, c->xcolor.pixel);
          118         XSetLineAttributes(s->window->renderdata->dpy, s->window->gc, line_width, LineSolid, CapButt, JoinMiter);
          119         XDrawRectangle(s->window->renderdata->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, rect.h);
          120 }
          121 
          122 void
          123 ltk_surface_draw_border(ltk_surface *s, ltk_color *c, ltk_rect rect, int line_width, ltk_border_sides border_sides) {
          124         /* drawn as rectangles to have proper control over line width - I'm not sure how exactly
          125            XDrawLine handles even line widths (i.e. on which side the extra pixel will be) */
          126         XSetForeground(s->window->renderdata->dpy, s->window->gc, c->xcolor.pixel);
          127         if (border_sides & LTK_BORDER_TOP)
          128                 XFillRectangle(s->window->renderdata->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, line_width);
          129         if (border_sides & LTK_BORDER_BOTTOM)
          130                 XFillRectangle(s->window->renderdata->dpy, s->d, s->window->gc, rect.x, rect.y + rect.h - line_width, rect.w, line_width);
          131         if (border_sides & LTK_BORDER_LEFT)
          132                 XFillRectangle(s->window->renderdata->dpy, s->d, s->window->gc, rect.x, rect.y, line_width, rect.h);
          133         if (border_sides & LTK_BORDER_RIGHT)
          134                 XFillRectangle(s->window->renderdata->dpy, s->d, s->window->gc, rect.x + rect.w - line_width, rect.y, line_width, rect.h);
          135 }
          136 
          137 void
          138 ltk_surface_draw_border_clipped(ltk_surface *s, ltk_color *c, ltk_rect rect, int line_width, ltk_border_sides border_sides, ltk_rect clip) {
          139         if (line_width <= 0)
          140                 return;
          141         /* NOTE: XRectangle only uses short, so this could cause issues */
          142         XRectangle xclip = {clip.x, clip.y, clip.w, clip.h};
          143         XSetClipRectangles(s->window->renderdata->dpy, s->window->gc, 0, 0, &xclip, 1, Unsorted);
          144         ltk_surface_draw_border(s, c, rect, line_width, border_sides);
          145         XSetClipMask(s->window->renderdata->dpy, s->window->gc, None);
          146 }
          147 
          148 void
          149 ltk_surface_fill_rect(ltk_surface *s, ltk_color *c, ltk_rect rect) {
          150         XSetForeground(s->window->renderdata->dpy, s->window->gc, c->xcolor.pixel);
          151         XFillRectangle(s->window->renderdata->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, rect.h);
          152 }
          153 
          154 void
          155 ltk_surface_fill_polygon(ltk_surface *s, ltk_color *c, ltk_point *points, size_t npoints) {
          156         /* FIXME: maybe make this static since this won't be threaded anyways? */
          157         XPoint tmp_points[6]; /* to avoid extra allocations when not necessary */
          158         /* FIXME: this is ugly and inefficient */
          159         XPoint *final_points;
          160         if (npoints <= 6) {
          161                 final_points = tmp_points;
          162         } else {
          163                 final_points = ltk_reallocarray(NULL, npoints, sizeof(XPoint));
          164         }
          165         /* FIXME: how to deal with ints that don't fit in short? */
          166         for (size_t i = 0; i < npoints; i++) {
          167                 final_points[i].x = (short)points[i].x;
          168                 final_points[i].y = (short)points[i].y;
          169         }
          170         XSetForeground(s->window->renderdata->dpy, s->window->gc, c->xcolor.pixel);
          171         XFillPolygon(s->window->renderdata->dpy, s->d, s->window->gc, final_points, (int)npoints, Complex, CoordModeOrigin);
          172         if (npoints > 6)
          173                 ltk_free(final_points);
          174 }
          175 
          176 void
          177 ltk_surface_fill_polygon_clipped(ltk_surface *s, ltk_color *c, ltk_point *points, size_t npoints, ltk_rect clip) {
          178         /* NOTE: XRectangle only uses short, so this could cause issues */
          179         XRectangle xclip = {clip.x, clip.y, clip.w, clip.h};
          180         XSetClipRectangles(s->window->renderdata->dpy, s->window->gc, 0, 0, &xclip, 1, Unsorted);
          181         ltk_surface_fill_polygon(s, c, points, npoints);
          182         XSetClipMask(s->window->renderdata->dpy, s->window->gc, None);
          183 }
          184 
          185 void
          186 ltk_surface_fill_ellipse(ltk_surface *s, ltk_color *c, ltk_rect rect) {
          187         XSetForeground(s->window->renderdata->dpy, s->window->gc, c->xcolor.pixel);
          188         XFillArc(s->window->renderdata->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, rect.h, 0, 360 * 64);
          189 }
          190 
          191 void
          192 ltk_surface_fill_ellipse_clipped(ltk_surface *s, ltk_color *c, ltk_rect rect, ltk_rect clip) {
          193         /* NOTE: XRectangle only uses short, so this could cause issues */
          194         XRectangle xclip = {clip.x, clip.y, clip.w, clip.h};
          195         XSetClipRectangles(s->window->renderdata->dpy, s->window->gc, 0, 0, &xclip, 1, Unsorted);
          196         ltk_surface_fill_ellipse(s, c, rect);
          197         XSetClipMask(s->window->renderdata->dpy, s->window->gc, None);
          198 }
          199 
          200 void
          201 ltk_surface_copy(ltk_surface *src, ltk_surface *dst, ltk_rect src_rect, int dst_x, int dst_y) {
          202         XCopyArea(
          203             src->window->renderdata->dpy, src->d, dst->d, src->window->gc,
          204             src_rect.x, src_rect.y, src_rect.w, src_rect.h, dst_x, dst_y
          205         );
          206 }
          207 
          208 /* TODO */
          209 /*
          210 void
          211 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) {
          212 }
          213 
          214 void
          215 ltk_surface_fill_arc(ltk_surface *s, ltk_color *c, int x, int y, int w, int h, int angle1, int angle2) {
          216 }
          217 
          218 void
          219 ltk_surface_draw_circle(ltk_surface *s, ltk_color *c, int xc, int yc, int r, int line_width) {
          220 }
          221 
          222 void
          223 ltk_surface_fill_circle(ltk_surface *s, ltk_color *c, int xc, int yc, int r) {
          224 }
          225 */
          226 
          227 /* FIXME: move this to a file where it makes more sense */
          228 /* blatantly stolen from st */
          229 static void ximinstantiate(Display *dpy, XPointer client, XPointer call);
          230 static void ximdestroy(XIM xim, XPointer client, XPointer call);
          231 static int xicdestroy(XIC xim, XPointer client, XPointer call);
          232 static int ximopen(ltk_renderwindow *window);
          233 
          234 static void
          235 ximdestroy(XIM xim, XPointer client, XPointer call) {
          236         (void)xim;
          237         (void)call;
          238         ltk_renderwindow *window = (ltk_renderwindow *)client;
          239         window->xim = NULL;
          240         XRegisterIMInstantiateCallback(
          241             window->renderdata->dpy, NULL, NULL, NULL, ximinstantiate, (XPointer)window
          242         );
          243         XFree(window->spotlist);
          244 }
          245 
          246 static int
          247 xicdestroy(XIC xim, XPointer client, XPointer call) {
          248         (void)xim;
          249         (void)call;
          250         ltk_renderwindow *window = (ltk_renderwindow *)client;
          251         window->xic = NULL;
          252         return 1;
          253 }
          254 
          255 static int
          256 ximopen(ltk_renderwindow *window) {
          257         XIMCallback imdestroy = { .client_data = (XPointer)window, .callback = ximdestroy };
          258         XICCallback icdestroy = { .client_data = (XPointer)window, .callback = xicdestroy };
          259 
          260         window->xim = XOpenIM(window->renderdata->dpy, NULL, NULL, NULL);
          261         if (window->xim == NULL)
          262                 return 0;
          263 
          264         if (XSetIMValues(window->xim, XNDestroyCallback, &imdestroy, NULL))
          265                 ltk_warn("XSetIMValues: Could not set XNDestroyCallback.\n");
          266 
          267         window->spotlist = XVaCreateNestedList(0, XNSpotLocation, &window->spot, NULL);
          268 
          269         if (window->xic == NULL) {
          270                 window->xic = XCreateIC(
          271                     window->xim, XNInputStyle,
          272                     XIMPreeditNothing | XIMStatusNothing,
          273                     XNClientWindow, window->xwindow,
          274                     XNDestroyCallback, &icdestroy, NULL
          275                 );
          276         }
          277         if (window->xic == NULL)
          278                 ltk_warn("XCreateIC: Could not create input context.\n");
          279 
          280         return 1;
          281 }
          282 
          283 static void
          284 ximinstantiate(Display *dpy, XPointer client, XPointer call) {
          285         (void)call;
          286         ltk_renderwindow *window = (ltk_renderwindow *)client;
          287         if (ximopen(window)) {
          288                 XUnregisterIMInstantiateCallback(
          289                     dpy, NULL, NULL, NULL, ximinstantiate, (XPointer)window
          290                 );
          291         }
          292 }
          293 
          294 void
          295 ltk_renderer_set_imspot(ltk_renderwindow *window, int x, int y) {
          296         if (window->xic == NULL)
          297                 return;
          298         window->spot.x = x;
          299         window->spot.y = y;
          300         XSetICValues(window->xic, XNPreeditAttributes, window->spotlist, NULL);
          301 }
          302 
          303 ltk_renderdata *
          304 ltk_renderer_create(void) {
          305         /* FIXME: this might not be the best place for this */
          306         XSetLocaleModifiers("");
          307         ltk_renderdata *renderdata = ltk_malloc(sizeof(ltk_renderdata));
          308         renderdata->dpy = XOpenDisplay(NULL);
          309         renderdata->screen = DefaultScreen(renderdata->dpy);
          310         renderdata->db_enabled = 0;
          311         /* based on http://wili.cc/blog/xdbe.html */
          312         int major, minor;
          313         if (XdbeQueryExtension(renderdata->dpy, &major, &minor)) {
          314                 int num_screens = 1;
          315                 Drawable screens[] = {DefaultRootWindow(renderdata->dpy)};
          316                 XdbeScreenVisualInfo *info = XdbeGetVisualInfo(
          317                     renderdata->dpy, screens, &num_screens
          318                 );
          319                 if (!info || num_screens < 1 || info->count < 1) {
          320                         ltk_fatal("No visuals support Xdbe.");
          321                 }
          322                 XVisualInfo xvisinfo_templ;
          323                 /* we know there's at least one */
          324                 xvisinfo_templ.visualid = info->visinfo[0].visual;
          325                 /* FIXME: proper screen number? */
          326                 xvisinfo_templ.screen = 0;
          327                 xvisinfo_templ.depth = info->visinfo[0].depth;
          328                 int matches;
          329                 XVisualInfo *xvisinfo_match = XGetVisualInfo(
          330                     renderdata->dpy,
          331                     VisualIDMask | VisualScreenMask | VisualDepthMask,
          332                     &xvisinfo_templ, &matches
          333                 );
          334                 if (!xvisinfo_match || matches < 1) {
          335                         ltk_fatal("Couldn't match a Visual with double buffering.\n");
          336                 }
          337                 renderdata->vis = xvisinfo_match->visual;
          338                 /* FIXME: is it legal to free this while keeping the visual? */
          339                 XFree(xvisinfo_match);
          340                 XdbeFreeVisualInfo(info);
          341                 renderdata->db_enabled = 1;
          342         } else {
          343                 renderdata->vis = DefaultVisual(renderdata->dpy, renderdata->screen);
          344                 ltk_warn("No Xdbe support.\n");
          345         }
          346         renderdata->cm = DefaultColormap(renderdata->dpy, renderdata->screen);
          347         renderdata->wm_delete_msg = XInternAtom(renderdata->dpy, "WM_DELETE_WINDOW", False);
          348         renderdata->depth = DefaultDepth(renderdata->dpy, renderdata->screen);
          349         renderdata->xkb_supported = 1;
          350         renderdata->xkb_event_type = 0;
          351         /* FIXME: check version */
          352         if (!XkbQueryExtension(renderdata->dpy, 0, &renderdata->xkb_event_type, NULL, &major, &minor)) {
          353                 ltk_warn("XKB not supported.\n");
          354                 renderdata->xkb_supported = 0;
          355         } else {
          356                 /* This should select the events when the keyboard mapping changes.
          357                  * When e.g. 'setxkbmap us' is executed, two events are sent, but I
          358                  * haven't figured out how to change that. When the xkb layout
          359                  * switching is used (e.g. 'setxkbmap -option grp:shifts_toggle'),
          360                  * this issue does not occur because only a state event is sent. */
          361                 XkbSelectEvents(
          362                     renderdata->dpy, XkbUseCoreKbd,
          363                     XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask
          364                 );
          365                 XkbSelectEventDetails(
          366                     renderdata->dpy, XkbUseCoreKbd, XkbStateNotify,
          367                     XkbAllStateComponentsMask, XkbGroupStateMask
          368                 );
          369         }
          370         renderdata->monitors = NULL;
          371         renderdata->xrandr_supported = 0;
          372         renderdata->root_window = XRootWindow(renderdata->dpy, renderdata->screen);
          373 #if USE_XRANDR
          374         if (!XRRQueryVersion(renderdata->dpy, &major, &minor)) {
          375                 ltk_warn("XRandR not supported.\n");
          376         } else if (major < 1 || (major == 1 && minor < 5)) {
          377                 ltk_warn("X server only supports XRandR version %d.%d. Need at least 1.5.\n", major, minor);
          378         } else {
          379                 /* FIXME: check if XRRQueryExtension allows passing NULL */
          380                 int tmp;
          381                 if (!XRRQueryExtension(renderdata->dpy, &renderdata->xrandr_event_type, &tmp)) {
          382                         ltk_warn("Unable to get XRandR event type.\n");
          383                 } else {
          384                         renderdata->xrandr_supported = 1;
          385                         /* FIXME: is this mask correct? */
          386                         XRRSelectInput(renderdata->dpy, renderdata->root_window, RRScreenChangeNotifyMask);
          387                 }
          388         }
          389 
          390 #endif
          391 
          392         return renderdata;
          393 }
          394 
          395 ltk_renderwindow *
          396 ltk_renderer_create_window(ltk_renderdata *data, const char *title, int x, int y, unsigned int w, unsigned int h, unsigned int initial_dpi) {
          397         XSetWindowAttributes attrs;
          398         ltk_renderwindow *window = ltk_malloc(sizeof(ltk_renderwindow));
          399         window->renderdata = data;
          400         memset(&attrs, 0, sizeof(attrs));
          401         attrs.background_pixel = BlackPixel(data->dpy, data->screen);
          402         attrs.colormap = data->cm;
          403         attrs.border_pixel = WhitePixel(data->dpy, data->screen);
          404         /* this causes the window contents to be kept
          405          * when it is resized, leading to less flicker */
          406         attrs.bit_gravity = NorthWestGravity;
          407         attrs.event_mask =
          408                 ExposureMask | KeyPressMask | KeyReleaseMask |
          409                 ButtonPressMask | ButtonReleaseMask |
          410                 StructureNotifyMask | PointerMotionMask;
          411         /* FIXME: conversion between signed and unsigned */
          412         window->rect = (ltk_rect){x, y, w, h};
          413         /* FIXME: set border width */
          414         window->xwindow = XCreateWindow(
          415             data->dpy, DefaultRootWindow(data->dpy), x, y,
          416             w, h, 0, data->depth,
          417             InputOutput, data->vis,
          418             CWBackPixel | CWColormap | CWBitGravity | CWEventMask | CWBorderPixel, &attrs
          419         );
          420 
          421         if (data->db_enabled) {
          422                 window->back_buf = XdbeAllocateBackBufferName(
          423                         data->dpy, window->xwindow, XdbeBackground
          424                 );
          425         } else {
          426                 window->back_buf = window->xwindow;
          427         }
          428         window->drawable = window->back_buf;
          429         window->gc = XCreateGC(data->dpy, window->xwindow, 0, 0);
          430         XSetStandardProperties(
          431                 data->dpy, window->xwindow,
          432                 title, NULL, None, NULL, 0, NULL
          433         );
          434         /* FIXME: check return value */
          435         XSetWMProtocols(data->dpy, window->xwindow, &data->wm_delete_msg, 1);
          436 
          437         window->xic = NULL;
          438         window->xim = NULL;
          439         if (!ximopen(window)) {
          440                 XRegisterIMInstantiateCallback(
          441                     window->renderdata->dpy, NULL, NULL, NULL,
          442                     ximinstantiate, (XPointer)window
          443                 );
          444         }
          445 
          446         XClearWindow(window->renderdata->dpy, window->xwindow);
          447         XMapRaised(window->renderdata->dpy, window->xwindow);
          448 
          449         window->dpi = initial_dpi;
          450         ltk_recalc_renderwindow_dpi(window);
          451 
          452         return window;
          453 }
          454 
          455 unsigned int
          456 ltk_renderer_get_window_dpi(ltk_renderwindow *window) {
          457         return window->dpi;
          458 }
          459 
          460 void
          461 ltk_renderer_destroy_window(ltk_renderwindow *window) {
          462         XFreeGC(window->renderdata->dpy, window->gc);
          463         if (window->spotlist)
          464                 XFree(window->spotlist);
          465         /* FIXME: destroy xim/xic? */
          466         XDestroyWindow(window->renderdata->dpy, window->xwindow);
          467         ltk_free(window);
          468 }
          469 
          470 void
          471 ltk_renderer_destroy(ltk_renderdata *renderdata) {
          472         if (renderdata->monitors)
          473                 ltk_array_destroy(moninfo, renderdata->monitors);
          474         XCloseDisplay(renderdata->dpy);
          475         /* FIXME: destroy visual, wm_delete_msg, etc.? */
          476         ltk_free(renderdata);
          477 }
          478 
          479 /* FIXME: this is a completely random collection of properties and should be
          480    changed to a more sensible list */
          481 void
          482 ltk_renderer_set_window_properties(ltk_renderwindow *window, ltk_color *bg) {
          483         XSetWindowBackground(window->renderdata->dpy, window->xwindow, bg->xcolor.pixel);
          484 }
          485 
          486 void
          487 ltk_renderer_swap_buffers(ltk_renderwindow *window) {
          488         XdbeSwapInfo swap_info;
          489         swap_info.swap_window = window->xwindow;
          490         swap_info.swap_action = XdbeBackground;
          491         if (!XdbeSwapBuffers(window->renderdata->dpy, &swap_info, 1))
          492                 ltk_fatal("Unable to swap buffers.\n");
          493         XFlush(window->renderdata->dpy);
          494 }
          495 
          496 unsigned long
          497 ltk_renderer_get_window_id(ltk_renderwindow *window) {
          498         return (unsigned long)window->xwindow;
          499 }