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