src/video/x11/SDL_x11events.c
author Ozkan Sezer <sezeroz@gmail.com>
Wed, 31 Jul 2019 05:11:40 +0300
changeset 12968 0e3948762c96
parent 12734 328690dfbdfb
permissions -rw-r--r--
use SDL_zeroa at more places where the argument is an array.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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_SHOWN, 0, 0);
   437     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 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_FRONTEND_H)
   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) {
   822 #ifdef DEBUG_XEVENTS
   823                 printf("window %p: FocusIn (NotifierInferior, 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) {
   855                 /* We still have focus if a child gets focus */
   856 #ifdef DEBUG_XEVENTS
   857                 printf("window %p: FocusOut (NotifierInferior, ignoring)\n", data);
   858 #endif
   859                 break;
   860             }
   861 #ifdef DEBUG_XEVENTS
   862             printf("window %p: FocusOut!\n", data);
   863 #endif
   864             if (!videodata->last_mode_change_deadline) /* no recent mode changes */
   865             {
   866                 data->pending_focus = PENDING_FOCUS_NONE;
   867                 data->pending_focus_time = 0;
   868                 X11_DispatchFocusOut(_this, data);
   869             }
   870             else
   871             {
   872                 data->pending_focus = PENDING_FOCUS_OUT;
   873                 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
   874             }
   875         }
   876         break;
   877 
   878         /* Key press? */
   879     case KeyPress:{
   880             KeyCode keycode = xevent.xkey.keycode;
   881             KeySym keysym = NoSymbol;
   882             char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
   883             Status status = 0;
   884             SDL_bool handled_by_ime = SDL_FALSE;
   885 
   886 #ifdef DEBUG_XEVENTS
   887             printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
   888 #endif
   889 #if 1
   890             if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
   891                 int min_keycode, max_keycode;
   892                 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
   893                 keysym = X11_KeyCodeToSym(_this, keycode, xevent.xkey.state >> 13);
   894                 fprintf(stderr,
   895                         "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",
   896                         keycode, keycode - min_keycode, keysym,
   897                         X11_XKeysymToString(keysym));
   898             }
   899 #endif
   900             /* */
   901             SDL_zeroa(text);
   902 #ifdef X_HAVE_UTF8_STRING
   903             if (data->ic) {
   904                 X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text),
   905                                   &keysym, &status);
   906             } else {
   907                 X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
   908             }
   909 #else
   910             X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
   911 #endif
   912 
   913 #ifdef SDL_USE_IME
   914             if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
   915                 handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode);
   916             }
   917 #endif
   918             if (!handled_by_ime) {
   919                 /* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */
   920                 if (xevent.xkey.keycode != videodata->filter_code || xevent.xkey.time != videodata->filter_time) {
   921                     SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]);
   922                 }
   923                 if(*text) {
   924                     SDL_SendKeyboardText(text);
   925                 }
   926             }
   927 
   928             X11_UpdateUserTime(data, xevent.xkey.time);
   929         }
   930         break;
   931 
   932         /* Key release? */
   933     case KeyRelease:{
   934             KeyCode keycode = xevent.xkey.keycode;
   935 
   936 #ifdef DEBUG_XEVENTS
   937             printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
   938 #endif
   939             if (X11_KeyRepeat(display, &xevent)) {
   940                 /* We're about to get a repeated key down, ignore the key up */
   941                 break;
   942             }
   943             SDL_SendKeyboardKey(SDL_RELEASED, videodata->key_layout[keycode]);
   944         }
   945         break;
   946 
   947         /* Have we been iconified? */
   948     case UnmapNotify:{
   949 #ifdef DEBUG_XEVENTS
   950             printf("window %p: UnmapNotify!\n", data);
   951 #endif
   952             X11_DispatchUnmapNotify(data);
   953         }
   954         break;
   955 
   956         /* Have we been restored? */
   957     case MapNotify:{
   958 #ifdef DEBUG_XEVENTS
   959             printf("window %p: MapNotify!\n", data);
   960 #endif
   961             X11_DispatchMapNotify(data);
   962         }
   963         break;
   964 
   965         /* Have we been resized or moved? */
   966     case ConfigureNotify:{
   967 #ifdef DEBUG_XEVENTS
   968             printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data,
   969                    xevent.xconfigure.x, xevent.xconfigure.y,
   970                    xevent.xconfigure.width, xevent.xconfigure.height);
   971 #endif
   972             /* Real configure notify events are relative to the parent, synthetic events are absolute. */
   973             if (!xevent.xconfigure.send_event) {
   974                 unsigned int NumChildren;
   975                 Window ChildReturn, Root, Parent;
   976                 Window * Children;
   977                 /* Translate these coodinates back to relative to root */
   978                 X11_XQueryTree(data->videodata->display, xevent.xconfigure.window, &Root, &Parent, &Children, &NumChildren);
   979                 X11_XTranslateCoordinates(xevent.xconfigure.display,
   980                                         Parent, DefaultRootWindow(xevent.xconfigure.display),
   981                                         xevent.xconfigure.x, xevent.xconfigure.y,
   982                                         &xevent.xconfigure.x, &xevent.xconfigure.y,
   983                                         &ChildReturn);
   984             }
   985                 
   986             if (xevent.xconfigure.x != data->last_xconfigure.x ||
   987                 xevent.xconfigure.y != data->last_xconfigure.y) {
   988                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,
   989                                     xevent.xconfigure.x, xevent.xconfigure.y);
   990 #ifdef SDL_USE_IME
   991                 if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
   992                     /* Update IME candidate list position */
   993                     SDL_IME_UpdateTextRect(NULL);
   994                 }
   995 #endif
   996             }
   997             if (xevent.xconfigure.width != data->last_xconfigure.width ||
   998                 xevent.xconfigure.height != data->last_xconfigure.height) {
   999                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED,
  1000                                     xevent.xconfigure.width,
  1001                                     xevent.xconfigure.height);
  1002             }
  1003             data->last_xconfigure = xevent.xconfigure;
  1004         }
  1005         break;
  1006 
  1007         /* Have we been requested to quit (or another client message?) */
  1008     case ClientMessage:{
  1009 
  1010             static int xdnd_version=0;
  1011 
  1012             if (xevent.xclient.message_type == videodata->XdndEnter) {
  1013 
  1014                 SDL_bool use_list = xevent.xclient.data.l[1] & 1;
  1015                 data->xdnd_source = xevent.xclient.data.l[0];
  1016                 xdnd_version = (xevent.xclient.data.l[1] >> 24);
  1017 #ifdef DEBUG_XEVENTS
  1018                 printf("XID of source window : %ld\n", data->xdnd_source);
  1019                 printf("Protocol version to use : %d\n", xdnd_version);
  1020                 printf("More then 3 data types : %d\n", (int) use_list);
  1021 #endif
  1022 
  1023                 if (use_list) {
  1024                     /* fetch conversion targets */
  1025                     SDL_x11Prop p;
  1026                     X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
  1027                     /* pick one */
  1028                     data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count);
  1029                     X11_XFree(p.data);
  1030                 } else {
  1031                     /* pick from list of three */
  1032                     data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
  1033                 }
  1034             }
  1035             else if (xevent.xclient.message_type == videodata->XdndPosition) {
  1036 
  1037 #ifdef DEBUG_XEVENTS
  1038                 Atom act= videodata->XdndActionCopy;
  1039                 if(xdnd_version >= 2) {
  1040                     act = xevent.xclient.data.l[4];
  1041                 }
  1042                 printf("Action requested by user is : %s\n", X11_XGetAtomName(display , act));
  1043 #endif
  1044 
  1045 
  1046                 /* reply with status */
  1047                 memset(&m, 0, sizeof(XClientMessageEvent));
  1048                 m.type = ClientMessage;
  1049                 m.display = xevent.xclient.display;
  1050                 m.window = xevent.xclient.data.l[0];
  1051                 m.message_type = videodata->XdndStatus;
  1052                 m.format=32;
  1053                 m.data.l[0] = data->xwindow;
  1054                 m.data.l[1] = (data->xdnd_req != None);
  1055                 m.data.l[2] = 0; /* specify an empty rectangle */
  1056                 m.data.l[3] = 0;
  1057                 m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
  1058 
  1059                 X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
  1060                 X11_XFlush(display);
  1061             }
  1062             else if(xevent.xclient.message_type == videodata->XdndDrop) {
  1063                 if (data->xdnd_req == None) {
  1064                     /* say again - not interested! */
  1065                     memset(&m, 0, sizeof(XClientMessageEvent));
  1066                     m.type = ClientMessage;
  1067                     m.display = xevent.xclient.display;
  1068                     m.window = xevent.xclient.data.l[0];
  1069                     m.message_type = videodata->XdndFinished;
  1070                     m.format=32;
  1071                     m.data.l[0] = data->xwindow;
  1072                     m.data.l[1] = 0;
  1073                     m.data.l[2] = None; /* fail! */
  1074                     X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
  1075                 } else {
  1076                     /* convert */
  1077                     if(xdnd_version >= 1) {
  1078                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]);
  1079                     } else {
  1080                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
  1081                     }
  1082                 }
  1083             }
  1084             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
  1085                 (xevent.xclient.format == 32) &&
  1086                 (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) {
  1087                 Window root = DefaultRootWindow(display);
  1088 
  1089 #ifdef DEBUG_XEVENTS
  1090                 printf("window %p: _NET_WM_PING\n", data);
  1091 #endif
  1092                 xevent.xclient.window = root;
  1093                 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
  1094                 break;
  1095             }
  1096 
  1097             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
  1098                 (xevent.xclient.format == 32) &&
  1099                 (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) {
  1100 
  1101 #ifdef DEBUG_XEVENTS
  1102                 printf("window %p: WM_DELETE_WINDOW\n", data);
  1103 #endif
  1104                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
  1105                 break;
  1106             }
  1107             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
  1108                 (xevent.xclient.format == 32) &&
  1109                 (xevent.xclient.data.l[0] == videodata->WM_TAKE_FOCUS)) {
  1110 
  1111 #ifdef DEBUG_XEVENTS
  1112                 printf("window %p: WM_TAKE_FOCUS\n", data);
  1113 #endif
  1114                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_TAKE_FOCUS, 0, 0);
  1115                 break;
  1116             }
  1117         }
  1118         break;
  1119 
  1120         /* Do we need to refresh ourselves? */
  1121     case Expose:{
  1122 #ifdef DEBUG_XEVENTS
  1123             printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count);
  1124 #endif
  1125             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
  1126         }
  1127         break;
  1128 
  1129     case MotionNotify:{
  1130             SDL_Mouse *mouse = SDL_GetMouse();
  1131             if(!mouse->relative_mode || mouse->relative_mode_warp) {
  1132 #ifdef DEBUG_MOTION
  1133                 printf("window %p: X11 motion: %d,%d\n", data, xevent.xmotion.x, xevent.xmotion.y);
  1134 #endif
  1135 
  1136                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y);
  1137             }
  1138         }
  1139         break;
  1140 
  1141     case ButtonPress:{
  1142             int xticks = 0, yticks = 0;
  1143 #ifdef DEBUG_XEVENTS
  1144             printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent.xbutton.button);
  1145 #endif
  1146             if (X11_IsWheelEvent(display,&xevent,&xticks, &yticks)) {
  1147                 SDL_SendMouseWheel(data->window, 0, (float) xticks, (float) yticks, SDL_MOUSEWHEEL_NORMAL);
  1148             } else {
  1149                 SDL_bool ignore_click = SDL_FALSE;
  1150                 int button = xevent.xbutton.button;
  1151                 if(button == Button1) {
  1152                     if (ProcessHitTest(_this, data, &xevent)) {
  1153                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
  1154                         break;  /* don't pass this event on to app. */
  1155                     }
  1156                 }
  1157                 else if(button > 7) {
  1158                     /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
  1159                        => subtract (8-SDL_BUTTON_X1) to get value SDL expects */
  1160                     button -= (8-SDL_BUTTON_X1);
  1161                 }
  1162                 if (data->last_focus_event_time) {
  1163                     const int X11_FOCUS_CLICK_TIMEOUT = 10;
  1164                     if (!SDL_TICKS_PASSED(SDL_GetTicks(), data->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
  1165                         ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
  1166                     }
  1167                     data->last_focus_event_time = 0;
  1168                 }
  1169                 if (!ignore_click) {
  1170                     SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button);
  1171                 }
  1172             }
  1173             X11_UpdateUserTime(data, xevent.xbutton.time);
  1174         }
  1175         break;
  1176 
  1177     case ButtonRelease:{
  1178             int button = xevent.xbutton.button;
  1179             /* The X server sends a Release event for each Press for wheels. Ignore them. */
  1180             int xticks = 0, yticks = 0;
  1181 #ifdef DEBUG_XEVENTS
  1182             printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent.xbutton.button);
  1183 #endif
  1184             if (!X11_IsWheelEvent(display, &xevent, &xticks, &yticks)) {
  1185                 if (button > 7) {
  1186                     /* see explanation at case ButtonPress */
  1187                     button -= (8-SDL_BUTTON_X1);
  1188                 }
  1189                 SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button);
  1190             }
  1191         }
  1192         break;
  1193 
  1194     case PropertyNotify:{
  1195 #ifdef DEBUG_XEVENTS
  1196             unsigned char *propdata;
  1197             int status, real_format;
  1198             Atom real_type;
  1199             unsigned long items_read, items_left;
  1200 
  1201             char *name = X11_XGetAtomName(display, xevent.xproperty.atom);
  1202             if (name) {
  1203                 printf("window %p: PropertyNotify: %s %s time=%lu\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent.xproperty.time);
  1204                 X11_XFree(name);
  1205             }
  1206 
  1207             status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
  1208             if (status == Success && items_read > 0) {
  1209                 if (real_type == XA_INTEGER) {
  1210                     int *values = (int *)propdata;
  1211 
  1212                     printf("{");
  1213                     for (i = 0; i < items_read; i++) {
  1214                         printf(" %d", values[i]);
  1215                     }
  1216                     printf(" }\n");
  1217                 } else if (real_type == XA_CARDINAL) {
  1218                     if (real_format == 32) {
  1219                         Uint32 *values = (Uint32 *)propdata;
  1220 
  1221                         printf("{");
  1222                         for (i = 0; i < items_read; i++) {
  1223                             printf(" %d", values[i]);
  1224                         }
  1225                         printf(" }\n");
  1226                     } else if (real_format == 16) {
  1227                         Uint16 *values = (Uint16 *)propdata;
  1228 
  1229                         printf("{");
  1230                         for (i = 0; i < items_read; i++) {
  1231                             printf(" %d", values[i]);
  1232                         }
  1233                         printf(" }\n");
  1234                     } else if (real_format == 8) {
  1235                         Uint8 *values = (Uint8 *)propdata;
  1236 
  1237                         printf("{");
  1238                         for (i = 0; i < items_read; i++) {
  1239                             printf(" %d", values[i]);
  1240                         }
  1241                         printf(" }\n");
  1242                     }
  1243                 } else if (real_type == XA_STRING ||
  1244                            real_type == videodata->UTF8_STRING) {
  1245                     printf("{ \"%s\" }\n", propdata);
  1246                 } else if (real_type == XA_ATOM) {
  1247                     Atom *atoms = (Atom *)propdata;
  1248 
  1249                     printf("{");
  1250                     for (i = 0; i < items_read; i++) {
  1251                         char *atomname = X11_XGetAtomName(display, atoms[i]);
  1252                         if (atomname) {
  1253                             printf(" %s", atomname);
  1254                             X11_XFree(atomname);
  1255                         }
  1256                     }
  1257                     printf(" }\n");
  1258                 } else {
  1259                     char *atomname = X11_XGetAtomName(display, real_type);
  1260                     printf("Unknown type: %ld (%s)\n", real_type, atomname ? atomname : "UNKNOWN");
  1261                     if (atomname) {
  1262                         X11_XFree(atomname);
  1263                     }
  1264                 }
  1265             }
  1266             if (status == Success) {
  1267                 X11_XFree(propdata);
  1268             }
  1269 #endif /* DEBUG_XEVENTS */
  1270 
  1271             /* Take advantage of this moment to make sure user_time has a
  1272                 valid timestamp from the X server, so if we later try to
  1273                 raise/restore this window, _NET_ACTIVE_WINDOW can have a
  1274                 non-zero timestamp, even if there's never been a mouse or
  1275                 key press to this window so far. Note that we don't try to
  1276                 set _NET_WM_USER_TIME here, though. That's only for legit
  1277                 user interaction with the window. */
  1278             if (!data->user_time) {
  1279                 data->user_time = xevent.xproperty.time;
  1280             }
  1281 
  1282             if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) {
  1283                 /* Get the new state from the window manager.
  1284                    Compositing window managers can alter visibility of windows
  1285                    without ever mapping / unmapping them, so we handle that here,
  1286                    because they use the NETWM protocol to notify us of changes.
  1287                  */
  1288                 const Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window);
  1289                 const Uint32 changed = flags ^ data->window->flags;
  1290 
  1291                 if ((changed & SDL_WINDOW_HIDDEN) || (changed & SDL_WINDOW_FULLSCREEN)) {
  1292                      if (flags & SDL_WINDOW_HIDDEN) {
  1293                          X11_DispatchUnmapNotify(data);
  1294                      } else {
  1295                          X11_DispatchMapNotify(data);
  1296                     }
  1297                 }
  1298 
  1299                 if (changed & SDL_WINDOW_MAXIMIZED) {
  1300                     if (flags & SDL_WINDOW_MAXIMIZED) {
  1301                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
  1302                     } else {
  1303                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
  1304                     }
  1305                 }
  1306             } else if (xevent.xproperty.atom == videodata->XKLAVIER_STATE) {
  1307                 /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
  1308                    events when the keyboard layout changes (for example,
  1309                    changing from English to French on the menubar's keyboard
  1310                    icon). Since it changes the XKLAVIER_STATE property, we
  1311                    notice and reinit our keymap here. This might not be the
  1312                    right approach, but it seems to work. */
  1313                 X11_UpdateKeymap(_this);
  1314                 SDL_SendKeymapChangedEvent();
  1315             } else if (xevent.xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
  1316                 Atom type;
  1317                 int format;
  1318                 unsigned long nitems, bytes_after;
  1319                 unsigned char *property;
  1320                 if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
  1321                     if (type != None && nitems == 4) {
  1322                         data->border_left = (int) ((long*)property)[0];
  1323                         data->border_right = (int) ((long*)property)[1];
  1324                         data->border_top = (int) ((long*)property)[2];
  1325                         data->border_bottom = (int) ((long*)property)[3];
  1326                     }
  1327                     X11_XFree(property);
  1328 
  1329                     #ifdef DEBUG_XEVENTS
  1330                     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);
  1331                     #endif
  1332                 }
  1333             }
  1334         }
  1335         break;
  1336 
  1337     case SelectionNotify: {
  1338             Atom target = xevent.xselection.target;
  1339 #ifdef DEBUG_XEVENTS
  1340             printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
  1341                 xevent.xselection.requestor, xevent.xselection.target);
  1342 #endif
  1343             if (target == data->xdnd_req) {
  1344                 /* read data */
  1345                 SDL_x11Prop p;
  1346                 X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);
  1347 
  1348                 if (p.format == 8) {
  1349                     /* !!! FIXME: don't use strtok here. It's not reentrant and not in SDL_stdinc. */
  1350                     char* name = X11_XGetAtomName(display, target);
  1351                     char *token = strtok((char *) p.data, "\r\n");
  1352                     while (token != NULL) {
  1353                         if (SDL_strcmp("text/plain", name)==0) {
  1354                             SDL_SendDropText(data->window, token);
  1355                         } else if (SDL_strcmp("text/uri-list", name)==0) {
  1356                             char *fn = X11_URIToLocal(token);
  1357                             if (fn) {
  1358                                 SDL_SendDropFile(data->window, fn);
  1359                             }
  1360                         }
  1361                         token = strtok(NULL, "\r\n");
  1362                     }
  1363                     SDL_SendDropComplete(data->window);
  1364                 }
  1365                 X11_XFree(p.data);
  1366 
  1367                 /* send reply */
  1368                 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
  1369                 m.type = ClientMessage;
  1370                 m.display = display;
  1371                 m.window = data->xdnd_source;
  1372                 m.message_type = videodata->XdndFinished;
  1373                 m.format = 32;
  1374                 m.data.l[0] = data->xwindow;
  1375                 m.data.l[1] = 1;
  1376                 m.data.l[2] = videodata->XdndActionCopy;
  1377                 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);
  1378 
  1379                 X11_XSync(display, False);
  1380             }
  1381         }
  1382         break;
  1383 
  1384     default:{
  1385 #ifdef DEBUG_XEVENTS
  1386             printf("window %p: Unhandled event %d\n", data, xevent.type);
  1387 #endif
  1388         }
  1389         break;
  1390     }
  1391 }
  1392 
  1393 static void
  1394 X11_HandleFocusChanges(_THIS)
  1395 {
  1396     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1397     int i;
  1398 
  1399     if (videodata && videodata->windowlist) {
  1400         for (i = 0; i < videodata->numwindows; ++i) {
  1401             SDL_WindowData *data = videodata->windowlist[i];
  1402             if (data && data->pending_focus != PENDING_FOCUS_NONE) {
  1403                 Uint32 now = SDL_GetTicks();
  1404                 if (SDL_TICKS_PASSED(now, data->pending_focus_time)) {
  1405                     if (data->pending_focus == PENDING_FOCUS_IN) {
  1406                         X11_DispatchFocusIn(_this, data);
  1407                     } else {
  1408                         X11_DispatchFocusOut(_this, data);
  1409                     }
  1410                     data->pending_focus = PENDING_FOCUS_NONE;
  1411                 }
  1412             }
  1413         }
  1414     }
  1415 }
  1416 /* Ack!  X11_XPending() actually performs a blocking read if no events available */
  1417 static int
  1418 X11_Pending(Display * display)
  1419 {
  1420     /* Flush the display connection and look to see if events are queued */
  1421     X11_XFlush(display);
  1422     if (X11_XEventsQueued(display, QueuedAlready)) {
  1423         return (1);
  1424     }
  1425 
  1426     /* More drastic measures are required -- see if X is ready to talk */
  1427     if (SDL_IOReady(ConnectionNumber(display), SDL_FALSE, 0)) {
  1428         return (X11_XPending(display));
  1429     }
  1430 
  1431     /* Oh well, nothing is ready .. */
  1432     return (0);
  1433 }
  1434 
  1435 void
  1436 X11_PumpEvents(_THIS)
  1437 {
  1438     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
  1439 
  1440     if (data->last_mode_change_deadline) {
  1441         if (SDL_TICKS_PASSED(SDL_GetTicks(), data->last_mode_change_deadline)) {
  1442             data->last_mode_change_deadline = 0;  /* assume we're done. */
  1443         }
  1444     }
  1445 
  1446     /* Update activity every 30 seconds to prevent screensaver */
  1447     if (_this->suspend_screensaver) {
  1448         const Uint32 now = SDL_GetTicks();
  1449         if (!data->screensaver_activity ||
  1450             SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
  1451             X11_XResetScreenSaver(data->display);
  1452 
  1453 #if SDL_USE_LIBDBUS
  1454             SDL_DBus_ScreensaverTickle();
  1455 #endif
  1456 
  1457             data->screensaver_activity = now;
  1458         }
  1459     }
  1460 
  1461     /* Keep processing pending events */
  1462     while (X11_Pending(data->display)) {
  1463         X11_DispatchEvent(_this);
  1464     }
  1465 
  1466 #ifdef SDL_USE_IME
  1467     if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
  1468         SDL_IME_PumpEvents();
  1469     }
  1470 #endif
  1471 
  1472     /* FIXME: Only need to do this when there are pending focus changes */
  1473     X11_HandleFocusChanges(_this);
  1474 }
  1475 
  1476 
  1477 void
  1478 X11_SuspendScreenSaver(_THIS)
  1479 {
  1480 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
  1481     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
  1482     int dummy;
  1483     int major_version, minor_version;
  1484 #endif /* SDL_VIDEO_DRIVER_X11_XSCRNSAVER */
  1485 
  1486 #if SDL_USE_LIBDBUS
  1487     if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
  1488         return;
  1489     }
  1490 
  1491     if (_this->suspend_screensaver) {
  1492         SDL_DBus_ScreensaverTickle();
  1493     }
  1494 #endif
  1495 
  1496 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
  1497     if (SDL_X11_HAVE_XSS) {
  1498         /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */
  1499         if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
  1500             !X11_XScreenSaverQueryVersion(data->display,
  1501                                       &major_version, &minor_version) ||
  1502             major_version < 1 || (major_version == 1 && minor_version < 1)) {
  1503             return;
  1504         }
  1505 
  1506         X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
  1507         X11_XResetScreenSaver(data->display);
  1508     }
  1509 #endif
  1510 }
  1511 
  1512 #endif /* SDL_VIDEO_DRIVER_X11 */
  1513 
  1514 /* vi: set ts=4 sw=4 expandtab: */