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