1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_VIDEO_DRIVER_X11
24 
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <signal.h>
28 #include <unistd.h>
29 #include <limits.h> /* For INT_MAX */
30 
31 #include "SDL_x11video.h"
32 #include "SDL_x11touch.h"
33 #include "SDL_x11xinput2.h"
34 #include "../../core/unix/SDL_poll.h"
35 #include "../../events/SDL_events_c.h"
36 #include "../../events/SDL_mouse_c.h"
37 #include "../../events/SDL_touch_c.h"
38 
39 #include "SDL_hints.h"
40 #include "SDL_timer.h"
41 #include "SDL_syswm.h"
42 #include "SDL_assert.h"
43 
44 #include <stdio.h>
45 
46 /*#define DEBUG_XEVENTS*/
47 
48 #ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT
49 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
50 #endif
51 
52 #ifndef _NET_WM_MOVERESIZE_SIZE_TOP
53 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
54 #endif
55 
56 #ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT
57 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
58 #endif
59 
60 #ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT
61 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
62 #endif
63 
64 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
65 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
66 #endif
67 
68 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM
69 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
70 #endif
71 
72 #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
73 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
74 #endif
75 
76 #ifndef _NET_WM_MOVERESIZE_SIZE_LEFT
77 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
78 #endif
79 
80 #ifndef _NET_WM_MOVERESIZE_MOVE
81 #define _NET_WM_MOVERESIZE_MOVE              8
82 #endif
83 
84 typedef struct {
85     unsigned char *data;
86     int format, count;
87     Atom type;
88 } SDL_x11Prop;
89 
90 /* Reads property
91    Must call X11_XFree on results
92  */
X11_ReadProperty(SDL_x11Prop * p,Display * disp,Window w,Atom prop)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 */
X11_PickTarget(Display * disp,Atom list[],int list_count)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 */
X11_PickTargetFromAtoms(Display * disp,Atom a0,Atom a1,Atom a2)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 
X11_KeyRepeatCheckIfEvent(Display * display,XEvent * chkev,XPointer arg)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  */
X11_KeyRepeat(Display * display,XEvent * event)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
X11_IsWheelEvent(Display * display,XEvent * event,int * xticks,int * yticks)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  */
X11_URIDecode(char * buf,int len)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 */
X11_URIToLocal(char * uri)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
X11_HandleGenericEvent(SDL_VideoData * videodata,XEvent * xev)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
X11_GetNumLockModifierMask(_THIS)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
X11_ReconcileKeyboardState(_THIS)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
X11_DispatchFocusIn(_THIS,SDL_WindowData * data)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
X11_DispatchFocusOut(_THIS,SDL_WindowData * data)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
X11_DispatchMapNotify(SDL_WindowData * data)434 X11_DispatchMapNotify(SDL_WindowData *data)
435 {
436     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
437     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
438 }
439 
440 static void
X11_DispatchUnmapNotify(SDL_WindowData * data)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
InitiateWindowMove(_THIS,const SDL_WindowData * data,const SDL_Point * point)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
InitiateWindowResize(_THIS,const SDL_WindowData * data,const SDL_Point * point,int direction)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
ProcessHitTest(_THIS,const SDL_WindowData * data,const XEvent * xev)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
X11_UpdateUserTime(SDL_WindowData * data,const unsigned long latest)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
X11_HandleClipboardEvent(_THIS,const XEvent * xevent)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
X11_DispatchEvent(_THIS)647 X11_DispatchEvent(_THIS)
648 {
649     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
650     Display *display;
651     SDL_WindowData *data;
652     XEvent xevent;
653     int orig_event_type;
654     KeyCode orig_keycode;
655     XClientMessageEvent m;
656     int i;
657 
658     if (!videodata) {
659         return;
660     }
661     display = videodata->display;
662 
663     SDL_zero(xevent);           /* valgrind fix. --ryan. */
664     X11_XNextEvent(display, &xevent);
665 
666     /* Save the original keycode for dead keys, which are filtered out by
667        the XFilterEvent() call below.
668     */
669     orig_event_type = xevent.type;
670     if (orig_event_type == KeyPress || orig_event_type == KeyRelease) {
671         orig_keycode = xevent.xkey.keycode;
672     } else {
673         orig_keycode = 0;
674     }
675 
676     /* filter events catchs XIM events and sends them to the correct handler */
677     if (X11_XFilterEvent(&xevent, None) == True) {
678 #if 0
679         printf("Filtered event type = %d display = %d window = %d\n",
680                xevent.type, xevent.xany.display, xevent.xany.window);
681 #endif
682         /* Make sure dead key press/release events are sent */
683         /* But only if we're using one of the DBus IMEs, otherwise
684            some XIM IMEs will generate duplicate events */
685         if (orig_keycode) {
686 #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX)
687             SDL_Scancode scancode = videodata->key_layout[orig_keycode];
688             videodata->filter_code = orig_keycode;
689             videodata->filter_time = xevent.xkey.time;
690 
691             if (orig_event_type == KeyPress) {
692                 SDL_SendKeyboardKey(SDL_PRESSED, scancode);
693             } else {
694                 SDL_SendKeyboardKey(SDL_RELEASED, scancode);
695             }
696 #endif
697         }
698         return;
699     }
700 
701 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
702     if(xevent.type == GenericEvent) {
703         X11_HandleGenericEvent(videodata, &xevent);
704         return;
705     }
706 #endif
707 
708     /* Send a SDL_SYSWMEVENT if the application wants them */
709     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
710         SDL_SysWMmsg wmmsg;
711 
712         SDL_VERSION(&wmmsg.version);
713         wmmsg.subsystem = SDL_SYSWM_X11;
714         wmmsg.msg.x11.event = xevent;
715         SDL_SendSysWMEvent(&wmmsg);
716     }
717 
718 #if 0
719     printf("type = %d display = %d window = %d\n",
720            xevent.type, xevent.xany.display, xevent.xany.window);
721 #endif
722 
723     if ((videodata->clipboard_window != None) &&
724         (videodata->clipboard_window == xevent.xany.window)) {
725         X11_HandleClipboardEvent(_this, &xevent);
726         return;
727     }
728 
729     data = NULL;
730     if (videodata && videodata->windowlist) {
731         for (i = 0; i < videodata->numwindows; ++i) {
732             if ((videodata->windowlist[i] != NULL) &&
733                 (videodata->windowlist[i]->xwindow == xevent.xany.window)) {
734                 data = videodata->windowlist[i];
735                 break;
736             }
737         }
738     }
739     if (!data) {
740         /* The window for KeymapNotify, etc events is 0 */
741         if (xevent.type == KeymapNotify) {
742             if (SDL_GetKeyboardFocus() != NULL) {
743                 X11_ReconcileKeyboardState(_this);
744             }
745         } else if (xevent.type == MappingNotify) {
746             /* Has the keyboard layout changed? */
747             const int request = xevent.xmapping.request;
748 
749 #ifdef DEBUG_XEVENTS
750             printf("window %p: MappingNotify!\n", data);
751 #endif
752             if ((request == MappingKeyboard) || (request == MappingModifier)) {
753                 X11_XRefreshKeyboardMapping(&xevent.xmapping);
754             }
755 
756             X11_UpdateKeymap(_this);
757             SDL_SendKeymapChangedEvent();
758         }
759         return;
760     }
761 
762     switch (xevent.type) {
763 
764         /* Gaining mouse coverage? */
765     case EnterNotify:{
766             SDL_Mouse *mouse = SDL_GetMouse();
767 #ifdef DEBUG_XEVENTS
768             printf("window %p: EnterNotify! (%d,%d,%d)\n", data,
769                    xevent.xcrossing.x,
770                    xevent.xcrossing.y,
771                    xevent.xcrossing.mode);
772             if (xevent.xcrossing.mode == NotifyGrab)
773                 printf("Mode: NotifyGrab\n");
774             if (xevent.xcrossing.mode == NotifyUngrab)
775                 printf("Mode: NotifyUngrab\n");
776 #endif
777             SDL_SetMouseFocus(data->window);
778 
779             mouse->last_x = xevent.xcrossing.x;
780             mouse->last_y = xevent.xcrossing.y;
781 
782             if (!mouse->relative_mode) {
783                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y);
784             }
785         }
786         break;
787         /* Losing mouse coverage? */
788     case LeaveNotify:{
789 #ifdef DEBUG_XEVENTS
790             printf("window %p: LeaveNotify! (%d,%d,%d)\n", data,
791                    xevent.xcrossing.x,
792                    xevent.xcrossing.y,
793                    xevent.xcrossing.mode);
794             if (xevent.xcrossing.mode == NotifyGrab)
795                 printf("Mode: NotifyGrab\n");
796             if (xevent.xcrossing.mode == NotifyUngrab)
797                 printf("Mode: NotifyUngrab\n");
798 #endif
799             if (!SDL_GetMouse()->relative_mode) {
800                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y);
801             }
802 
803             if (xevent.xcrossing.mode != NotifyGrab &&
804                 xevent.xcrossing.mode != NotifyUngrab &&
805                 xevent.xcrossing.detail != NotifyInferior) {
806                 SDL_SetMouseFocus(NULL);
807             }
808         }
809         break;
810 
811         /* Gaining input focus? */
812     case FocusIn:{
813             if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) {
814                 /* Someone is handling a global hotkey, ignore it */
815 #ifdef DEBUG_XEVENTS
816                 printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data);
817 #endif
818                 break;
819             }
820 
821             if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) {
822 #ifdef DEBUG_XEVENTS
823                 printf("window %p: FocusIn (NotifyInferior/NotifyPointer, ignoring)\n", data);
824 #endif
825                 break;
826             }
827 #ifdef DEBUG_XEVENTS
828             printf("window %p: FocusIn!\n", data);
829 #endif
830             if (!videodata->last_mode_change_deadline) /* no recent mode changes */
831             {
832                 data->pending_focus = PENDING_FOCUS_NONE;
833                 data->pending_focus_time = 0;
834                 X11_DispatchFocusIn(_this, data);
835             }
836             else
837             {
838                 data->pending_focus = PENDING_FOCUS_IN;
839                 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
840             }
841             data->last_focus_event_time = SDL_GetTicks();
842         }
843         break;
844 
845         /* Losing input focus? */
846     case FocusOut:{
847             if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) {
848                 /* Someone is handling a global hotkey, ignore it */
849 #ifdef DEBUG_XEVENTS
850                 printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data);
851 #endif
852                 break;
853             }
854             if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) {
855                 /* We still have focus if a child gets focus. We also don't
856                    care about the position of the pointer when the keyboard
857                    focus changed. */
858 #ifdef DEBUG_XEVENTS
859                 printf("window %p: FocusOut (NotifyInferior/NotifyPointer, ignoring)\n", data);
860 #endif
861                 break;
862             }
863 #ifdef DEBUG_XEVENTS
864             printf("window %p: FocusOut!\n", data);
865 #endif
866             if (!videodata->last_mode_change_deadline) /* no recent mode changes */
867             {
868                 data->pending_focus = PENDING_FOCUS_NONE;
869                 data->pending_focus_time = 0;
870                 X11_DispatchFocusOut(_this, data);
871             }
872             else
873             {
874                 data->pending_focus = PENDING_FOCUS_OUT;
875                 data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
876             }
877         }
878         break;
879 
880         /* Key press? */
881     case KeyPress:{
882             KeyCode keycode = xevent.xkey.keycode;
883             KeySym keysym = NoSymbol;
884             char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
885             Status status = 0;
886             SDL_bool handled_by_ime = SDL_FALSE;
887 
888 #ifdef DEBUG_XEVENTS
889             printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
890 #endif
891 #if 1
892             if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
893                 int min_keycode, max_keycode;
894                 X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
895                 keysym = X11_KeyCodeToSym(_this, keycode, xevent.xkey.state >> 13);
896                 fprintf(stderr,
897                         "The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n",
898                         keycode, keycode - min_keycode, keysym,
899                         X11_XKeysymToString(keysym));
900             }
901 #endif
902             /* */
903             SDL_zeroa(text);
904 #ifdef X_HAVE_UTF8_STRING
905             if (data->ic) {
906                 X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text),
907                                   &keysym, &status);
908             } else {
909                 X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
910             }
911 #else
912             X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
913 #endif
914 
915 #ifdef SDL_USE_IME
916             if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
917                 handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode);
918             }
919 #endif
920             if (!handled_by_ime) {
921                 /* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */
922                 if (xevent.xkey.keycode != videodata->filter_code || xevent.xkey.time != videodata->filter_time) {
923                     SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]);
924                 }
925                 if(*text) {
926                     SDL_SendKeyboardText(text);
927                 }
928             }
929 
930             X11_UpdateUserTime(data, xevent.xkey.time);
931         }
932         break;
933 
934         /* Key release? */
935     case KeyRelease:{
936             KeyCode keycode = xevent.xkey.keycode;
937 
938 #ifdef DEBUG_XEVENTS
939             printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode);
940 #endif
941             if (X11_KeyRepeat(display, &xevent)) {
942                 /* We're about to get a repeated key down, ignore the key up */
943                 break;
944             }
945             SDL_SendKeyboardKey(SDL_RELEASED, videodata->key_layout[keycode]);
946         }
947         break;
948 
949         /* Have we been iconified? */
950     case UnmapNotify:{
951 #ifdef DEBUG_XEVENTS
952             printf("window %p: UnmapNotify!\n", data);
953 #endif
954             X11_DispatchUnmapNotify(data);
955         }
956         break;
957 
958         /* Have we been restored? */
959     case MapNotify:{
960 #ifdef DEBUG_XEVENTS
961             printf("window %p: MapNotify!\n", data);
962 #endif
963             X11_DispatchMapNotify(data);
964         }
965         break;
966 
967         /* Have we been resized or moved? */
968     case ConfigureNotify:{
969 #ifdef DEBUG_XEVENTS
970             printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data,
971                    xevent.xconfigure.x, xevent.xconfigure.y,
972                    xevent.xconfigure.width, xevent.xconfigure.height);
973 #endif
974             /* Real configure notify events are relative to the parent, synthetic events are absolute. */
975             if (!xevent.xconfigure.send_event) {
976                 unsigned int NumChildren;
977                 Window ChildReturn, Root, Parent;
978                 Window * Children;
979                 /* Translate these coodinates back to relative to root */
980                 X11_XQueryTree(data->videodata->display, xevent.xconfigure.window, &Root, &Parent, &Children, &NumChildren);
981                 X11_XTranslateCoordinates(xevent.xconfigure.display,
982                                         Parent, DefaultRootWindow(xevent.xconfigure.display),
983                                         xevent.xconfigure.x, xevent.xconfigure.y,
984                                         &xevent.xconfigure.x, &xevent.xconfigure.y,
985                                         &ChildReturn);
986             }
987 
988             if (xevent.xconfigure.x != data->last_xconfigure.x ||
989                 xevent.xconfigure.y != data->last_xconfigure.y) {
990                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,
991                                     xevent.xconfigure.x, xevent.xconfigure.y);
992 #ifdef SDL_USE_IME
993                 if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
994                     /* Update IME candidate list position */
995                     SDL_IME_UpdateTextRect(NULL);
996                 }
997 #endif
998             }
999             if (xevent.xconfigure.width != data->last_xconfigure.width ||
1000                 xevent.xconfigure.height != data->last_xconfigure.height) {
1001                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED,
1002                                     xevent.xconfigure.width,
1003                                     xevent.xconfigure.height);
1004             }
1005             data->last_xconfigure = xevent.xconfigure;
1006         }
1007         break;
1008 
1009         /* Have we been requested to quit (or another client message?) */
1010     case ClientMessage:{
1011 
1012             static int xdnd_version=0;
1013 
1014             if (xevent.xclient.message_type == videodata->XdndEnter) {
1015 
1016                 SDL_bool use_list = xevent.xclient.data.l[1] & 1;
1017                 data->xdnd_source = xevent.xclient.data.l[0];
1018                 xdnd_version = (xevent.xclient.data.l[1] >> 24);
1019 #ifdef DEBUG_XEVENTS
1020                 printf("XID of source window : %ld\n", data->xdnd_source);
1021                 printf("Protocol version to use : %d\n", xdnd_version);
1022                 printf("More then 3 data types : %d\n", (int) use_list);
1023 #endif
1024 
1025                 if (use_list) {
1026                     /* fetch conversion targets */
1027                     SDL_x11Prop p;
1028                     X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
1029                     /* pick one */
1030                     data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count);
1031                     X11_XFree(p.data);
1032                 } else {
1033                     /* pick from list of three */
1034                     data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
1035                 }
1036             }
1037             else if (xevent.xclient.message_type == videodata->XdndPosition) {
1038 
1039 #ifdef DEBUG_XEVENTS
1040                 Atom act= videodata->XdndActionCopy;
1041                 if(xdnd_version >= 2) {
1042                     act = xevent.xclient.data.l[4];
1043                 }
1044                 printf("Action requested by user is : %s\n", X11_XGetAtomName(display , act));
1045 #endif
1046 
1047 
1048                 /* reply with status */
1049                 memset(&m, 0, sizeof(XClientMessageEvent));
1050                 m.type = ClientMessage;
1051                 m.display = xevent.xclient.display;
1052                 m.window = xevent.xclient.data.l[0];
1053                 m.message_type = videodata->XdndStatus;
1054                 m.format=32;
1055                 m.data.l[0] = data->xwindow;
1056                 m.data.l[1] = (data->xdnd_req != None);
1057                 m.data.l[2] = 0; /* specify an empty rectangle */
1058                 m.data.l[3] = 0;
1059                 m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
1060 
1061                 X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
1062                 X11_XFlush(display);
1063             }
1064             else if(xevent.xclient.message_type == videodata->XdndDrop) {
1065                 if (data->xdnd_req == None) {
1066                     /* say again - not interested! */
1067                     memset(&m, 0, sizeof(XClientMessageEvent));
1068                     m.type = ClientMessage;
1069                     m.display = xevent.xclient.display;
1070                     m.window = xevent.xclient.data.l[0];
1071                     m.message_type = videodata->XdndFinished;
1072                     m.format=32;
1073                     m.data.l[0] = data->xwindow;
1074                     m.data.l[1] = 0;
1075                     m.data.l[2] = None; /* fail! */
1076                     X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
1077                 } else {
1078                     /* convert */
1079                     if(xdnd_version >= 1) {
1080                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]);
1081                     } else {
1082                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
1083                     }
1084                 }
1085             }
1086             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
1087                 (xevent.xclient.format == 32) &&
1088                 (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) {
1089                 Window root = DefaultRootWindow(display);
1090 
1091 #ifdef DEBUG_XEVENTS
1092                 printf("window %p: _NET_WM_PING\n", data);
1093 #endif
1094                 xevent.xclient.window = root;
1095                 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
1096                 break;
1097             }
1098 
1099             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
1100                 (xevent.xclient.format == 32) &&
1101                 (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) {
1102 
1103 #ifdef DEBUG_XEVENTS
1104                 printf("window %p: WM_DELETE_WINDOW\n", data);
1105 #endif
1106                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
1107                 break;
1108             }
1109             else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
1110                 (xevent.xclient.format == 32) &&
1111                 (xevent.xclient.data.l[0] == videodata->WM_TAKE_FOCUS)) {
1112 
1113 #ifdef DEBUG_XEVENTS
1114                 printf("window %p: WM_TAKE_FOCUS\n", data);
1115 #endif
1116                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_TAKE_FOCUS, 0, 0);
1117                 break;
1118             }
1119         }
1120         break;
1121 
1122         /* Do we need to refresh ourselves? */
1123     case Expose:{
1124 #ifdef DEBUG_XEVENTS
1125             printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count);
1126 #endif
1127             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
1128         }
1129         break;
1130 
1131     case MotionNotify:{
1132             SDL_Mouse *mouse = SDL_GetMouse();
1133             if(!mouse->relative_mode || mouse->relative_mode_warp) {
1134 #ifdef DEBUG_MOTION
1135                 printf("window %p: X11 motion: %d,%d\n", data, xevent.xmotion.x, xevent.xmotion.y);
1136 #endif
1137 
1138                 SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y);
1139             }
1140         }
1141         break;
1142 
1143     case ButtonPress:{
1144             int xticks = 0, yticks = 0;
1145 #ifdef DEBUG_XEVENTS
1146             printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent.xbutton.button);
1147 #endif
1148             if (X11_IsWheelEvent(display,&xevent,&xticks, &yticks)) {
1149                 SDL_SendMouseWheel(data->window, 0, (float) xticks, (float) yticks, SDL_MOUSEWHEEL_NORMAL);
1150             } else {
1151                 SDL_bool ignore_click = SDL_FALSE;
1152                 int button = xevent.xbutton.button;
1153                 if(button == Button1) {
1154                     if (ProcessHitTest(_this, data, &xevent)) {
1155                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
1156                         break;  /* don't pass this event on to app. */
1157                     }
1158                 }
1159                 else if(button > 7) {
1160                     /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
1161                        => subtract (8-SDL_BUTTON_X1) to get value SDL expects */
1162                     button -= (8-SDL_BUTTON_X1);
1163                 }
1164                 if (data->last_focus_event_time) {
1165                     const int X11_FOCUS_CLICK_TIMEOUT = 10;
1166                     if (!SDL_TICKS_PASSED(SDL_GetTicks(), data->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
1167                         ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
1168                     }
1169                     data->last_focus_event_time = 0;
1170                 }
1171                 if (!ignore_click) {
1172                     SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button);
1173                 }
1174             }
1175             X11_UpdateUserTime(data, xevent.xbutton.time);
1176         }
1177         break;
1178 
1179     case ButtonRelease:{
1180             int button = xevent.xbutton.button;
1181             /* The X server sends a Release event for each Press for wheels. Ignore them. */
1182             int xticks = 0, yticks = 0;
1183 #ifdef DEBUG_XEVENTS
1184             printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent.xbutton.button);
1185 #endif
1186             if (!X11_IsWheelEvent(display, &xevent, &xticks, &yticks)) {
1187                 if (button > 7) {
1188                     /* see explanation at case ButtonPress */
1189                     button -= (8-SDL_BUTTON_X1);
1190                 }
1191                 SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button);
1192             }
1193         }
1194         break;
1195 
1196     case PropertyNotify:{
1197 #ifdef DEBUG_XEVENTS
1198             unsigned char *propdata;
1199             int status, real_format;
1200             Atom real_type;
1201             unsigned long items_read, items_left;
1202 
1203             char *name = X11_XGetAtomName(display, xevent.xproperty.atom);
1204             if (name) {
1205                 printf("window %p: PropertyNotify: %s %s time=%lu\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent.xproperty.time);
1206                 X11_XFree(name);
1207             }
1208 
1209             status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata);
1210             if (status == Success && items_read > 0) {
1211                 if (real_type == XA_INTEGER) {
1212                     int *values = (int *)propdata;
1213 
1214                     printf("{");
1215                     for (i = 0; i < items_read; i++) {
1216                         printf(" %d", values[i]);
1217                     }
1218                     printf(" }\n");
1219                 } else if (real_type == XA_CARDINAL) {
1220                     if (real_format == 32) {
1221                         Uint32 *values = (Uint32 *)propdata;
1222 
1223                         printf("{");
1224                         for (i = 0; i < items_read; i++) {
1225                             printf(" %d", values[i]);
1226                         }
1227                         printf(" }\n");
1228                     } else if (real_format == 16) {
1229                         Uint16 *values = (Uint16 *)propdata;
1230 
1231                         printf("{");
1232                         for (i = 0; i < items_read; i++) {
1233                             printf(" %d", values[i]);
1234                         }
1235                         printf(" }\n");
1236                     } else if (real_format == 8) {
1237                         Uint8 *values = (Uint8 *)propdata;
1238 
1239                         printf("{");
1240                         for (i = 0; i < items_read; i++) {
1241                             printf(" %d", values[i]);
1242                         }
1243                         printf(" }\n");
1244                     }
1245                 } else if (real_type == XA_STRING ||
1246                            real_type == videodata->UTF8_STRING) {
1247                     printf("{ \"%s\" }\n", propdata);
1248                 } else if (real_type == XA_ATOM) {
1249                     Atom *atoms = (Atom *)propdata;
1250 
1251                     printf("{");
1252                     for (i = 0; i < items_read; i++) {
1253                         char *atomname = X11_XGetAtomName(display, atoms[i]);
1254                         if (atomname) {
1255                             printf(" %s", atomname);
1256                             X11_XFree(atomname);
1257                         }
1258                     }
1259                     printf(" }\n");
1260                 } else {
1261                     char *atomname = X11_XGetAtomName(display, real_type);
1262                     printf("Unknown type: %ld (%s)\n", real_type, atomname ? atomname : "UNKNOWN");
1263                     if (atomname) {
1264                         X11_XFree(atomname);
1265                     }
1266                 }
1267             }
1268             if (status == Success) {
1269                 X11_XFree(propdata);
1270             }
1271 #endif /* DEBUG_XEVENTS */
1272 
1273             /* Take advantage of this moment to make sure user_time has a
1274                 valid timestamp from the X server, so if we later try to
1275                 raise/restore this window, _NET_ACTIVE_WINDOW can have a
1276                 non-zero timestamp, even if there's never been a mouse or
1277                 key press to this window so far. Note that we don't try to
1278                 set _NET_WM_USER_TIME here, though. That's only for legit
1279                 user interaction with the window. */
1280             if (!data->user_time) {
1281                 data->user_time = xevent.xproperty.time;
1282             }
1283 
1284             if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) {
1285                 /* Get the new state from the window manager.
1286                    Compositing window managers can alter visibility of windows
1287                    without ever mapping / unmapping them, so we handle that here,
1288                    because they use the NETWM protocol to notify us of changes.
1289                  */
1290                 const Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window);
1291                 const Uint32 changed = flags ^ data->window->flags;
1292 
1293                 if ((changed & SDL_WINDOW_HIDDEN) || (changed & SDL_WINDOW_FULLSCREEN)) {
1294                      if (flags & SDL_WINDOW_HIDDEN) {
1295                          X11_DispatchUnmapNotify(data);
1296                      } else {
1297                          X11_DispatchMapNotify(data);
1298                     }
1299                 }
1300 
1301                 if (changed & SDL_WINDOW_MAXIMIZED) {
1302                     if (flags & SDL_WINDOW_MAXIMIZED) {
1303                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
1304                     } else {
1305                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
1306                     }
1307                 }
1308             } else if (xevent.xproperty.atom == videodata->XKLAVIER_STATE) {
1309                 /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
1310                    events when the keyboard layout changes (for example,
1311                    changing from English to French on the menubar's keyboard
1312                    icon). Since it changes the XKLAVIER_STATE property, we
1313                    notice and reinit our keymap here. This might not be the
1314                    right approach, but it seems to work. */
1315                 X11_UpdateKeymap(_this);
1316                 SDL_SendKeymapChangedEvent();
1317             } else if (xevent.xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
1318                 Atom type;
1319                 int format;
1320                 unsigned long nitems, bytes_after;
1321                 unsigned char *property;
1322                 if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
1323                     if (type != None && nitems == 4) {
1324                         data->border_left = (int) ((long*)property)[0];
1325                         data->border_right = (int) ((long*)property)[1];
1326                         data->border_top = (int) ((long*)property)[2];
1327                         data->border_bottom = (int) ((long*)property)[3];
1328                     }
1329                     X11_XFree(property);
1330 
1331                     #ifdef DEBUG_XEVENTS
1332                     printf("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d\n", data->border_left, data->border_right, data->border_top, data->border_bottom);
1333                     #endif
1334                 }
1335             }
1336         }
1337         break;
1338 
1339     case SelectionNotify: {
1340             Atom target = xevent.xselection.target;
1341 #ifdef DEBUG_XEVENTS
1342             printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
1343                 xevent.xselection.requestor, xevent.xselection.target);
1344 #endif
1345             if (target == data->xdnd_req) {
1346                 /* read data */
1347                 SDL_x11Prop p;
1348                 X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);
1349 
1350                 if (p.format == 8) {
1351                     char* saveptr = NULL;
1352                     char* name = X11_XGetAtomName(display, target);
1353                     char *token = SDL_strtokr((char *) p.data, "\r\n", &saveptr);
1354                     while (token != NULL) {
1355                         if (SDL_strcmp("text/plain", name)==0) {
1356                             SDL_SendDropText(data->window, token);
1357                         } else if (SDL_strcmp("text/uri-list", name)==0) {
1358                             char *fn = X11_URIToLocal(token);
1359                             if (fn) {
1360                                 SDL_SendDropFile(data->window, fn);
1361                             }
1362                         }
1363                         token = SDL_strtokr(NULL, "\r\n", &saveptr);
1364                     }
1365                     SDL_SendDropComplete(data->window);
1366                 }
1367                 X11_XFree(p.data);
1368 
1369                 /* send reply */
1370                 SDL_memset(&m, 0, sizeof(XClientMessageEvent));
1371                 m.type = ClientMessage;
1372                 m.display = display;
1373                 m.window = data->xdnd_source;
1374                 m.message_type = videodata->XdndFinished;
1375                 m.format = 32;
1376                 m.data.l[0] = data->xwindow;
1377                 m.data.l[1] = 1;
1378                 m.data.l[2] = videodata->XdndActionCopy;
1379                 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);
1380 
1381                 X11_XSync(display, False);
1382             }
1383         }
1384         break;
1385 
1386     default:{
1387 #ifdef DEBUG_XEVENTS
1388             printf("window %p: Unhandled event %d\n", data, xevent.type);
1389 #endif
1390         }
1391         break;
1392     }
1393 }
1394 
1395 static void
X11_HandleFocusChanges(_THIS)1396 X11_HandleFocusChanges(_THIS)
1397 {
1398     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
1399     int i;
1400 
1401     if (videodata && videodata->windowlist) {
1402         for (i = 0; i < videodata->numwindows; ++i) {
1403             SDL_WindowData *data = videodata->windowlist[i];
1404             if (data && data->pending_focus != PENDING_FOCUS_NONE) {
1405                 Uint32 now = SDL_GetTicks();
1406                 if (SDL_TICKS_PASSED(now, data->pending_focus_time)) {
1407                     if (data->pending_focus == PENDING_FOCUS_IN) {
1408                         X11_DispatchFocusIn(_this, data);
1409                     } else {
1410                         X11_DispatchFocusOut(_this, data);
1411                     }
1412                     data->pending_focus = PENDING_FOCUS_NONE;
1413                 }
1414             }
1415         }
1416     }
1417 }
1418 /* Ack!  X11_XPending() actually performs a blocking read if no events available */
1419 static int
X11_Pending(Display * display)1420 X11_Pending(Display * display)
1421 {
1422     /* Flush the display connection and look to see if events are queued */
1423     X11_XFlush(display);
1424     if (X11_XEventsQueued(display, QueuedAlready)) {
1425         return (1);
1426     }
1427 
1428     /* More drastic measures are required -- see if X is ready to talk */
1429     if (SDL_IOReady(ConnectionNumber(display), SDL_FALSE, 0)) {
1430         return (X11_XPending(display));
1431     }
1432 
1433     /* Oh well, nothing is ready .. */
1434     return (0);
1435 }
1436 
1437 void
X11_PumpEvents(_THIS)1438 X11_PumpEvents(_THIS)
1439 {
1440     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
1441 
1442     if (data->last_mode_change_deadline) {
1443         if (SDL_TICKS_PASSED(SDL_GetTicks(), data->last_mode_change_deadline)) {
1444             data->last_mode_change_deadline = 0;  /* assume we're done. */
1445         }
1446     }
1447 
1448     /* Update activity every 30 seconds to prevent screensaver */
1449     if (_this->suspend_screensaver) {
1450         const Uint32 now = SDL_GetTicks();
1451         if (!data->screensaver_activity ||
1452             SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
1453             X11_XResetScreenSaver(data->display);
1454 
1455 #if SDL_USE_LIBDBUS
1456             SDL_DBus_ScreensaverTickle();
1457 #endif
1458 
1459             data->screensaver_activity = now;
1460         }
1461     }
1462 
1463     /* Keep processing pending events */
1464     while (X11_Pending(data->display)) {
1465         X11_DispatchEvent(_this);
1466     }
1467 
1468 #ifdef SDL_USE_IME
1469     if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
1470         SDL_IME_PumpEvents();
1471     }
1472 #endif
1473 
1474     /* FIXME: Only need to do this when there are pending focus changes */
1475     X11_HandleFocusChanges(_this);
1476 }
1477 
1478 
1479 void
X11_SuspendScreenSaver(_THIS)1480 X11_SuspendScreenSaver(_THIS)
1481 {
1482 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
1483     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
1484     int dummy;
1485     int major_version, minor_version;
1486 #endif /* SDL_VIDEO_DRIVER_X11_XSCRNSAVER */
1487 
1488 #if SDL_USE_LIBDBUS
1489     if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
1490         return;
1491     }
1492 
1493     if (_this->suspend_screensaver) {
1494         SDL_DBus_ScreensaverTickle();
1495     }
1496 #endif
1497 
1498 #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER
1499     if (SDL_X11_HAVE_XSS) {
1500         /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */
1501         if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) ||
1502             !X11_XScreenSaverQueryVersion(data->display,
1503                                       &major_version, &minor_version) ||
1504             major_version < 1 || (major_version == 1 && minor_version < 1)) {
1505             return;
1506         }
1507 
1508         X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
1509         X11_XResetScreenSaver(data->display);
1510     }
1511 #endif
1512 }
1513 
1514 #endif /* SDL_VIDEO_DRIVER_X11 */
1515 
1516 /* vi: set ts=4 sw=4 expandtab: */
1517