sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_x11events.c (55955B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../../SDL_internal.h"
     22 
     23 #if SDL_VIDEO_DRIVER_X11
     24 
     25 #include <sys/types.h>
     26 #include <sys/time.h>
     27 #include <signal.h>
     28 #include <unistd.h>
     29 #include <limits.h> /* For INT_MAX */
     30 
     31 #include "SDL_x11video.h"
     32 #include "SDL_x11touch.h"
     33 #include "SDL_x11xinput2.h"
     34 #include "../../core/unix/SDL_poll.h"
     35 #include "../../events/SDL_events_c.h"
     36 #include "../../events/SDL_mouse_c.h"
     37 #include "../../events/SDL_touch_c.h"
     38 
     39 #include "SDL_hints.h"
     40 #include "SDL_timer.h"
     41 #include "SDL_syswm.h"
     42 
     43 #include <stdio.h>
     44 
     45 /*#define DEBUG_XEVENTS*/
     46 
     47 #ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT
     48 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
     49 #endif
     50 
     51 #ifndef _NET_WM_MOVERESIZE_SIZE_TOP
     52 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
     53 #endif
     54 
     55 #ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT
     56 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
     57 #endif
     58 
     59 #ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT
     60 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
     61 #endif
     62 
     63 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
     64 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
     65 #endif
     66 
     67 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM
     68 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
     69 #endif
     70 
     71 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
     72 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
     73 #endif
     74 
     75 #ifndef _NET_WM_MOVERESIZE_SIZE_LEFT
     76 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
     77 #endif
     78 
     79 #ifndef _NET_WM_MOVERESIZE_MOVE
     80 #define _NET_WM_MOVERESIZE_MOVE              8
     81 #endif
     82 
     83 typedef struct {
     84     unsigned char *data;
     85     int format, count;
     86     Atom type;
     87 } SDL_x11Prop;
     88 
     89 /* Reads property
     90    Must call X11_XFree on results
     91  */
     92 static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
     93 {
     94     unsigned char *ret=NULL;
     95     Atom type;
     96     int fmt;
     97     unsigned long count;
     98     unsigned long bytes_left;
     99     int bytes_fetch = 0;
    100 
    101     do {
    102         if (ret != 0) X11_XFree(ret);
    103         X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
    104         bytes_fetch += bytes_left;
    105     } while (bytes_left != 0);
    106 
    107     p->data=ret;
    108     p->format=fmt;
    109     p->count=count;
    110     p->type=type;
    111 }
    112 
    113 /* Find text-uri-list in a list of targets and return it's atom
    114    if available, else return None */
    115 static Atom X11_PickTarget(Display *disp, Atom list[], int list_count)
    116 {
    117     Atom request = None;
    118     char *name;
    119     int i;
    120     for (i=0; i < list_count && request == None; i++) {
    121         name = X11_XGetAtomName(disp, list[i]);
    122         if ((SDL_strcmp("text/uri-list", name) == 0) || (SDL_strcmp("text/plain", name) == 0)) {
    123              request = list[i];
    124         }
    125         X11_XFree(name);
    126     }
    127     return request;
    128 }
    129 
    130 /* Wrapper for X11_PickTarget for a maximum of three targets, a special
    131    case in the Xdnd protocol */
    132 static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2)
    133 {
    134     int count=0;
    135     Atom atom[3];
    136     if (a0 != None) atom[count++] = a0;
    137     if (a1 != None) atom[count++] = a1;
    138     if (a2 != None) atom[count++] = a2;
    139     return X11_PickTarget(disp, atom, count);
    140 }
    141 
    142 struct KeyRepeatCheckData
    143 {
    144     XEvent *event;
    145     SDL_bool found;
    146 };
    147 
    148 static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev,
    149     XPointer arg)
    150 {
    151     struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *) arg;
    152     if (chkev->type == KeyPress &&
    153         chkev->xkey.keycode == d->event->xkey.keycode &&
    154         chkev->xkey.time - d->event->xkey.time < 2)
    155         d->found = SDL_TRUE;
    156     return False;
    157 }
    158 
    159 /* Check to see if this is a repeated key.
    160    (idea shamelessly lifted from GII -- thanks guys! :)
    161  */
    162 static SDL_bool X11_KeyRepeat(Display *display, XEvent *event)
    163 {
    164     XEvent dummyev;
    165     struct KeyRepeatCheckData d;
    166     d.event = event;
    167     d.found = SDL_FALSE;
    168     if (X11_XPending(display))
    169         X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent,
    170             (XPointer) &d);
    171     return d.found;
    172 }
    173 
    174 static SDL_bool
    175 X11_IsWheelEvent(Display * display,XEvent * event,int * xticks,int * yticks)
    176 {
    177     /* according to the xlib docs, no specific mouse wheel events exist.
    178        However, the defacto standard is that the vertical wheel is X buttons
    179        4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */
    180 
    181     /* Xlib defines "Button1" through 5, so we just use literals here. */
    182     switch (event->xbutton.button) {
    183         case 4: *yticks = 1; return SDL_TRUE;
    184         case 5: *yticks = -1; return SDL_TRUE;
    185         case 6: *xticks = 1; return SDL_TRUE;
    186         case 7: *xticks = -1; return SDL_TRUE;
    187         default: break;
    188     }
    189     return SDL_FALSE;
    190 }
    191 
    192 /* Decodes URI escape sequences in string buf of len bytes
    193    (excluding the terminating NULL byte) in-place. Since
    194    URI-encoded characters take three times the space of
    195    normal characters, this should not be an issue.
    196 
    197    Returns the number of decoded bytes that wound up in
    198    the buffer, excluding the terminating NULL byte.
    199 
    200    The buffer is guaranteed to be NULL-terminated but
    201    may contain embedded NULL bytes.
    202 
    203    On error, -1 is returned.
    204  */
    205 static int X11_URIDecode(char *buf, int len) {
    206     int ri, wi, di;
    207     char decode = '\0';
    208     if (buf == NULL || len < 0) {
    209         errno = EINVAL;
    210         return -1;
    211     }
    212     if (len == 0) {
    213         len = SDL_strlen(buf);
    214     }
    215     for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) {
    216         if (di == 0) {
    217             /* start decoding */
    218             if (buf[ri] == '%') {
    219                 decode = '\0';
    220                 di += 1;
    221                 continue;
    222             }
    223             /* normal write */
    224             buf[wi] = buf[ri];
    225             wi += 1;
    226             continue;
    227         } else if (di == 1 || di == 2) {
    228             char off = '\0';
    229             char isa = buf[ri] >= 'a' && buf[ri] <= 'f';
    230             char isA = buf[ri] >= 'A' && buf[ri] <= 'F';
    231             char isn = buf[ri] >= '0' && buf[ri] <= '9';
    232             if (!(isa || isA || isn)) {
    233                 /* not a hexadecimal */
    234                 int sri;
    235                 for (sri = ri - di; sri <= ri; sri += 1) {
    236                     buf[wi] = buf[sri];
    237                     wi += 1;
    238                 }
    239                 di = 0;
    240                 continue;
    241             }
    242             /* itsy bitsy magicsy */
    243             if (isn) {
    244                 off = 0 - '0';
    245             } else if (isa) {
    246                 off = 10 - 'a';
    247             } else if (isA) {
    248                 off = 10 - 'A';
    249             }
    250             decode |= (buf[ri] + off) << (2 - di) * 4;
    251             if (di == 2) {
    252                 buf[wi] = decode;
    253                 wi += 1;
    254                 di = 0;
    255             } else {
    256                 di += 1;
    257             }
    258             continue;
    259         }
    260     }
    261     buf[wi] = '\0';
    262     return wi;
    263 }
    264 
    265 /* Convert URI to local filename
    266    return filename if possible, else NULL
    267 */
    268 static char* X11_URIToLocal(char* uri) {
    269     char *file = NULL;
    270     SDL_bool local;
    271 
    272     if (memcmp(uri,"file:/",6) == 0) uri += 6;      /* local file? */
    273     else if (strstr(uri,":/") != NULL) return file; /* wrong scheme */
    274 
    275     local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/');
    276 
    277     /* got a hostname? */
    278     if (!local && uri[0] == '/' && uri[2] != '/') {
    279       char* hostname_end = strchr(uri+1, '/');
    280       if (hostname_end != NULL) {
    281           char hostname[ 257 ];
    282           if (gethostname(hostname, 255) == 0) {
    283             hostname[ 256 ] = '\0';
    284             if (memcmp(uri+1, hostname, hostname_end - (uri+1)) == 0) {
    285                 uri = hostname_end + 1;
    286                 local = SDL_TRUE;
    287             }
    288           }
    289       }
    290     }
    291     if (local) {
    292       file = uri;
    293       /* Convert URI escape sequences to real characters */
    294       X11_URIDecode(file, 0);
    295       if (uri[1] == '/') {
    296           file++;
    297       } else {
    298           file--;
    299       }
    300     }
    301     return file;
    302 }
    303 
    304 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
    305 static void X11_HandleGenericEvent(SDL_VideoData *videodata, XEvent *xev)
    306 {
    307     /* event is a union, so cookie == &event, but this is type safe. */
    308     XGenericEventCookie *cookie = &xev->xcookie;
    309     if (X11_XGetEventData(videodata->display, cookie)) {
    310         X11_HandleXinput2Event(videodata, cookie);
    311 
    312         /* Send a SDL_SYSWMEVENT if the application wants them.
    313          * Since event data is only available until XFreeEventData is called,
    314          * the *only* way for an application to access it is to register an event filter/watcher
    315          * and do all the processing on the SDL_SYSWMEVENT inside the callback. */
    316         if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
    317             SDL_SysWMmsg wmmsg;
    318 
    319             SDL_VERSION(&wmmsg.version);
    320             wmmsg.subsystem = SDL_SYSWM_X11;
    321             wmmsg.msg.x11.event = *xev;
    322             SDL_SendSysWMEvent(&wmmsg);
    323         }
    324 
    325         X11_XFreeEventData(videodata->display, cookie);
    326     }
    327 }
    328 #endif /* SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS */
    329 
    330 static unsigned
    331 X11_GetNumLockModifierMask(_THIS)
    332 {
    333     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
    334     Display *display = viddata->display;
    335     unsigned num_mask = 0;
    336     int i, j;
    337     XModifierKeymap *xmods;
    338     unsigned n;
    339 
    340     xmods = X11_XGetModifierMapping(display);
    341     n = xmods->max_keypermod;
    342     for(i = 3; i < 8; i++) {
    343         for(j = 0; j < n; j++) {
    344             KeyCode kc = xmods->modifiermap[i * n + j];
    345             if (viddata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) {
    346                 num_mask = 1 << i;
    347                 break;
    348             }
    349         }
    350     }
    351     X11_XFreeModifiermap(xmods);
    352 
    353     return num_mask;
    354 }
    355 
    356 static void
    357 X11_ReconcileKeyboardState(_THIS)
    358 {
    359     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
    360     Display *display = viddata->display;
    361     char keys[32];
    362     int keycode;
    363     Window junk_window;
    364     int x, y;
    365     unsigned int mask;
    366     const Uint8 *keyboardState;
    367 
    368     X11_XQueryKeymap(display, keys);
    369 
    370     /* Sync up the keyboard modifier state */
    371     if (X11_XQueryPointer(display, DefaultRootWindow(display), &junk_window, &junk_window, &x, &y, &x, &y, &mask)) {
    372         SDL_ToggleModState(KMOD_CAPS, (mask & LockMask) != 0);
    373         SDL_ToggleModState(KMOD_NUM, (mask & X11_GetNumLockModifierMask(_this)) != 0);
    374     }
    375 
    376     keyboardState = SDL_GetKeyboardState(0);
    377     for (keycode = 0; keycode < 256; ++keycode) {
    378         SDL_Scancode scancode = viddata->key_layout[keycode];
    379         SDL_bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
    380         SDL_bool sdlKeyPressed = keyboardState[scancode] == SDL_PRESSED;
    381 
    382         if (x11KeyPressed && !sdlKeyPressed) {
    383             SDL_SendKeyboardKey(SDL_PRESSED, scancode);
    384         } else if (!x11KeyPressed && sdlKeyPressed) {
    385             SDL_SendKeyboardKey(SDL_RELEASED, scancode);
    386         }
    387     }
    388 }
    389 
    390 
    391 static void
    392 X11_DispatchFocusIn(_THIS, SDL_WindowData *data)
    393 {
    394 #ifdef DEBUG_XEVENTS
    395     printf("window %p: Dispatching FocusIn\n", data);
    396 #endif
    397     SDL_SetKeyboardFocus(data->window);
    398     X11_ReconcileKeyboardState(_this);
    399 #ifdef X_HAVE_UTF8_STRING
    400     if (data->ic) {
    401         X11_XSetICFocus(data->ic);
    402     }
    403 #endif
    404 #ifdef SDL_USE_IME
    405     SDL_IME_SetFocus(SDL_TRUE);
    406 #endif
    407 }
    408 
    409 static void
    410 X11_DispatchFocusOut(_THIS, SDL_WindowData *data)
    411 {
    412 #ifdef DEBUG_XEVENTS
    413     printf("window %p: Dispatching FocusOut\n", data);
    414 #endif
    415     /* If another window has already processed a focus in, then don't try to
    416      * remove focus here.  Doing so will incorrectly remove focus from that
    417      * window, and the focus lost event for this window will have already
    418      * been dispatched anyway. */
    419     if (data->window == SDL_GetKeyboardFocus()) {
    420         SDL_SetKeyboardFocus(NULL);
    421     }
    422 #ifdef X_HAVE_UTF8_STRING
    423     if (data->ic) {
    424         X11_XUnsetICFocus(data->ic);
    425     }
    426 #endif
    427 #ifdef SDL_USE_IME
    428     SDL_IME_SetFocus(SDL_FALSE);
    429 #endif
    430 }
    431 
    432 static void
    433 X11_DispatchMapNotify(SDL_WindowData *data)
    434 {
    435     SDL_Window *window = data->window;
    436     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
    437     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
    438     if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
    439         SDL_UpdateWindowGrab(window);
    440     }
    441 }
    442 
    443 static void
    444 X11_DispatchUnmapNotify(SDL_WindowData *data)
    445 {
    446     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
    447     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
    448 }
    449 
    450 static void
    451 InitiateWindowMove(_THIS, const SDL_WindowData *data, const SDL_Point *point)
    452 {
    453     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
    454     SDL_Window* window = data->window;
    455     Display *display = viddata->display;
    456     XEvent evt;
    457 
    458     /* !!! FIXME: we need to regrab this if necessary when the drag is done. */
    459     X11_XUngrabPointer(display, 0L);
    460     X11_XFlush(display);
    461 
    462     evt.xclient.type = ClientMessage;
    463     evt.xclient.window = data->xwindow;
    464     evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True);
    465     evt.xclient.format = 32;
    466     evt.xclient.data.l[0] = window->x + point->x;
    467     evt.xclient.data.l[1] = window->y + point->y;
    468     evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE;
    469     evt.xclient.data.l[3] = Button1;
    470     evt.xclient.data.l[4] = 0;
    471     X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
    472 
    473     X11_XSync(display, 0);
    474 }
    475 
    476 static void
    477 InitiateWindowResize(_THIS, const SDL_WindowData *data, const SDL_Point *point, int direction)
    478 {
    479     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
    480     SDL_Window* window = data->window;
    481     Display *display = viddata->display;
    482     XEvent evt;
    483 
    484     if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT)
    485         return;
    486 
    487     /* !!! FIXME: we need to regrab this if necessary when the drag is done. */
    488     X11_XUngrabPointer(display, 0L);
    489     X11_XFlush(display);
    490 
    491     evt.xclient.type = ClientMessage;
    492     evt.xclient.window = data->xwindow;
    493     evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True);
    494     evt.xclient.format = 32;
    495     evt.xclient.data.l[0] = window->x + point->x;
    496     evt.xclient.data.l[1] = window->y + point->y;
    497     evt.xclient.data.l[2] = direction;
    498     evt.xclient.data.l[3] = Button1;
    499     evt.xclient.data.l[4] = 0;
    500     X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
    501 
    502     X11_XSync(display, 0);
    503 }
    504 
    505 static SDL_bool
    506 ProcessHitTest(_THIS, const SDL_WindowData *data, const XEvent *xev)
    507 {
    508     SDL_Window *window = data->window;
    509 
    510     if (window->hit_test) {
    511         const SDL_Point point = { xev->xbutton.x, xev->xbutton.y };
    512         const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
    513         static const int directions[] = {
    514             _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP,
    515             _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT,
    516             _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM,
    517             _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT
    518         };
    519 
    520         switch (rc) {
    521             case SDL_HITTEST_DRAGGABLE:
    522                 InitiateWindowMove(_this, data, &point);
    523                 return SDL_TRUE;
    524 
    525             case SDL_HITTEST_RESIZE_TOPLEFT:
    526             case SDL_HITTEST_RESIZE_TOP:
    527             case SDL_HITTEST_RESIZE_TOPRIGHT:
    528             case SDL_HITTEST_RESIZE_RIGHT:
    529             case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
    530             case SDL_HITTEST_RESIZE_BOTTOM:
    531             case SDL_HITTEST_RESIZE_BOTTOMLEFT:
    532             case SDL_HITTEST_RESIZE_LEFT:
    533                 InitiateWindowResize(_this, data, &point, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
    534                 return SDL_TRUE;
    535 
    536             default: return SDL_FALSE;
    537         }
    538     }
    539 
    540     return SDL_FALSE;
    541 }
    542 
    543 static void
    544 X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest)
    545 {
    546     if (latest && (latest != data->user_time)) {
    547         SDL_VideoData *videodata = data->videodata;
    548         Display *display = videodata->display;
    549         X11_XChangeProperty(display, data->xwindow, videodata->_NET_WM_USER_TIME,
    550                             XA_CARDINAL, 32, PropModeReplace,
    551                             (const unsigned char *) &latest, 1);
    552 #ifdef DEBUG_XEVENTS
    553         printf("window %p: updating _NET_WM_USER_TIME to %lu\n", data, latest);
    554 #endif
    555         data->user_time = latest;
    556     }
    557 }
    558 
    559 static void
    560 X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
    561 {
    562     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    563     Display *display = videodata->display;
    564 
    565     SDL_assert(videodata->clipboard_window != None);
    566     SDL_assert(xevent->xany.window == videodata->clipboard_window);
    567 
    568     switch (xevent->type) {
    569     /* Copy the selection from our own CUTBUFFER to the requested property */
    570         case SelectionRequest: {
    571             const XSelectionRequestEvent *req = &xevent->xselectionrequest;
    572             XEvent sevent;
    573             int seln_format;
    574             unsigned long nbytes;
    575             unsigned long overflow;
    576             unsigned char *seln_data;
    577 
    578 #ifdef DEBUG_XEVENTS
    579             printf("window CLIPBOARD: SelectionRequest (requestor = %ld, target = %ld)\n",
    580                 req->requestor, req->target);
    581 #endif
    582 
    583             SDL_zero(sevent);
    584             sevent.xany.type = SelectionNotify;
    585             sevent.xselection.selection = req->selection;
    586             sevent.xselection.target = None;
    587             sevent.xselection.property = None;  /* tell them no by default */
    588             sevent.xselection.requestor = req->requestor;
    589             sevent.xselection.time = req->time;
    590 
    591             /* !!! FIXME: We were probably storing this on the root window
    592                because an SDL window might go away...? but we don't have to do
    593                this now (or ever, really). */
    594             if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
    595                     X11_GetSDLCutBufferClipboardType(display), 0, INT_MAX/4, False, req->target,
    596                     &sevent.xselection.target, &seln_format, &nbytes,
    597                     &overflow, &seln_data) == Success) {
    598                 /* !!! FIXME: cache atoms */
    599                 Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
    600                 if (sevent.xselection.target == req->target) {
    601                     X11_XChangeProperty(display, req->requestor, req->property,
    602                         sevent.xselection.target, seln_format, PropModeReplace,
    603                         seln_data, nbytes);
    604                     sevent.xselection.property = req->property;
    605                 } else if (XA_TARGETS == req->target) {
    606                     Atom SupportedFormats[] = { XA_TARGETS, sevent.xselection.target };
    607                     X11_XChangeProperty(display, req->requestor, req->property,
    608                         XA_ATOM, 32, PropModeReplace,
    609                         (unsigned char*)SupportedFormats,
    610                         SDL_arraysize(SupportedFormats));
    611                     sevent.xselection.property = req->property;
    612                     sevent.xselection.target = XA_TARGETS;
    613                 }
    614                 X11_XFree(seln_data);
    615             }
    616             X11_XSendEvent(display, req->requestor, False, 0, &sevent);
    617             X11_XSync(display, False);
    618         }
    619         break;
    620 
    621         case SelectionNotify: {
    622 #ifdef DEBUG_XEVENTS
    623             printf("window CLIPBOARD: SelectionNotify (requestor = %ld, target = %ld)\n",
    624                 xevent->xselection.requestor, xevent->xselection.target);
    625 #endif
    626             videodata->selection_waiting = SDL_FALSE;
    627         }
    628         break;
    629 
    630         case SelectionClear: {
    631             /* !!! FIXME: cache atoms */
    632             Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
    633 
    634 #ifdef DEBUG_XEVENTS
    635             printf("window CLIPBOARD: SelectionClear (requestor = %ld, target = %ld)\n",
    636                 xevent->xselection.requestor, xevent->xselection.target);
    637 #endif
    638 
    639             if (xevent->xselectionclear.selection == XA_PRIMARY ||
    640                 (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD)) {
    641                 SDL_SendClipboardUpdate();
    642             }
    643         }
    644         break;
    645     }
    646 }
    647 
    648 static Bool
    649 isMapNotify(Display *display, XEvent *ev, XPointer arg)
    650 {
    651     XUnmapEvent *unmap;
    652 
    653     unmap = (XUnmapEvent*) arg;
    654 
    655     return ev->type == MapNotify &&
    656         ev->xmap.window == unmap->window &&
    657         ev->xmap.serial == unmap->serial;
    658 }
    659 
    660 static Bool
    661 isReparentNotify(Display *display, XEvent *ev, XPointer arg)
    662 {
    663     XUnmapEvent *unmap;
    664 
    665     unmap = (XUnmapEvent*) arg;
    666 
    667     return ev->type == ReparentNotify &&
    668         ev->xreparent.window == unmap->window &&
    669         ev->xreparent.serial == unmap->serial;
    670 }
    671 
    672 static void
    673 X11_DispatchEvent(_THIS)
    674 {
    675     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    676     Display *display;
    677     SDL_WindowData *data;
    678     XEvent xevent;
    679     int orig_event_type;
    680     KeyCode orig_keycode;
    681     XClientMessageEvent m;
    682     int i;
    683 
    684     if (!videodata) {
    685         return;
    686     }
    687     display = videodata->display;
    688 
    689     SDL_zero(xevent);           /* valgrind fix. --ryan. */
    690     X11_XNextEvent(display, &xevent);
    691 
    692     /* Save the original keycode for dead keys, which are filtered out by
    693        the XFilterEvent() call below.
    694     */
    695     orig_event_type = xevent.type;
    696     if (orig_event_type == KeyPress || orig_event_type == KeyRelease) {
    697         orig_keycode = xevent.xkey.keycode;
    698     } else {
    699         orig_keycode = 0;
    700     }
    701 
    702     /* filter events catchs XIM events and sends them to the correct handler */
    703     if (X11_XFilterEvent(&xevent, None) == True) {
    704 #if 0
    705         printf("Filtered event type = %d display = %d window = %d\n",
    706                xevent.type, xevent.xany.display, xevent.xany.window);
    707 #endif
    708         /* Make sure dead key press/release events are sent */
    709         /* But only if we're using one of the DBus IMEs, otherwise
    710            some XIM IMEs will generate duplicate events */
    711         if (orig_keycode) {
    712 #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX)
    713             SDL_Scancode scancode = videodata->key_layout[orig_keycode];
    714             videodata->filter_code = orig_keycode;
    715             videodata->filter_time = xevent.xkey.time;
    716 
    717             if (orig_event_type == KeyPress) {
    718                 SDL_SendKeyboardKey(SDL_PRESSED, scancode);
    719             } else {
    720                 SDL_SendKeyboardKey(SDL_RELEASED, scancode);
    721             }
    722 #endif
    723         }
    724         return;
    725     }
    726 
    727 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
    728     if(xevent.type == GenericEvent) {
    729         X11_HandleGenericEvent(videodata, &xevent);
    730         return;
    731     }
    732 #endif
    733 
    734     /* Send a SDL_SYSWMEVENT if the application wants them */
    735     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
    736         SDL_SysWMmsg wmmsg;
    737 
    738         SDL_VERSION(&wmmsg.version);
    739         wmmsg.subsystem = SDL_SYSWM_X11;
    740         wmmsg.msg.x11.event = xevent;
    741         SDL_SendSysWMEvent(&wmmsg);
    742     }
    743 
    744 #if 0
    745     printf("type = %d display = %d window = %d\n",
    746            xevent.type, xevent.xany.display, xevent.xany.window);
    747 #endif
    748 
    749     if ((videodata->clipboard_window != None) &&
    750         (videodata->clipboard_window == xevent.xany.window)) {
    751         X11_HandleClipboardEvent(_this, &xevent);
    752         return;
    753     }
    754 
    755     data = NULL;
    756     if (videodata && videodata->windowlist) {
    757         for (i = 0; i < videodata->numwindows; ++i) {
    758             if ((videodata->windowlist[i] != NULL) &&
    759                 (videodata->windowlist[i]->xwindow == xevent.xany.window)) {
    760                 data = videodata->windowlist[i];
    761                 break;
    762             }
    763         }
    764     }
    765     if (!data) {
    766         /* The window for KeymapNotify, etc events is 0 */
    767         if (xevent.type == KeymapNotify) {
    768             if (SDL_GetKeyboardFocus() != NULL) {
    769                 X11_ReconcileKeyboardState(_this);
    770             }
    771         } else if (xevent.type == MappingNotify) {
    772             /* Has the keyboard layout changed? */
    773             const int request = xevent.xmapping.request;
    774 
    775 #ifdef DEBUG_XEVENTS
    776             printf("window %p: MappingNotify!\n", data);
    777 #endif
    778             if ((request == MappingKeyboard) || (request == MappingModifier)) {
    779                 X11_XRefreshKeyboardMapping(&xevent.xmapping);
    780             }
    781 
    782             X11_UpdateKeymap(_this);
    783             SDL_SendKeymapChangedEvent();
    784         }
    785         return;
    786     }
    787 
    788     switch (xevent.type) {
    789 
    790         /* Gaining mouse coverage? */
    791     case EnterNotify:{
    792             SDL_Mouse *mouse = SDL_GetMouse();
    793 #ifdef DEBUG_XEVENTS
    794             printf("window %p: EnterNotify! (%d,%d,%d)\n", data,
    795                    xevent.xcrossing.x,
    796                    xevent.xcrossing.y,
    797                    xevent.xcrossing.mode);
    798             if (xevent.xcrossing.mode == NotifyGrab)
    799                 printf("Mode: NotifyGrab\n");
    800             if (xevent.xcrossing.mode == NotifyUngrab)
    801                 printf("Mode: NotifyUngrab\n");
    802 #endif
    803             SDL_SetMouseFocus(data->window);
    804 
    805             mouse->last_x = xevent.xcrossing.x;
    806             mouse->last_y = xevent.xcrossing.y;
    807 
    808             if (!mouse->relative_mode) {
    809                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y);
    810             }
    811         }
    812         break;
    813         /* Losing mouse coverage? */
    814     case LeaveNotify:{
    815 #ifdef DEBUG_XEVENTS
    816             printf("window %p: LeaveNotify! (%d,%d,%d)\n", data,
    817                    xevent.xcrossing.x,
    818                    xevent.xcrossing.y,
    819                    xevent.xcrossing.mode);
    820             if (xevent.xcrossing.mode == NotifyGrab)
    821                 printf("Mode: NotifyGrab\n");
    822             if (xevent.xcrossing.mode == NotifyUngrab)
    823                 printf("Mode: NotifyUngrab\n");
    824 #endif
    825             if (!SDL_GetMouse()->relative_mode) {
    826                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y);
    827             }
    828 
    829             if (xevent.xcrossing.mode != NotifyGrab &&
    830                 xevent.xcrossing.mode != NotifyUngrab &&
    831                 xevent.xcrossing.detail != NotifyInferior) {
    832                 SDL_SetMouseFocus(NULL);
    833             }
    834         }
    835         break;
    836 
    837         /* Gaining input focus? */
    838     case FocusIn:{
    839             if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) {
    840                 /* Someone is handling a global hotkey, ignore it */
    841 #ifdef DEBUG_XEVENTS
    842                 printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data);
    843 #endif
    844                 break;
    845             }
    846 
    847             if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) {
    848 #ifdef DEBUG_XEVENTS
    849                 printf("window %p: FocusIn (NotifyInferior/NotifyPointer, ignoring)\n", data);
    850 #endif
    851                 break;
    852             }
    853 #ifdef DEBUG_XEVENTS
    854             printf("window %p: FocusIn!\n", data);
    855 #endif
    856             if (!videodata->last_mode_change_deadline) /* no recent mode changes */
    857             {
    858                 data->pending_focus = PENDING_FOCUS_NONE;
    859                 data->pending_focus_time = 0;
    860                 X11_DispatchFocusIn(_this, data);
    861             }
    862             else
    863             {
    864                 data->pending_focus = PENDING_FOCUS_IN;
    865                 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
    866             }
    867             data->last_focus_event_time = SDL_GetTicks();
    868         }
    869         break;
    870 
    871         /* Losing input focus? */
    872     case FocusOut:{
    873             if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) {
    874                 /* Someone is handling a global hotkey, ignore it */
    875 #ifdef DEBUG_XEVENTS
    876                 printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data);
    877 #endif
    878                 break;
    879             }
    880             if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) {
    881                 /* We still have focus if a child gets focus. We also don't
    882                    care about the position of the pointer when the keyboard
    883                    focus changed. */
    884 #ifdef DEBUG_XEVENTS
    885                 printf("window %p: FocusOut (NotifyInferior/NotifyPointer, ignoring)\n", data);
    886 #endif
    887                 break;
    888             }
    889 #ifdef DEBUG_XEVENTS
    890             printf("window %p: FocusOut!\n", data);
    891 #endif
    892             if (!videodata->last_mode_change_deadline) /* no recent mode changes */
    893             {
    894                 data->pending_focus = PENDING_FOCUS_NONE;
    895                 data->pending_focus_time = 0;
    896                 X11_DispatchFocusOut(_this, data);
    897             }
    898             else
    899             {
    900                 data->pending_focus = PENDING_FOCUS_OUT;
    901                 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
    902             }
    903         }
    904         break;
    905 
    906         /* Key press? */
    907     case KeyPress:{
    908             KeyCode keycode = xevent.xkey.keycode;
    909             KeySym keysym = NoSymbol;
    910             char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
    911             Status status = 0;
    912             SDL_bool handled_by_ime = SDL_FALSE;
    913 
    914 #ifdef DEBUG_XEVENTS
    915             printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
    916 #endif
    917 #if 1
    918             if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
    919                 int min_keycode, max_keycode;
    920                 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
    921                 keysym = X11_KeyCodeToSym(_this, keycode, xevent.xkey.state >> 13);
    922                 fprintf(stderr,
    923                         "The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n",
    924                         keycode, keycode - min_keycode, keysym,
    925                         X11_XKeysymToString(keysym));
    926             }
    927 #endif
    928             /* */
    929             SDL_zeroa(text);
    930 #ifdef X_HAVE_UTF8_STRING
    931             if (data->ic) {
    932                 X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text),
    933                                   &keysym, &status);
    934             } else {
    935                 X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
    936             }
    937 #else
    938             X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
    939 #endif
    940 
    941 #ifdef SDL_USE_IME
    942             if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
    943                 handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode);
    944             }
    945 #endif
    946             if (!handled_by_ime) {
    947                 /* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */
    948                 if (xevent.xkey.keycode != videodata->filter_code || xevent.xkey.time != videodata->filter_time) {
    949                     SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]);
    950                 }
    951                 if(*text) {
    952                     SDL_SendKeyboardText(text);
    953                 }
    954             }
    955 
    956             X11_UpdateUserTime(data, xevent.xkey.time);
    957         }
    958         break;
    959 
    960         /* Key release? */
    961     case KeyRelease:{
    962             KeyCode keycode = xevent.xkey.keycode;
    963 
    964 #ifdef DEBUG_XEVENTS
    965             printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
    966 #endif
    967             if (X11_KeyRepeat(display, &xevent)) {
    968                 /* We're about to get a repeated key down, ignore the key up */
    969                 break;
    970             }
    971             SDL_SendKeyboardKey(SDL_RELEASED, videodata->key_layout[keycode]);
    972         }
    973         break;
    974 
    975         /* Have we been iconified? */
    976     case UnmapNotify:{
    977             XEvent ev;
    978 
    979 #ifdef DEBUG_XEVENTS
    980             printf("window %p: UnmapNotify!\n", data);
    981 #endif
    982 
    983             if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent.xunmap)) {
    984                 X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent.xunmap);
    985             } else {
    986                 X11_DispatchUnmapNotify(data);
    987             }
    988         }
    989         break;
    990 
    991         /* Have we been restored? */
    992     case MapNotify:{
    993 #ifdef DEBUG_XEVENTS
    994             printf("window %p: MapNotify!\n", data);
    995 #endif
    996             X11_DispatchMapNotify(data);
    997         }
    998         break;
    999 
   1000         /* Have we been resized or moved? */
   1001     case ConfigureNotify:{
   1002 #ifdef DEBUG_XEVENTS
   1003             printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data,
   1004                    xevent.xconfigure.x, xevent.xconfigure.y,
   1005                    xevent.xconfigure.width, xevent.xconfigure.height);
   1006 #endif
   1007             /* Real configure notify events are relative to the parent, synthetic events are absolute. */
   1008             if (!xevent.xconfigure.send_event) {
   1009                 unsigned int NumChildren;
   1010                 Window ChildReturn, Root, Parent;
   1011                 Window * Children;
   1012                 /* Translate these coodinates back to relative to root */
   1013                 X11_XQueryTree(data->videodata->display, xevent.xconfigure.window, &Root, &Parent, &Children, &NumChildren);
   1014                 X11_XTranslateCoordinates(xevent.xconfigure.display,
   1015                                         Parent, DefaultRootWindow(xevent.xconfigure.display),
   1016                                         xevent.xconfigure.x, xevent.xconfigure.y,
   1017                                         &xevent.xconfigure.x, &xevent.xconfigure.y,
   1018                                         &ChildReturn);
   1019             }
   1020                 
   1021             if (xevent.xconfigure.x != data->last_xconfigure.x ||
   1022                 xevent.xconfigure.y != data->last_xconfigure.y) {
   1023                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,
   1024                                     xevent.xconfigure.x, xevent.xconfigure.y);
   1025 #ifdef SDL_USE_IME
   1026                 if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
   1027                     /* Update IME candidate list position */
   1028                     SDL_IME_UpdateTextRect(NULL);
   1029                 }
   1030 #endif
   1031             }
   1032             if (xevent.xconfigure.width != data->last_xconfigure.width ||
   1033                 xevent.xconfigure.height != data->last_xconfigure.height) {
   1034                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED,
   1035                                     xevent.xconfigure.width,
   1036                                     xevent.xconfigure.height);
   1037             }
   1038             data->last_xconfigure = xevent.xconfigure;
   1039         }
   1040         break;
   1041 
   1042         /* Have we been requested to quit (or another client message?) */
   1043     case ClientMessage:{
   1044 
   1045             static int xdnd_version=0;
   1046 
   1047             if (xevent.xclient.message_type == videodata->XdndEnter) {
   1048 
   1049                 SDL_bool use_list = xevent.xclient.data.l[1] & 1;
   1050                 data->xdnd_source = xevent.xclient.data.l[0];
   1051                 xdnd_version = (xevent.xclient.data.l[1] >> 24);
   1052 #ifdef DEBUG_XEVENTS
   1053                 printf("XID of source window : %ld\n", data->xdnd_source);
   1054                 printf("Protocol version to use : %d\n", xdnd_version);
   1055                 printf("More then 3 data types : %d\n", (int) use_list);
   1056 #endif
   1057 
   1058                 if (use_list) {
   1059                     /* fetch conversion targets */
   1060                     SDL_x11Prop p;
   1061                     X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
   1062                     /* pick one */
   1063                     data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count);
   1064                     X11_XFree(p.data);
   1065                 } else {
   1066                     /* pick from list of three */
   1067                     data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
   1068                 }
   1069             }
   1070             else if (xevent.xclient.message_type == videodata->XdndPosition) {
   1071 
   1072 #ifdef DEBUG_XEVENTS
   1073                 Atom act= videodata->XdndActionCopy;
   1074                 if(xdnd_version >= 2) {
   1075                     act = xevent.xclient.data.l[4];
   1076                 }
   1077                 printf("Action requested by user is : %s\n", X11_XGetAtomName(display , act));
   1078 #endif
   1079 
   1080 
   1081                 /* reply with status */
   1082                 memset(&m, 0, sizeof(XClientMessageEvent));
   1083                 m.type = ClientMessage;
   1084                 m.display = xevent.xclient.display;
   1085                 m.window = xevent.xclient.data.l[0];
   1086                 m.message_type = videodata->XdndStatus;
   1087                 m.format=32;
   1088                 m.data.l[0] = data->xwindow;
   1089                 m.data.l[1] = (data->xdnd_req != None);
   1090                 m.data.l[2] = 0; /* specify an empty rectangle */
   1091                 m.data.l[3] = 0;
   1092                 m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
   1093 
   1094                 X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   1095                 X11_XFlush(display);
   1096             }
   1097             else if(xevent.xclient.message_type == videodata->XdndDrop) {
   1098                 if (data->xdnd_req == None) {
   1099                     /* say again - not interested! */
   1100                     memset(&m, 0, sizeof(XClientMessageEvent));
   1101                     m.type = ClientMessage;
   1102                     m.display = xevent.xclient.display;
   1103                     m.window = xevent.xclient.data.l[0];
   1104                     m.message_type = videodata->XdndFinished;
   1105                     m.format=32;
   1106                     m.data.l[0] = data->xwindow;
   1107                     m.data.l[1] = 0;
   1108                     m.data.l[2] = None; /* fail! */
   1109                     X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   1110                 } else {
   1111                     /* convert */
   1112                     if(xdnd_version >= 1) {
   1113                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]);
   1114                     } else {
   1115                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
   1116                     }
   1117                 }
   1118             }
   1119             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   1120                 (xevent.xclient.format == 32) &&
   1121                 (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) {
   1122                 Window root = DefaultRootWindow(display);
   1123 
   1124 #ifdef DEBUG_XEVENTS
   1125                 printf("window %p: _NET_WM_PING\n", data);
   1126 #endif
   1127                 xevent.xclient.window = root;
   1128                 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
   1129                 break;
   1130             }
   1131 
   1132             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   1133                 (xevent.xclient.format == 32) &&
   1134                 (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) {
   1135 
   1136 #ifdef DEBUG_XEVENTS
   1137                 printf("window %p: WM_DELETE_WINDOW\n", data);
   1138 #endif
   1139                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   1140                 break;
   1141             }
   1142             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   1143                 (xevent.xclient.format == 32) &&
   1144                 (xevent.xclient.data.l[0] == videodata->WM_TAKE_FOCUS)) {
   1145 
   1146 #ifdef DEBUG_XEVENTS
   1147                 printf("window %p: WM_TAKE_FOCUS\n", data);
   1148 #endif
   1149                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_TAKE_FOCUS, 0, 0);
   1150                 break;
   1151             }
   1152         }
   1153         break;
   1154 
   1155         /* Do we need to refresh ourselves? */
   1156     case Expose:{
   1157 #ifdef DEBUG_XEVENTS
   1158             printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count);
   1159 #endif
   1160             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
   1161         }
   1162         break;
   1163 
   1164     case MotionNotify:{
   1165             SDL_Mouse *mouse = SDL_GetMouse();
   1166             if(!mouse->relative_mode || mouse->relative_mode_warp) {
   1167 #ifdef DEBUG_MOTION
   1168                 printf("window %p: X11 motion: %d,%d\n", data, xevent.xmotion.x, xevent.xmotion.y);
   1169 #endif
   1170 
   1171                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y);
   1172             }
   1173         }
   1174         break;
   1175 
   1176     case ButtonPress:{
   1177             int xticks = 0, yticks = 0;
   1178 #ifdef DEBUG_XEVENTS
   1179             printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent.xbutton.button);
   1180 #endif
   1181             if (X11_IsWheelEvent(display,&xevent,&xticks, &yticks)) {
   1182                 SDL_SendMouseWheel(data->window, 0, (float) xticks, (float) yticks, SDL_MOUSEWHEEL_NORMAL);
   1183             } else {
   1184                 SDL_bool ignore_click = SDL_FALSE;
   1185                 int button = xevent.xbutton.button;
   1186                 if(button == Button1) {
   1187                     if (ProcessHitTest(_this, data, &xevent)) {
   1188                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
   1189                         break;  /* don't pass this event on to app. */
   1190                     }
   1191                 }
   1192                 else if(button > 7) {
   1193                     /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
   1194                        => subtract (8-SDL_BUTTON_X1) to get value SDL expects */
   1195                     button -= (8-SDL_BUTTON_X1);
   1196                 }
   1197                 if (data->last_focus_event_time) {
   1198                     const int X11_FOCUS_CLICK_TIMEOUT = 10;
   1199                     if (!SDL_TICKS_PASSED(SDL_GetTicks(), data->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
   1200                         ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
   1201                     }
   1202                     data->last_focus_event_time = 0;
   1203                 }
   1204                 if (!ignore_click) {
   1205                     SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button);
   1206                 }
   1207             }
   1208             X11_UpdateUserTime(data, xevent.xbutton.time);
   1209         }
   1210         break;
   1211 
   1212     case ButtonRelease:{
   1213             int button = xevent.xbutton.button;
   1214             /* The X server sends a Release event for each Press for wheels. Ignore them. */
   1215             int xticks = 0, yticks = 0;
   1216 #ifdef DEBUG_XEVENTS
   1217             printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent.xbutton.button);
   1218 #endif
   1219             if (!X11_IsWheelEvent(display, &xevent, &xticks, &yticks)) {
   1220                 if (button > 7) {
   1221                     /* see explanation at case ButtonPress */
   1222                     button -= (8-SDL_BUTTON_X1);
   1223                 }
   1224                 SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button);
   1225             }
   1226         }
   1227         break;
   1228 
   1229     case PropertyNotify:{
   1230 #ifdef DEBUG_XEVENTS
   1231             unsigned char *propdata;
   1232             int status, real_format;
   1233             Atom real_type;
   1234             unsigned long items_read, items_left;
   1235 
   1236             char *name = X11_XGetAtomName(display, xevent.xproperty.atom);
   1237             if (name) {
   1238                 printf("window %p: PropertyNotify: %s %s time=%lu\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent.xproperty.time);
   1239                 X11_XFree(name);
   1240             }
   1241 
   1242             status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
   1243             if (status == Success && items_read > 0) {
   1244                 if (real_type == XA_INTEGER) {
   1245                     int *values = (int *)propdata;
   1246 
   1247                     printf("{");
   1248                     for (i = 0; i < items_read; i++) {
   1249                         printf(" %d", values[i]);
   1250                     }
   1251                     printf(" }\n");
   1252                 } else if (real_type == XA_CARDINAL) {
   1253                     if (real_format == 32) {
   1254                         Uint32 *values = (Uint32 *)propdata;
   1255 
   1256                         printf("{");
   1257                         for (i = 0; i < items_read; i++) {
   1258                             printf(" %d", values[i]);
   1259                         }
   1260                         printf(" }\n");
   1261                     } else if (real_format == 16) {
   1262                         Uint16 *values = (Uint16 *)propdata;
   1263 
   1264                         printf("{");
   1265                         for (i = 0; i < items_read; i++) {
   1266                             printf(" %d", values[i]);
   1267                         }
   1268                         printf(" }\n");
   1269                     } else if (real_format == 8) {
   1270                         Uint8 *values = (Uint8 *)propdata;
   1271 
   1272                         printf("{");
   1273                         for (i = 0; i < items_read; i++) {
   1274                             printf(" %d", values[i]);
   1275                         }
   1276                         printf(" }\n");
   1277                     }
   1278                 } else if (real_type == XA_STRING ||
   1279                            real_type == videodata->UTF8_STRING) {
   1280                     printf("{ \"%s\" }\n", propdata);
   1281                 } else if (real_type == XA_ATOM) {
   1282                     Atom *atoms = (Atom *)propdata;
   1283 
   1284                     printf("{");
   1285                     for (i = 0; i < items_read; i++) {
   1286                         char *atomname = X11_XGetAtomName(display, atoms[i]);
   1287                         if (atomname) {
   1288                             printf(" %s", atomname);
   1289                             X11_XFree(atomname);
   1290                         }
   1291                     }
   1292                     printf(" }\n");
   1293                 } else {
   1294                     char *atomname = X11_XGetAtomName(display, real_type);
   1295                     printf("Unknown type: %ld (%s)\n", real_type, atomname ? atomname : "UNKNOWN");
   1296                     if (atomname) {
   1297                         X11_XFree(atomname);
   1298                     }
   1299                 }
   1300             }
   1301             if (status == Success) {
   1302                 X11_XFree(propdata);
   1303             }
   1304 #endif /* DEBUG_XEVENTS */
   1305 
   1306             /* Take advantage of this moment to make sure user_time has a
   1307                 valid timestamp from the X server, so if we later try to
   1308                 raise/restore this window, _NET_ACTIVE_WINDOW can have a
   1309                 non-zero timestamp, even if there's never been a mouse or
   1310                 key press to this window so far. Note that we don't try to
   1311                 set _NET_WM_USER_TIME here, though. That's only for legit
   1312                 user interaction with the window. */
   1313             if (!data->user_time) {
   1314                 data->user_time = xevent.xproperty.time;
   1315             }
   1316 
   1317             if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) {
   1318                 /* Get the new state from the window manager.
   1319                    Compositing window managers can alter visibility of windows
   1320                    without ever mapping / unmapping them, so we handle that here,
   1321                    because they use the NETWM protocol to notify us of changes.
   1322                  */
   1323                 const Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window);
   1324                 const Uint32 changed = flags ^ data->window->flags;
   1325 
   1326                 if ((changed & SDL_WINDOW_HIDDEN) || (changed & SDL_WINDOW_FULLSCREEN)) {
   1327                      if (flags & SDL_WINDOW_HIDDEN) {
   1328                          X11_DispatchUnmapNotify(data);
   1329                      } else {
   1330                          X11_DispatchMapNotify(data);
   1331                     }
   1332                 }
   1333 
   1334                 if (changed & SDL_WINDOW_MAXIMIZED) {
   1335                     if (flags & SDL_WINDOW_MAXIMIZED) {
   1336                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   1337                     } else {
   1338                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   1339                     }
   1340                 }
   1341             } else if (xevent.xproperty.atom == videodata->XKLAVIER_STATE) {
   1342                 /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
   1343                    events when the keyboard layout changes (for example,
   1344                    changing from English to French on the menubar's keyboard
   1345                    icon). Since it changes the XKLAVIER_STATE property, we
   1346                    notice and reinit our keymap here. This might not be the
   1347                    right approach, but it seems to work. */
   1348                 X11_UpdateKeymap(_this);
   1349                 SDL_SendKeymapChangedEvent();
   1350             } else if (xevent.xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
   1351                 Atom type;
   1352                 int format;
   1353                 unsigned long nitems, bytes_after;
   1354                 unsigned char *property;
   1355                 if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
   1356                     if (type != None && nitems == 4) {
   1357                         data->border_left = (int) ((long*)property)[0];
   1358                         data->border_right = (int) ((long*)property)[1];
   1359                         data->border_top = (int) ((long*)property)[2];
   1360                         data->border_bottom = (int) ((long*)property)[3];
   1361                     }
   1362                     X11_XFree(property);
   1363 
   1364                     #ifdef DEBUG_XEVENTS
   1365                     printf("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d\n", data->border_left, data->border_right, data->border_top, data->border_bottom);
   1366                     #endif
   1367                 }
   1368             }
   1369         }
   1370         break;
   1371 
   1372     case SelectionNotify: {
   1373             Atom target = xevent.xselection.target;
   1374 #ifdef DEBUG_XEVENTS
   1375             printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
   1376                 xevent.xselection.requestor, xevent.xselection.target);
   1377 #endif
   1378             if (target == data->xdnd_req) {
   1379                 /* read data */
   1380                 SDL_x11Prop p;
   1381                 X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);
   1382 
   1383                 if (p.format == 8) {
   1384                     char *saveptr = NULL;
   1385                     char *name = X11_XGetAtomName(display, target);
   1386                     if (name) {
   1387                         char *token = SDL_strtokr((char *) p.data, "\r\n", &saveptr);
   1388                         while (token != NULL) {
   1389                             if (SDL_strcmp("text/plain", name) == 0) {
   1390                                 SDL_SendDropText(data->window, token);
   1391                             } else if (SDL_strcmp("text/uri-list", name) == 0) {
   1392                                 char *fn = X11_URIToLocal(token);
   1393                                 if (fn) {
   1394                                     SDL_SendDropFile(data->window, fn);
   1395                                 }
   1396                             }
   1397                             token = SDL_strtokr(NULL, "\r\n", &saveptr);
   1398                         }
   1399                         X11_XFree(name);
   1400                     }
   1401                     SDL_SendDropComplete(data->window);
   1402                 }
   1403                 X11_XFree(p.data);
   1404 
   1405                 /* send reply */
   1406                 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
   1407                 m.type = ClientMessage;
   1408                 m.display = display;
   1409                 m.window = data->xdnd_source;
   1410                 m.message_type = videodata->XdndFinished;
   1411                 m.format = 32;
   1412                 m.data.l[0] = data->xwindow;
   1413                 m.data.l[1] = 1;
   1414                 m.data.l[2] = videodata->XdndActionCopy;
   1415                 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);
   1416 
   1417                 X11_XSync(display, False);
   1418             }
   1419         }
   1420         break;
   1421 
   1422     default:{
   1423 #ifdef DEBUG_XEVENTS
   1424             printf("window %p: Unhandled event %d\n", data, xevent.type);
   1425 #endif
   1426         }
   1427         break;
   1428     }
   1429 }
   1430 
   1431 static void
   1432 X11_HandleFocusChanges(_THIS)
   1433 {
   1434     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   1435     int i;
   1436 
   1437     if (videodata && videodata->windowlist) {
   1438         for (i = 0; i < videodata->numwindows; ++i) {
   1439             SDL_WindowData *data = videodata->windowlist[i];
   1440             if (data && data->pending_focus != PENDING_FOCUS_NONE) {
   1441                 Uint32 now = SDL_GetTicks();
   1442                 if (SDL_TICKS_PASSED(now, data->pending_focus_time)) {
   1443                     if (data->pending_focus == PENDING_FOCUS_IN) {
   1444                         X11_DispatchFocusIn(_this, data);
   1445                     } else {
   1446                         X11_DispatchFocusOut(_this, data);
   1447                     }
   1448                     data->pending_focus = PENDING_FOCUS_NONE;
   1449                 }
   1450             }
   1451         }
   1452     }
   1453 }
   1454 /* Ack!  X11_XPending() actually performs a blocking read if no events available */
   1455 static int
   1456 X11_Pending(Display * display)
   1457 {
   1458     /* Flush the display connection and look to see if events are queued */
   1459     X11_XFlush(display);
   1460     if (X11_XEventsQueued(display, QueuedAlready)) {
   1461         return (1);
   1462     }
   1463 
   1464     /* More drastic measures are required -- see if X is ready to talk */
   1465     if (SDL_IOReady(ConnectionNumber(display), SDL_FALSE, 0)) {
   1466         return (X11_XPending(display));
   1467     }
   1468 
   1469     /* Oh well, nothing is ready .. */
   1470     return (0);
   1471 }
   1472 
   1473 void
   1474 X11_PumpEvents(_THIS)
   1475 {
   1476     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   1477 
   1478     if (data->last_mode_change_deadline) {
   1479         if (SDL_TICKS_PASSED(SDL_GetTicks(), data->last_mode_change_deadline)) {
   1480             data->last_mode_change_deadline = 0;  /* assume we're done. */
   1481         }
   1482     }
   1483 
   1484     /* Update activity every 30 seconds to prevent screensaver */
   1485     if (_this->suspend_screensaver) {
   1486         const Uint32 now = SDL_GetTicks();
   1487         if (!data->screensaver_activity ||
   1488             SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
   1489             X11_XResetScreenSaver(data->display);
   1490 
   1491 #if SDL_USE_LIBDBUS
   1492             SDL_DBus_ScreensaverTickle();
   1493 #endif
   1494 
   1495             data->screensaver_activity = now;
   1496         }
   1497     }
   1498 
   1499     /* Keep processing pending events */
   1500     while (X11_Pending(data->display)) {
   1501         X11_DispatchEvent(_this);
   1502     }
   1503 
   1504 #ifdef SDL_USE_IME
   1505     if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
   1506         SDL_IME_PumpEvents();
   1507     }
   1508 #endif
   1509 
   1510     /* FIXME: Only need to do this when there are pending focus changes */
   1511     X11_HandleFocusChanges(_this);
   1512 }
   1513 
   1514 
   1515 void
   1516 X11_SuspendScreenSaver(_THIS)
   1517 {
   1518 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
   1519     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   1520     int dummy;
   1521     int major_version, minor_version;
   1522 #endif /* SDL_VIDEO_DRIVER_X11_XSCRNSAVER */
   1523 
   1524 #if SDL_USE_LIBDBUS
   1525     if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
   1526         return;
   1527     }
   1528 
   1529     if (_this->suspend_screensaver) {
   1530         SDL_DBus_ScreensaverTickle();
   1531     }
   1532 #endif
   1533 
   1534 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
   1535     if (SDL_X11_HAVE_XSS) {
   1536         /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */
   1537         if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
   1538             !X11_XScreenSaverQueryVersion(data->display,
   1539                                       &major_version, &minor_version) ||
   1540             major_version < 1 || (major_version == 1 && minor_version < 1)) {
   1541             return;
   1542         }
   1543 
   1544         X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
   1545         X11_XResetScreenSaver(data->display);
   1546     }
   1547 #endif
   1548 }
   1549 
   1550 #endif /* SDL_VIDEO_DRIVER_X11 */
   1551 
   1552 /* vi: set ts=4 sw=4 expandtab: */