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