| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "../../SDL_internal.h" |
| |
| #if SDL_VIDEO_DRIVER_X11 |
| |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <limits.h> /* For INT_MAX */ |
| |
| #include "SDL_x11video.h" |
| #include "SDL_x11touch.h" |
| #include "SDL_x11xinput2.h" |
| #include "../../events/SDL_events_c.h" |
| #include "../../events/SDL_mouse_c.h" |
| #include "../../events/SDL_touch_c.h" |
| |
| #include "SDL_hints.h" |
| #include "SDL_timer.h" |
| #include "SDL_syswm.h" |
| |
| #include <stdio.h> |
| |
| /*#define DEBUG_XEVENTS*/ |
| |
| #ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT |
| #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 |
| #endif |
| |
| #ifndef _NET_WM_MOVERESIZE_SIZE_TOP |
| #define _NET_WM_MOVERESIZE_SIZE_TOP 1 |
| #endif |
| |
| #ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT |
| #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 |
| #endif |
| |
| #ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT |
| #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 |
| #endif |
| |
| #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT |
| #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 |
| #endif |
| |
| #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM |
| #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 |
| #endif |
| |
| #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT |
| #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 |
| #endif |
| |
| #ifndef _NET_WM_MOVERESIZE_SIZE_LEFT |
| #define _NET_WM_MOVERESIZE_SIZE_LEFT 7 |
| #endif |
| |
| #ifndef _NET_WM_MOVERESIZE_MOVE |
| #define _NET_WM_MOVERESIZE_MOVE 8 |
| #endif |
| |
| typedef struct { |
| unsigned char *data; |
| int format, count; |
| Atom type; |
| } SDL_x11Prop; |
| |
| /* Reads property |
| Must call X11_XFree on results |
| */ |
| static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop) |
| { |
| unsigned char *ret=NULL; |
| Atom type; |
| int fmt; |
| unsigned long count; |
| unsigned long bytes_left; |
| int bytes_fetch = 0; |
| |
| do { |
| if (ret != 0) X11_XFree(ret); |
| X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret); |
| bytes_fetch += bytes_left; |
| } while (bytes_left != 0); |
| |
| p->data=ret; |
| p->format=fmt; |
| p->count=count; |
| p->type=type; |
| } |
| |
| /* Find text-uri-list in a list of targets and return it's atom |
| if available, else return None */ |
| static Atom X11_PickTarget(Display *disp, Atom list[], int list_count) |
| { |
| Atom request = None; |
| char *name; |
| int i; |
| for (i=0; i < list_count && request == None; i++) { |
| name = X11_XGetAtomName(disp, list[i]); |
| if ((SDL_strcmp("text/uri-list", name) == 0) || (SDL_strcmp("text/plain", name) == 0)) { |
| request = list[i]; |
| } |
| X11_XFree(name); |
| } |
| return request; |
| } |
| |
| /* Wrapper for X11_PickTarget for a maximum of three targets, a special |
| case in the Xdnd protocol */ |
| static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2) |
| { |
| int count=0; |
| Atom atom[3]; |
| if (a0 != None) atom[count++] = a0; |
| if (a1 != None) atom[count++] = a1; |
| if (a2 != None) atom[count++] = a2; |
| return X11_PickTarget(disp, atom, count); |
| } |
| |
| struct KeyRepeatCheckData |
| { |
| XEvent *event; |
| SDL_bool found; |
| }; |
| |
| static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev, |
| XPointer arg) |
| { |
| struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *) arg; |
| if (chkev->type == KeyPress && |
| chkev->xkey.keycode == d->event->xkey.keycode && |
| chkev->xkey.time - d->event->xkey.time < 2) |
| d->found = SDL_TRUE; |
| return False; |
| } |
| |
| /* Check to see if this is a repeated key. |
| (idea shamelessly lifted from GII -- thanks guys! :) |
| */ |
| static SDL_bool X11_KeyRepeat(Display *display, XEvent *event) |
| { |
| XEvent dummyev; |
| struct KeyRepeatCheckData d; |
| d.event = event; |
| d.found = SDL_FALSE; |
| if (X11_XPending(display)) |
| X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent, |
| (XPointer) &d); |
| return d.found; |
| } |
| |
| static SDL_bool |
| X11_IsWheelEvent(Display * display,XEvent * event,int * xticks,int * yticks) |
| { |
| /* according to the xlib docs, no specific mouse wheel events exist. |
| However, the defacto standard is that the vertical wheel is X buttons |
| 4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */ |
| |
| /* Xlib defines "Button1" through 5, so we just use literals here. */ |
| switch (event->xbutton.button) { |
| case 4: *yticks = 1; return SDL_TRUE; |
| case 5: *yticks = -1; return SDL_TRUE; |
| case 6: *xticks = 1; return SDL_TRUE; |
| case 7: *xticks = -1; return SDL_TRUE; |
| default: break; |
| } |
| return SDL_FALSE; |
| } |
| |
| /* Decodes URI escape sequences in string buf of len bytes |
| (excluding the terminating NULL byte) in-place. Since |
| URI-encoded characters take three times the space of |
| normal characters, this should not be an issue. |
| |
| Returns the number of decoded bytes that wound up in |
| the buffer, excluding the terminating NULL byte. |
| |
| The buffer is guaranteed to be NULL-terminated but |
| may contain embedded NULL bytes. |
| |
| On error, -1 is returned. |
| */ |
| int X11_URIDecode(char *buf, int len) { |
| int ri, wi, di; |
| char decode = '\0'; |
| if (buf == NULL || len < 0) { |
| errno = EINVAL; |
| return -1; |
| } |
| if (len == 0) { |
| len = SDL_strlen(buf); |
| } |
| for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) { |
| if (di == 0) { |
| /* start decoding */ |
| if (buf[ri] == '%') { |
| decode = '\0'; |
| di += 1; |
| continue; |
| } |
| /* normal write */ |
| buf[wi] = buf[ri]; |
| wi += 1; |
| continue; |
| } else if (di == 1 || di == 2) { |
| char off = '\0'; |
| char isa = buf[ri] >= 'a' && buf[ri] <= 'f'; |
| char isA = buf[ri] >= 'A' && buf[ri] <= 'F'; |
| char isn = buf[ri] >= '0' && buf[ri] <= '9'; |
| if (!(isa || isA || isn)) { |
| /* not a hexadecimal */ |
| int sri; |
| for (sri = ri - di; sri <= ri; sri += 1) { |
| buf[wi] = buf[sri]; |
| wi += 1; |
| } |
| di = 0; |
| continue; |
| } |
| /* itsy bitsy magicsy */ |
| if (isn) { |
| off = 0 - '0'; |
| } else if (isa) { |
| off = 10 - 'a'; |
| } else if (isA) { |
| off = 10 - 'A'; |
| } |
| decode |= (buf[ri] + off) << (2 - di) * 4; |
| if (di == 2) { |
| buf[wi] = decode; |
| wi += 1; |
| di = 0; |
| } else { |
| di += 1; |
| } |
| continue; |
| } |
| } |
| buf[wi] = '\0'; |
| return wi; |
| } |
| |
| /* Convert URI to local filename |
| return filename if possible, else NULL |
| */ |
| static char* X11_URIToLocal(char* uri) { |
| char *file = NULL; |
| SDL_bool local; |
| |
| if (memcmp(uri,"file:/",6) == 0) uri += 6; /* local file? */ |
| else if (strstr(uri,":/") != NULL) return file; /* wrong scheme */ |
| |
| local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/'); |
| |
| /* got a hostname? */ |
| if (!local && uri[0] == '/' && uri[2] != '/') { |
| char* hostname_end = strchr(uri+1, '/'); |
| if (hostname_end != NULL) { |
| char hostname[ 257 ]; |
| if (gethostname(hostname, 255) == 0) { |
| hostname[ 256 ] = '\0'; |
| if (memcmp(uri+1, hostname, hostname_end - (uri+1)) == 0) { |
| uri = hostname_end + 1; |
| local = SDL_TRUE; |
| } |
| } |
| } |
| } |
| if (local) { |
| file = uri; |
| /* Convert URI escape sequences to real characters */ |
| X11_URIDecode(file, 0); |
| if (uri[1] == '/') { |
| file++; |
| } else { |
| file--; |
| } |
| } |
| return file; |
| } |
| |
| #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS |
| static void X11_HandleGenericEvent(SDL_VideoData *videodata,XEvent event) |
| { |
| /* event is a union, so cookie == &event, but this is type safe. */ |
| XGenericEventCookie *cookie = &event.xcookie; |
| if (X11_XGetEventData(videodata->display, cookie)) { |
| X11_HandleXinput2Event(videodata, cookie); |
| X11_XFreeEventData(videodata->display, cookie); |
| } |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS */ |
| |
| static unsigned |
| X11_GetNumLockModifierMask(_THIS) |
| { |
| SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; |
| Display *display = viddata->display; |
| unsigned num_mask = 0; |
| int i, j; |
| XModifierKeymap *xmods; |
| unsigned n; |
| |
| xmods = X11_XGetModifierMapping(display); |
| n = xmods->max_keypermod; |
| for(i = 3; i < 8; i++) { |
| for(j = 0; j < n; j++) { |
| KeyCode kc = xmods->modifiermap[i * n + j]; |
| if (viddata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) { |
| num_mask = 1 << i; |
| break; |
| } |
| } |
| } |
| X11_XFreeModifiermap(xmods); |
| |
| return num_mask; |
| } |
| |
| static void |
| X11_ReconcileKeyboardState(_THIS) |
| { |
| SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; |
| Display *display = viddata->display; |
| char keys[32]; |
| int keycode; |
| Window junk_window; |
| int x, y; |
| unsigned int mask; |
| |
| X11_XQueryKeymap(display, keys); |
| |
| /* Sync up the keyboard modifier state */ |
| if (X11_XQueryPointer(display, DefaultRootWindow(display), &junk_window, &junk_window, &x, &y, &x, &y, &mask)) { |
| SDL_ToggleModState(KMOD_CAPS, (mask & LockMask) != 0); |
| SDL_ToggleModState(KMOD_NUM, (mask & X11_GetNumLockModifierMask(_this)) != 0); |
| } |
| |
| for (keycode = 0; keycode < 256; ++keycode) { |
| if (keys[keycode / 8] & (1 << (keycode % 8))) { |
| SDL_SendKeyboardKey(SDL_PRESSED, viddata->key_layout[keycode]); |
| } else { |
| SDL_SendKeyboardKey(SDL_RELEASED, viddata->key_layout[keycode]); |
| } |
| } |
| } |
| |
| |
| static void |
| X11_DispatchFocusIn(_THIS, SDL_WindowData *data) |
| { |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: Dispatching FocusIn\n", data); |
| #endif |
| SDL_SetKeyboardFocus(data->window); |
| X11_ReconcileKeyboardState(_this); |
| #ifdef X_HAVE_UTF8_STRING |
| if (data->ic) { |
| X11_XSetICFocus(data->ic); |
| } |
| #endif |
| #ifdef SDL_USE_IME |
| SDL_IME_SetFocus(SDL_TRUE); |
| #endif |
| } |
| |
| static void |
| X11_DispatchFocusOut(_THIS, SDL_WindowData *data) |
| { |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: Dispatching FocusOut\n", data); |
| #endif |
| /* If another window has already processed a focus in, then don't try to |
| * remove focus here. Doing so will incorrectly remove focus from that |
| * window, and the focus lost event for this window will have already |
| * been dispatched anyway. */ |
| if (data->window == SDL_GetKeyboardFocus()) { |
| SDL_SetKeyboardFocus(NULL); |
| } |
| #ifdef X_HAVE_UTF8_STRING |
| if (data->ic) { |
| X11_XUnsetICFocus(data->ic); |
| } |
| #endif |
| #ifdef SDL_USE_IME |
| SDL_IME_SetFocus(SDL_FALSE); |
| #endif |
| } |
| |
| static void |
| X11_DispatchMapNotify(SDL_WindowData *data) |
| { |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0); |
| } |
| |
| static void |
| X11_DispatchUnmapNotify(SDL_WindowData *data) |
| { |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0); |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); |
| } |
| |
| static void |
| InitiateWindowMove(_THIS, const SDL_WindowData *data, const SDL_Point *point) |
| { |
| SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; |
| SDL_Window* window = data->window; |
| Display *display = viddata->display; |
| XEvent evt; |
| |
| /* !!! FIXME: we need to regrab this if necessary when the drag is done. */ |
| X11_XUngrabPointer(display, 0L); |
| X11_XFlush(display); |
| |
| evt.xclient.type = ClientMessage; |
| evt.xclient.window = data->xwindow; |
| evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True); |
| evt.xclient.format = 32; |
| evt.xclient.data.l[0] = window->x + point->x; |
| evt.xclient.data.l[1] = window->y + point->y; |
| evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE; |
| evt.xclient.data.l[3] = Button1; |
| evt.xclient.data.l[4] = 0; |
| X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); |
| |
| X11_XSync(display, 0); |
| } |
| |
| static void |
| InitiateWindowResize(_THIS, const SDL_WindowData *data, const SDL_Point *point, int direction) |
| { |
| SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata; |
| SDL_Window* window = data->window; |
| Display *display = viddata->display; |
| XEvent evt; |
| |
| if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT) |
| return; |
| |
| /* !!! FIXME: we need to regrab this if necessary when the drag is done. */ |
| X11_XUngrabPointer(display, 0L); |
| X11_XFlush(display); |
| |
| evt.xclient.type = ClientMessage; |
| evt.xclient.window = data->xwindow; |
| evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True); |
| evt.xclient.format = 32; |
| evt.xclient.data.l[0] = window->x + point->x; |
| evt.xclient.data.l[1] = window->y + point->y; |
| evt.xclient.data.l[2] = direction; |
| evt.xclient.data.l[3] = Button1; |
| evt.xclient.data.l[4] = 0; |
| X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); |
| |
| X11_XSync(display, 0); |
| } |
| |
| static SDL_bool |
| ProcessHitTest(_THIS, const SDL_WindowData *data, const XEvent *xev) |
| { |
| SDL_Window *window = data->window; |
| |
| if (window->hit_test) { |
| const SDL_Point point = { xev->xbutton.x, xev->xbutton.y }; |
| const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); |
| static const int directions[] = { |
| _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP, |
| _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT, |
| _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM, |
| _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT |
| }; |
| |
| switch (rc) { |
| case SDL_HITTEST_DRAGGABLE: |
| InitiateWindowMove(_this, data, &point); |
| return SDL_TRUE; |
| |
| case SDL_HITTEST_RESIZE_TOPLEFT: |
| case SDL_HITTEST_RESIZE_TOP: |
| case SDL_HITTEST_RESIZE_TOPRIGHT: |
| case SDL_HITTEST_RESIZE_RIGHT: |
| case SDL_HITTEST_RESIZE_BOTTOMRIGHT: |
| case SDL_HITTEST_RESIZE_BOTTOM: |
| case SDL_HITTEST_RESIZE_BOTTOMLEFT: |
| case SDL_HITTEST_RESIZE_LEFT: |
| InitiateWindowResize(_this, data, &point, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]); |
| return SDL_TRUE; |
| |
| default: return SDL_FALSE; |
| } |
| } |
| |
| return SDL_FALSE; |
| } |
| |
| static void |
| X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest) |
| { |
| if (latest && (latest != data->user_time)) { |
| SDL_VideoData *videodata = data->videodata; |
| Display *display = videodata->display; |
| X11_XChangeProperty(display, data->xwindow, videodata->_NET_WM_USER_TIME, |
| XA_CARDINAL, 32, PropModeReplace, |
| (const unsigned char *) &latest, 1); |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: updating _NET_WM_USER_TIME to %lu\n", data, latest); |
| #endif |
| data->user_time = latest; |
| } |
| } |
| |
| |
| static void |
| X11_DispatchEvent(_THIS) |
| { |
| SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; |
| Display *display; |
| SDL_WindowData *data; |
| XEvent xevent; |
| int orig_event_type; |
| KeyCode orig_keycode; |
| XClientMessageEvent m; |
| int i; |
| |
| if (!videodata) { |
| return; |
| } |
| display = videodata->display; |
| |
| SDL_zero(xevent); /* valgrind fix. --ryan. */ |
| X11_XNextEvent(display, &xevent); |
| |
| /* Save the original keycode for dead keys, which are filtered out by |
| the XFilterEvent() call below. |
| */ |
| orig_event_type = xevent.type; |
| if (orig_event_type == KeyPress || orig_event_type == KeyRelease) { |
| orig_keycode = xevent.xkey.keycode; |
| } else { |
| orig_keycode = 0; |
| } |
| |
| /* filter events catchs XIM events and sends them to the correct handler */ |
| if (X11_XFilterEvent(&xevent, None) == True) { |
| #if 0 |
| printf("Filtered event type = %d display = %d window = %d\n", |
| xevent.type, xevent.xany.display, xevent.xany.window); |
| #endif |
| if (orig_keycode) { |
| /* Make sure dead key press/release events are sent */ |
| SDL_Scancode scancode = videodata->key_layout[orig_keycode]; |
| if (orig_event_type == KeyPress) { |
| SDL_SendKeyboardKey(SDL_PRESSED, scancode); |
| } else { |
| SDL_SendKeyboardKey(SDL_RELEASED, scancode); |
| } |
| } |
| return; |
| } |
| |
| /* Send a SDL_SYSWMEVENT if the application wants them */ |
| if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) { |
| SDL_SysWMmsg wmmsg; |
| |
| SDL_VERSION(&wmmsg.version); |
| wmmsg.subsystem = SDL_SYSWM_X11; |
| wmmsg.msg.x11.event = xevent; |
| SDL_SendSysWMEvent(&wmmsg); |
| } |
| |
| #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS |
| if(xevent.type == GenericEvent) { |
| X11_HandleGenericEvent(videodata,xevent); |
| return; |
| } |
| #endif |
| |
| #if 0 |
| printf("type = %d display = %d window = %d\n", |
| xevent.type, xevent.xany.display, xevent.xany.window); |
| #endif |
| |
| data = NULL; |
| if (videodata && videodata->windowlist) { |
| for (i = 0; i < videodata->numwindows; ++i) { |
| if ((videodata->windowlist[i] != NULL) && |
| (videodata->windowlist[i]->xwindow == xevent.xany.window)) { |
| data = videodata->windowlist[i]; |
| break; |
| } |
| } |
| } |
| if (!data) { |
| /* The window for KeymapNotify, etc events is 0 */ |
| if (xevent.type == KeymapNotify) { |
| if (SDL_GetKeyboardFocus() != NULL) { |
| X11_ReconcileKeyboardState(_this); |
| } |
| } else if (xevent.type == MappingNotify) { |
| /* Has the keyboard layout changed? */ |
| const int request = xevent.xmapping.request; |
| |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: MappingNotify!\n", data); |
| #endif |
| if ((request == MappingKeyboard) || (request == MappingModifier)) { |
| X11_XRefreshKeyboardMapping(&xevent.xmapping); |
| } |
| |
| X11_UpdateKeymap(_this); |
| SDL_SendKeymapChangedEvent(); |
| } |
| return; |
| } |
| |
| switch (xevent.type) { |
| |
| /* Gaining mouse coverage? */ |
| case EnterNotify:{ |
| SDL_Mouse *mouse = SDL_GetMouse(); |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: EnterNotify! (%d,%d,%d)\n", data, |
| xevent.xcrossing.x, |
| xevent.xcrossing.y, |
| xevent.xcrossing.mode); |
| if (xevent.xcrossing.mode == NotifyGrab) |
| printf("Mode: NotifyGrab\n"); |
| if (xevent.xcrossing.mode == NotifyUngrab) |
| printf("Mode: NotifyUngrab\n"); |
| #endif |
| SDL_SetMouseFocus(data->window); |
| |
| mouse->last_x = xevent.xcrossing.x; |
| mouse->last_y = xevent.xcrossing.y; |
| |
| if (!mouse->relative_mode) { |
| SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y); |
| } |
| } |
| break; |
| /* Losing mouse coverage? */ |
| case LeaveNotify:{ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: LeaveNotify! (%d,%d,%d)\n", data, |
| xevent.xcrossing.x, |
| xevent.xcrossing.y, |
| xevent.xcrossing.mode); |
| if (xevent.xcrossing.mode == NotifyGrab) |
| printf("Mode: NotifyGrab\n"); |
| if (xevent.xcrossing.mode == NotifyUngrab) |
| printf("Mode: NotifyUngrab\n"); |
| #endif |
| if (!SDL_GetMouse()->relative_mode) { |
| SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y); |
| } |
| |
| if (xevent.xcrossing.mode != NotifyGrab && |
| xevent.xcrossing.mode != NotifyUngrab && |
| xevent.xcrossing.detail != NotifyInferior) { |
| SDL_SetMouseFocus(NULL); |
| } |
| } |
| break; |
| |
| /* Gaining input focus? */ |
| case FocusIn:{ |
| if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) { |
| /* Someone is handling a global hotkey, ignore it */ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data); |
| #endif |
| break; |
| } |
| |
| if (xevent.xfocus.detail == NotifyInferior) { |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: FocusIn (NotifierInferior, ignoring)\n", data); |
| #endif |
| break; |
| } |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: FocusIn!\n", data); |
| #endif |
| if (!videodata->last_mode_change_deadline) /* no recent mode changes */ |
| { |
| data->pending_focus = PENDING_FOCUS_NONE; |
| data->pending_focus_time = 0; |
| X11_DispatchFocusIn(_this, data); |
| } |
| else |
| { |
| data->pending_focus = PENDING_FOCUS_IN; |
| data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; |
| } |
| data->last_focus_event_time = SDL_GetTicks(); |
| } |
| break; |
| |
| /* Losing input focus? */ |
| case FocusOut:{ |
| if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) { |
| /* Someone is handling a global hotkey, ignore it */ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data); |
| #endif |
| break; |
| } |
| if (xevent.xfocus.detail == NotifyInferior) { |
| /* We still have focus if a child gets focus */ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: FocusOut (NotifierInferior, ignoring)\n", data); |
| #endif |
| break; |
| } |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: FocusOut!\n", data); |
| #endif |
| if (!videodata->last_mode_change_deadline) /* no recent mode changes */ |
| { |
| data->pending_focus = PENDING_FOCUS_NONE; |
| data->pending_focus_time = 0; |
| X11_DispatchFocusOut(_this, data); |
| } |
| else |
| { |
| data->pending_focus = PENDING_FOCUS_OUT; |
| data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; |
| } |
| } |
| break; |
| |
| /* Key press? */ |
| case KeyPress:{ |
| KeyCode keycode = xevent.xkey.keycode; |
| KeySym keysym = NoSymbol; |
| char text[SDL_TEXTINPUTEVENT_TEXT_SIZE]; |
| Status status = 0; |
| SDL_bool handled_by_ime = SDL_FALSE; |
| |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode); |
| #endif |
| #if 1 |
| if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) { |
| int min_keycode, max_keycode; |
| X11_XDisplayKeycodes(display, &min_keycode, &max_keycode); |
| keysym = X11_KeyCodeToSym(_this, keycode, xevent.xkey.state >> 13); |
| fprintf(stderr, |
| "The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL mailing list <sdl@libsdl.org> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n", |
| keycode, keycode - min_keycode, keysym, |
| X11_XKeysymToString(keysym)); |
| } |
| #endif |
| /* */ |
| SDL_zero(text); |
| #ifdef X_HAVE_UTF8_STRING |
| if (data->ic) { |
| X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text), |
| &keysym, &status); |
| } else { |
| X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL); |
| } |
| #else |
| X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL); |
| #endif |
| |
| #ifdef SDL_USE_IME |
| if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){ |
| handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode); |
| } |
| #endif |
| if (!handled_by_ime) { |
| SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]); |
| if(*text) { |
| SDL_SendKeyboardText(text); |
| } |
| } |
| |
| X11_UpdateUserTime(data, xevent.xkey.time); |
| } |
| break; |
| |
| /* Key release? */ |
| case KeyRelease:{ |
| KeyCode keycode = xevent.xkey.keycode; |
| |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode); |
| #endif |
| if (X11_KeyRepeat(display, &xevent)) { |
| /* We're about to get a repeated key down, ignore the key up */ |
| break; |
| } |
| SDL_SendKeyboardKey(SDL_RELEASED, videodata->key_layout[keycode]); |
| } |
| break; |
| |
| /* Have we been iconified? */ |
| case UnmapNotify:{ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: UnmapNotify!\n", data); |
| #endif |
| X11_DispatchUnmapNotify(data); |
| } |
| break; |
| |
| /* Have we been restored? */ |
| case MapNotify:{ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: MapNotify!\n", data); |
| #endif |
| X11_DispatchMapNotify(data); |
| } |
| break; |
| |
| /* Have we been resized or moved? */ |
| case ConfigureNotify:{ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data, |
| xevent.xconfigure.x, xevent.xconfigure.y, |
| xevent.xconfigure.width, xevent.xconfigure.height); |
| #endif |
| /* Real configure notify events are relative to the parent, synthetic events are absolute. */ |
| if (!xevent.xconfigure.send_event) { |
| unsigned int NumChildren; |
| Window ChildReturn, Root, Parent; |
| Window * Children; |
| /* Translate these coodinates back to relative to root */ |
| X11_XQueryTree(data->videodata->display, xevent.xconfigure.window, &Root, &Parent, &Children, &NumChildren); |
| X11_XTranslateCoordinates(xevent.xconfigure.display, |
| Parent, DefaultRootWindow(xevent.xconfigure.display), |
| xevent.xconfigure.x, xevent.xconfigure.y, |
| &xevent.xconfigure.x, &xevent.xconfigure.y, |
| &ChildReturn); |
| } |
| |
| if (xevent.xconfigure.x != data->last_xconfigure.x || |
| xevent.xconfigure.y != data->last_xconfigure.y) { |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, |
| xevent.xconfigure.x, xevent.xconfigure.y); |
| #ifdef SDL_USE_IME |
| if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){ |
| /* Update IME candidate list position */ |
| SDL_IME_UpdateTextRect(NULL); |
| } |
| #endif |
| } |
| if (xevent.xconfigure.width != data->last_xconfigure.width || |
| xevent.xconfigure.height != data->last_xconfigure.height) { |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, |
| xevent.xconfigure.width, |
| xevent.xconfigure.height); |
| } |
| data->last_xconfigure = xevent.xconfigure; |
| } |
| break; |
| |
| /* Have we been requested to quit (or another client message?) */ |
| case ClientMessage:{ |
| |
| static int xdnd_version=0; |
| |
| if (xevent.xclient.message_type == videodata->XdndEnter) { |
| |
| SDL_bool use_list = xevent.xclient.data.l[1] & 1; |
| data->xdnd_source = xevent.xclient.data.l[0]; |
| xdnd_version = (xevent.xclient.data.l[1] >> 24); |
| #ifdef DEBUG_XEVENTS |
| printf("XID of source window : %ld\n", data->xdnd_source); |
| printf("Protocol version to use : %d\n", xdnd_version); |
| printf("More then 3 data types : %d\n", (int) use_list); |
| #endif |
| |
| if (use_list) { |
| /* fetch conversion targets */ |
| SDL_x11Prop p; |
| X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList); |
| /* pick one */ |
| data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count); |
| X11_XFree(p.data); |
| } else { |
| /* pick from list of three */ |
| data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]); |
| } |
| } |
| else if (xevent.xclient.message_type == videodata->XdndPosition) { |
| |
| #ifdef DEBUG_XEVENTS |
| Atom act= videodata->XdndActionCopy; |
| if(xdnd_version >= 2) { |
| act = xevent.xclient.data.l[4]; |
| } |
| printf("Action requested by user is : %s\n", X11_XGetAtomName(display , act)); |
| #endif |
| |
| |
| /* reply with status */ |
| memset(&m, 0, sizeof(XClientMessageEvent)); |
| m.type = ClientMessage; |
| m.display = xevent.xclient.display; |
| m.window = xevent.xclient.data.l[0]; |
| m.message_type = videodata->XdndStatus; |
| m.format=32; |
| m.data.l[0] = data->xwindow; |
| m.data.l[1] = (data->xdnd_req != None); |
| m.data.l[2] = 0; /* specify an empty rectangle */ |
| m.data.l[3] = 0; |
| m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */ |
| |
| X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m); |
| X11_XFlush(display); |
| } |
| else if(xevent.xclient.message_type == videodata->XdndDrop) { |
| if (data->xdnd_req == None) { |
| /* say again - not interested! */ |
| memset(&m, 0, sizeof(XClientMessageEvent)); |
| m.type = ClientMessage; |
| m.display = xevent.xclient.display; |
| m.window = xevent.xclient.data.l[0]; |
| m.message_type = videodata->XdndFinished; |
| m.format=32; |
| m.data.l[0] = data->xwindow; |
| m.data.l[1] = 0; |
| m.data.l[2] = None; /* fail! */ |
| X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m); |
| } else { |
| /* convert */ |
| if(xdnd_version >= 1) { |
| X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]); |
| } else { |
| X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime); |
| } |
| } |
| } |
| else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && |
| (xevent.xclient.format == 32) && |
| (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) { |
| Window root = DefaultRootWindow(display); |
| |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: _NET_WM_PING\n", data); |
| #endif |
| xevent.xclient.window = root; |
| X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent); |
| break; |
| } |
| |
| else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && |
| (xevent.xclient.format == 32) && |
| (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) { |
| |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: WM_DELETE_WINDOW\n", data); |
| #endif |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); |
| break; |
| } |
| else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && |
| (xevent.xclient.format == 32) && |
| (xevent.xclient.data.l[0] == videodata->WM_TAKE_FOCUS)) { |
| |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: WM_TAKE_FOCUS\n", data); |
| #endif |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_TAKE_FOCUS, 0, 0); |
| break; |
| } |
| } |
| break; |
| |
| /* Do we need to refresh ourselves? */ |
| case Expose:{ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count); |
| #endif |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0); |
| } |
| break; |
| |
| case MotionNotify:{ |
| SDL_Mouse *mouse = SDL_GetMouse(); |
| if(!mouse->relative_mode || mouse->relative_mode_warp) { |
| #ifdef DEBUG_MOTION |
| printf("window %p: X11 motion: %d,%d\n", data, xevent.xmotion.x, xevent.xmotion.y); |
| #endif |
| |
| SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y); |
| } |
| } |
| break; |
| |
| case ButtonPress:{ |
| int xticks = 0, yticks = 0; |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent.xbutton.button); |
| #endif |
| if (X11_IsWheelEvent(display,&xevent,&xticks, &yticks)) { |
| SDL_SendMouseWheel(data->window, 0, xticks, yticks, SDL_MOUSEWHEEL_NORMAL); |
| } else { |
| SDL_bool ignore_click = SDL_FALSE; |
| int button = xevent.xbutton.button; |
| if(button == Button1) { |
| if (ProcessHitTest(_this, data, &xevent)) { |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); |
| break; /* don't pass this event on to app. */ |
| } |
| } |
| else if(button > 7) { |
| /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ... |
| => subtract (8-SDL_BUTTON_X1) to get value SDL expects */ |
| button -= (8-SDL_BUTTON_X1); |
| } |
| if (data->last_focus_event_time) { |
| const int X11_FOCUS_CLICK_TIMEOUT = 10; |
| if (!SDL_TICKS_PASSED(SDL_GetTicks(), data->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) { |
| ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE); |
| } |
| data->last_focus_event_time = 0; |
| } |
| if (!ignore_click) { |
| SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button); |
| } |
| } |
| X11_UpdateUserTime(data, xevent.xbutton.time); |
| } |
| break; |
| |
| case ButtonRelease:{ |
| int button = xevent.xbutton.button; |
| /* The X server sends a Release event for each Press for wheels. Ignore them. */ |
| int xticks = 0, yticks = 0; |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent.xbutton.button); |
| #endif |
| if (!X11_IsWheelEvent(display, &xevent, &xticks, &yticks)) { |
| if (button > 7) { |
| /* see explanation at case ButtonPress */ |
| button -= (8-SDL_BUTTON_X1); |
| } |
| SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button); |
| } |
| } |
| break; |
| |
| case PropertyNotify:{ |
| #ifdef DEBUG_XEVENTS |
| unsigned char *propdata; |
| int status, real_format; |
| Atom real_type; |
| unsigned long items_read, items_left; |
| |
| char *name = X11_XGetAtomName(display, xevent.xproperty.atom); |
| if (name) { |
| printf("window %p: PropertyNotify: %s %s time=%lu\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent.xproperty.time); |
| X11_XFree(name); |
| } |
| |
| status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata); |
| if (status == Success && items_read > 0) { |
| if (real_type == XA_INTEGER) { |
| int *values = (int *)propdata; |
| |
| printf("{"); |
| for (i = 0; i < items_read; i++) { |
| printf(" %d", values[i]); |
| } |
| printf(" }\n"); |
| } else if (real_type == XA_CARDINAL) { |
| if (real_format == 32) { |
| Uint32 *values = (Uint32 *)propdata; |
| |
| printf("{"); |
| for (i = 0; i < items_read; i++) { |
| printf(" %d", values[i]); |
| } |
| printf(" }\n"); |
| } else if (real_format == 16) { |
| Uint16 *values = (Uint16 *)propdata; |
| |
| printf("{"); |
| for (i = 0; i < items_read; i++) { |
| printf(" %d", values[i]); |
| } |
| printf(" }\n"); |
| } else if (real_format == 8) { |
| Uint8 *values = (Uint8 *)propdata; |
| |
| printf("{"); |
| for (i = 0; i < items_read; i++) { |
| printf(" %d", values[i]); |
| } |
| printf(" }\n"); |
| } |
| } else if (real_type == XA_STRING || |
| real_type == videodata->UTF8_STRING) { |
| printf("{ \"%s\" }\n", propdata); |
| } else if (real_type == XA_ATOM) { |
| Atom *atoms = (Atom *)propdata; |
| |
| printf("{"); |
| for (i = 0; i < items_read; i++) { |
| char *atomname = X11_XGetAtomName(display, atoms[i]); |
| if (atomname) { |
| printf(" %s", atomname); |
| X11_XFree(atomname); |
| } |
| } |
| printf(" }\n"); |
| } else { |
| char *atomname = X11_XGetAtomName(display, real_type); |
| printf("Unknown type: %ld (%s)\n", real_type, atomname ? atomname : "UNKNOWN"); |
| if (atomname) { |
| X11_XFree(atomname); |
| } |
| } |
| } |
| if (status == Success) { |
| X11_XFree(propdata); |
| } |
| #endif /* DEBUG_XEVENTS */ |
| |
| /* Take advantage of this moment to make sure user_time has a |
| valid timestamp from the X server, so if we later try to |
| raise/restore this window, _NET_ACTIVE_WINDOW can have a |
| non-zero timestamp, even if there's never been a mouse or |
| key press to this window so far. Note that we don't try to |
| set _NET_WM_USER_TIME here, though. That's only for legit |
| user interaction with the window. */ |
| if (!data->user_time) { |
| data->user_time = xevent.xproperty.time; |
| } |
| |
| if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) { |
| /* Get the new state from the window manager. |
| Compositing window managers can alter visibility of windows |
| without ever mapping / unmapping them, so we handle that here, |
| because they use the NETWM protocol to notify us of changes. |
| */ |
| const Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window); |
| const Uint32 changed = flags ^ data->window->flags; |
| |
| if ((changed & SDL_WINDOW_HIDDEN) || (changed & SDL_WINDOW_FULLSCREEN)) { |
| if (flags & SDL_WINDOW_HIDDEN) { |
| X11_DispatchUnmapNotify(data); |
| } else { |
| X11_DispatchMapNotify(data); |
| } |
| } |
| |
| if (changed & SDL_WINDOW_MAXIMIZED) { |
| if (flags & SDL_WINDOW_MAXIMIZED) { |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0); |
| } else { |
| SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0); |
| } |
| } |
| } else if (xevent.xproperty.atom == videodata->XKLAVIER_STATE) { |
| /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify |
| events when the keyboard layout changes (for example, |
| changing from English to French on the menubar's keyboard |
| icon). Since it changes the XKLAVIER_STATE property, we |
| notice and reinit our keymap here. This might not be the |
| right approach, but it seems to work. */ |
| X11_UpdateKeymap(_this); |
| SDL_SendKeymapChangedEvent(); |
| } else if (xevent.xproperty.atom == videodata->_NET_FRAME_EXTENTS) { |
| Atom type; |
| int format; |
| unsigned long nitems, bytes_after; |
| unsigned char *property; |
| if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) { |
| if (type != None && nitems == 4) { |
| data->border_left = (int) ((long*)property)[0]; |
| data->border_right = (int) ((long*)property)[1]; |
| data->border_top = (int) ((long*)property)[2]; |
| data->border_bottom = (int) ((long*)property)[3]; |
| } |
| X11_XFree(property); |
| |
| #ifdef DEBUG_XEVENTS |
| 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); |
| #endif |
| } |
| } |
| } |
| break; |
| |
| /* Copy the selection from our own CUTBUFFER to the requested property */ |
| case SelectionRequest: { |
| XSelectionRequestEvent *req; |
| XEvent sevent; |
| int seln_format; |
| unsigned long nbytes; |
| unsigned long overflow; |
| unsigned char *seln_data; |
| |
| req = &xevent.xselectionrequest; |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: SelectionRequest (requestor = %ld, target = %ld)\n", data, |
| req->requestor, req->target); |
| #endif |
| |
| SDL_zero(sevent); |
| sevent.xany.type = SelectionNotify; |
| sevent.xselection.selection = req->selection; |
| sevent.xselection.target = None; |
| sevent.xselection.property = None; |
| sevent.xselection.requestor = req->requestor; |
| sevent.xselection.time = req->time; |
| |
| if (X11_XGetWindowProperty(display, DefaultRootWindow(display), |
| X11_GetSDLCutBufferClipboardType(display), 0, INT_MAX/4, False, req->target, |
| &sevent.xselection.target, &seln_format, &nbytes, |
| &overflow, &seln_data) == Success) { |
| Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0); |
| if (sevent.xselection.target == req->target) { |
| X11_XChangeProperty(display, req->requestor, req->property, |
| sevent.xselection.target, seln_format, PropModeReplace, |
| seln_data, nbytes); |
| sevent.xselection.property = req->property; |
| } else if (XA_TARGETS == req->target) { |
| Atom SupportedFormats[] = { XA_TARGETS, sevent.xselection.target }; |
| X11_XChangeProperty(display, req->requestor, req->property, |
| XA_ATOM, 32, PropModeReplace, |
| (unsigned char*)SupportedFormats, |
| SDL_arraysize(SupportedFormats)); |
| sevent.xselection.property = req->property; |
| sevent.xselection.target = XA_TARGETS; |
| } |
| X11_XFree(seln_data); |
| } |
| X11_XSendEvent(display, req->requestor, False, 0, &sevent); |
| X11_XSync(display, False); |
| } |
| break; |
| |
| case SelectionNotify: { |
| Atom target = xevent.xselection.target; |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data, |
| xevent.xselection.requestor, xevent.xselection.target); |
| #endif |
| if (target == data->xdnd_req) { |
| /* read data */ |
| SDL_x11Prop p; |
| X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY); |
| |
| if (p.format == 8) { |
| /* !!! FIXME: don't use strtok here. It's not reentrant and not in SDL_stdinc. */ |
| char* name = X11_XGetAtomName(display, target); |
| char *token = strtok((char *) p.data, "\r\n"); |
| while (token != NULL) { |
| if (SDL_strcmp("text/plain", name)==0) { |
| SDL_SendDropText(data->window, token); |
| } else if (SDL_strcmp("text/uri-list", name)==0) { |
| char *fn = X11_URIToLocal(token); |
| if (fn) { |
| SDL_SendDropFile(data->window, fn); |
| } |
| } |
| token = strtok(NULL, "\r\n"); |
| } |
| SDL_SendDropComplete(data->window); |
| } |
| X11_XFree(p.data); |
| |
| /* send reply */ |
| SDL_memset(&m, 0, sizeof(XClientMessageEvent)); |
| m.type = ClientMessage; |
| m.display = display; |
| m.window = data->xdnd_source; |
| m.message_type = videodata->XdndFinished; |
| m.format = 32; |
| m.data.l[0] = data->xwindow; |
| m.data.l[1] = 1; |
| m.data.l[2] = videodata->XdndActionCopy; |
| X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m); |
| |
| X11_XSync(display, False); |
| |
| } else { |
| videodata->selection_waiting = SDL_FALSE; |
| } |
| } |
| break; |
| |
| case SelectionClear: { |
| Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0); |
| |
| if (xevent.xselectionclear.selection == XA_PRIMARY || |
| (XA_CLIPBOARD != None && xevent.xselectionclear.selection == XA_CLIPBOARD)) { |
| SDL_SendClipboardUpdate(); |
| } |
| } |
| break; |
| |
| default:{ |
| #ifdef DEBUG_XEVENTS |
| printf("window %p: Unhandled event %d\n", data, xevent.type); |
| #endif |
| } |
| break; |
| } |
| } |
| |
| static void |
| X11_HandleFocusChanges(_THIS) |
| { |
| SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; |
| int i; |
| |
| if (videodata && videodata->windowlist) { |
| for (i = 0; i < videodata->numwindows; ++i) { |
| SDL_WindowData *data = videodata->windowlist[i]; |
| if (data && data->pending_focus != PENDING_FOCUS_NONE) { |
| Uint32 now = SDL_GetTicks(); |
| if (SDL_TICKS_PASSED(now, data->pending_focus_time)) { |
| if (data->pending_focus == PENDING_FOCUS_IN) { |
| X11_DispatchFocusIn(_this, data); |
| } else { |
| X11_DispatchFocusOut(_this, data); |
| } |
| data->pending_focus = PENDING_FOCUS_NONE; |
| } |
| } |
| } |
| } |
| } |
| /* Ack! X11_XPending() actually performs a blocking read if no events available */ |
| static int |
| X11_Pending(Display * display) |
| { |
| /* Flush the display connection and look to see if events are queued */ |
| X11_XFlush(display); |
| if (X11_XEventsQueued(display, QueuedAlready)) { |
| return (1); |
| } |
| |
| /* More drastic measures are required -- see if X is ready to talk */ |
| { |
| static struct timeval zero_time; /* static == 0 */ |
| int x11_fd; |
| fd_set fdset; |
| |
| x11_fd = ConnectionNumber(display); |
| FD_ZERO(&fdset); |
| FD_SET(x11_fd, &fdset); |
| if (select(x11_fd + 1, &fdset, NULL, NULL, &zero_time) == 1) { |
| return (X11_XPending(display)); |
| } |
| } |
| |
| /* Oh well, nothing is ready .. */ |
| return (0); |
| } |
| |
| void |
| X11_PumpEvents(_THIS) |
| { |
| SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; |
| |
| if (data->last_mode_change_deadline) { |
| if (SDL_TICKS_PASSED(SDL_GetTicks(), data->last_mode_change_deadline)) { |
| data->last_mode_change_deadline = 0; /* assume we're done. */ |
| } |
| } |
| |
| /* Update activity every 30 seconds to prevent screensaver */ |
| if (_this->suspend_screensaver) { |
| const Uint32 now = SDL_GetTicks(); |
| if (!data->screensaver_activity || |
| SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) { |
| X11_XResetScreenSaver(data->display); |
| |
| #if SDL_USE_LIBDBUS |
| SDL_DBus_ScreensaverTickle(); |
| #endif |
| |
| data->screensaver_activity = now; |
| } |
| } |
| |
| /* Keep processing pending events */ |
| while (X11_Pending(data->display)) { |
| X11_DispatchEvent(_this); |
| } |
| |
| #ifdef SDL_USE_IME |
| if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){ |
| SDL_IME_PumpEvents(); |
| } |
| #endif |
| |
| /* FIXME: Only need to do this when there are pending focus changes */ |
| X11_HandleFocusChanges(_this); |
| } |
| |
| |
| void |
| X11_SuspendScreenSaver(_THIS) |
| { |
| #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER |
| SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; |
| int dummy; |
| int major_version, minor_version; |
| #endif /* SDL_VIDEO_DRIVER_X11_XSCRNSAVER */ |
| |
| #if SDL_USE_LIBDBUS |
| if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) { |
| return; |
| } |
| |
| if (_this->suspend_screensaver) { |
| SDL_DBus_ScreensaverTickle(); |
| } |
| #endif |
| |
| #if SDL_VIDEO_DRIVER_X11_XSCRNSAVER |
| if (SDL_X11_HAVE_XSS) { |
| /* X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 */ |
| if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) || |
| !X11_XScreenSaverQueryVersion(data->display, |
| &major_version, &minor_version) || |
| major_version < 1 || (major_version == 1 && minor_version < 1)) { |
| return; |
| } |
| |
| X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver); |
| X11_XResetScreenSaver(data->display); |
| } |
| #endif |
| } |
| |
| #endif /* SDL_VIDEO_DRIVER_X11 */ |
| |
| /* vi: set ts=4 sw=4 expandtab: */ |