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