123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205 |
- /*
- Simple DirectMedia Layer
- Copyright (C) 1997-2025 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"
- #ifdef 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_x11pen.h"
- #include "SDL_x11touch.h"
- #include "SDL_x11xinput2.h"
- #include "SDL_x11xfixes.h"
- #include "SDL_x11settings.h"
- #include "../SDL_clipboard_c.h"
- #include "SDL_x11xsync.h"
- #include "../../core/unix/SDL_poll.h"
- #include "../../events/SDL_events_c.h"
- #include "../../events/SDL_mouse_c.h"
- #include "../../events/SDL_touch_c.h"
- #include "../../core/linux/SDL_system_theme.h"
- #include "../SDL_sysvideo.h"
- #include <stdio.h>
- #if 0
- #define DEBUG_XEVENTS
- #endif
- #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) {
- 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]);
- // Preferred MIME targets
- if ((SDL_strcmp("text/uri-list", name) == 0) ||
- (SDL_strcmp("text/plain;charset=utf-8", name) == 0) ||
- (SDL_strcmp("UTF8_STRING", name) == 0)) {
- request = list[i];
- }
- // Fallback MIME targets
- if ((SDL_strcmp("text/plain", name) == 0) ||
- (SDL_strcmp("TEXT", name) == 0)) {
- if (request == None) {
- 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;
- 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 = true;
- }
- return False;
- }
- /* Check to see if this is a repeated key.
- (idea shamelessly lifted from GII -- thanks guys! :)
- */
- static bool X11_KeyRepeat(Display *display, XEvent *event)
- {
- XEvent dummyev;
- struct KeyRepeatCheckData d;
- d.event = event;
- d.found = false;
- if (X11_XPending(display)) {
- X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent, (XPointer)&d);
- }
- return d.found;
- }
- static bool X11_IsWheelEvent(Display *display, int button, 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 (button) {
- case 4:
- *yticks = 1;
- return true;
- case 5:
- *yticks = -1;
- return true;
- case 6:
- *xticks = 1;
- return true;
- case 7:
- *xticks = -1;
- return true;
- default:
- break;
- }
- return false;
- }
- // An X11 event hook
- static SDL_X11EventHook g_X11EventHook = NULL;
- static void *g_X11EventHookData = NULL;
- void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata)
- {
- g_X11EventHook = callback;
- g_X11EventHookData = userdata;
- }
- #ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
- static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev)
- {
- SDL_VideoData *videodata = _this->internal;
- // event is a union, so cookie == &event, but this is type safe.
- XGenericEventCookie *cookie = &xev->xcookie;
- if (X11_XGetEventData(videodata->display, cookie)) {
- if (!g_X11EventHook || g_X11EventHook(g_X11EventHookData, xev)) {
- X11_HandleXinput2Event(_this, cookie);
- }
- X11_XFreeEventData(videodata->display, cookie);
- }
- }
- #endif // SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
- static void X11_UpdateSystemKeyModifiers(SDL_VideoData *viddata)
- {
- Window junk_window;
- int x, y;
- X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &viddata->xkb.xkb_modifiers);
- }
- static void X11_ReconcileModifiers(SDL_VideoData *viddata)
- {
- const Uint32 xk_modifiers = viddata->xkb.xkb_modifiers;
- /* If a modifier was activated by a keypress, it will be tied to the
- * specific left/right key that initiated it. Otherwise, the ambiguous
- * left/right combo is used.
- */
- if (xk_modifiers & ShiftMask) {
- if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_SHIFT)) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_SHIFT;
- }
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SHIFT;
- }
- if (xk_modifiers & ControlMask) {
- if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_CTRL)) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_CTRL;
- }
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CTRL;
- }
- // Mod1 is used for the Alt keys
- if (xk_modifiers & Mod1Mask) {
- if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_ALT)) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_ALT;
- }
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_ALT;
- }
- // Mod4 is used for the Super (aka GUI/Logo) keys.
- if (xk_modifiers & Mod4Mask) {
- if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_GUI)) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_GUI;
- }
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_GUI;
- }
- // Mod3 is typically Level 5 shift.
- if (xk_modifiers & Mod3Mask) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_LEVEL5;
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_LEVEL5;
- }
- // Mod5 is typically Level 3 shift (aka AltGr).
- if (xk_modifiers & Mod5Mask) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_MODE;
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE;
- }
- if (xk_modifiers & LockMask) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_CAPS;
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CAPS;
- }
- if (xk_modifiers & viddata->xkb.numlock_mask) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM;
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_NUM;
- }
- if (xk_modifiers & viddata->xkb.scrolllock_mask) {
- viddata->xkb.sdl_modifiers |= SDL_KMOD_SCROLL;
- } else {
- viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SCROLL;
- }
- SDL_SetModState(viddata->xkb.sdl_modifiers);
- }
- static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool allow_reconciliation)
- {
- const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
- SDL_Keymod mod = SDL_KMOD_NONE;
- bool reconcile = false;
- /* SDL clients expect modifier state to be activated at the same time as the
- * source keypress, so we set pressed modifier state with the usual modifier
- * keys here, as the explicit modifier event won't arrive until after the
- * keypress event. If this is wrong, it will be corrected when the explicit
- * modifier state is checked.
- */
- switch (keycode) {
- case SDLK_LSHIFT:
- mod = SDL_KMOD_LSHIFT;
- break;
- case SDLK_RSHIFT:
- mod = SDL_KMOD_RSHIFT;
- break;
- case SDLK_LCTRL:
- mod = SDL_KMOD_LCTRL;
- break;
- case SDLK_RCTRL:
- mod = SDL_KMOD_RCTRL;
- break;
- case SDLK_LALT:
- mod = SDL_KMOD_LALT;
- break;
- case SDLK_RALT:
- mod = SDL_KMOD_RALT;
- break;
- case SDLK_LGUI:
- mod = SDL_KMOD_LGUI;
- break;
- case SDLK_RGUI:
- mod = SDL_KMOD_RGUI;
- break;
- case SDLK_MODE:
- mod = SDL_KMOD_MODE;
- break;
- case SDLK_LEVEL5_SHIFT:
- mod = SDL_KMOD_LEVEL5;
- break;
- case SDLK_CAPSLOCK:
- case SDLK_NUMLOCKCLEAR:
- case SDLK_SCROLLLOCK:
- {
- /* For locking modifier keys, query the lock state directly, or we may have to wait until the next
- * key press event to know if a lock was actually activated from the key event.
- */
- unsigned int cur_mask = viddata->xkb.xkb_modifiers;
- X11_UpdateSystemKeyModifiers(viddata);
- if (viddata->xkb.xkb_modifiers & LockMask) {
- cur_mask |= LockMask;
- } else {
- cur_mask &= ~LockMask;
- }
- if (viddata->xkb.xkb_modifiers & viddata->xkb.numlock_mask) {
- cur_mask |= viddata->xkb.numlock_mask;
- } else {
- cur_mask &= ~viddata->xkb.numlock_mask;
- }
- if (viddata->xkb.xkb_modifiers & viddata->xkb.scrolllock_mask) {
- cur_mask |= viddata->xkb.scrolllock_mask;
- } else {
- cur_mask &= ~viddata->xkb.scrolllock_mask;
- }
- viddata->xkb.xkb_modifiers = cur_mask;
- } SDL_FALLTHROUGH;
- default:
- reconcile = true;
- break;
- }
- if (pressed) {
- viddata->xkb.sdl_modifiers |= mod;
- } else {
- viddata->xkb.sdl_modifiers &= ~mod;
- }
- if (allow_reconciliation) {
- if (reconcile) {
- X11_ReconcileModifiers(viddata);
- } else {
- SDL_SetModState(viddata->xkb.sdl_modifiers);
- }
- }
- }
- void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
- {
- SDL_VideoData *videodata = _this->internal;
- Display *display = videodata->display;
- char keys[32];
- int keycode;
- const bool *keyboardState;
- X11_XQueryKeymap(display, keys);
- keyboardState = SDL_GetKeyboardState(0);
- for (keycode = 0; keycode < SDL_arraysize(videodata->key_layout); ++keycode) {
- SDL_Scancode scancode = videodata->key_layout[keycode];
- bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
- bool sdlKeyPressed = keyboardState[scancode];
- if (x11KeyPressed && !sdlKeyPressed) {
- // Only update modifier state for keys that are pressed in another application
- switch (SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false)) {
- case SDLK_LCTRL:
- case SDLK_RCTRL:
- case SDLK_LSHIFT:
- case SDLK_RSHIFT:
- case SDLK_LALT:
- case SDLK_RALT:
- case SDLK_LGUI:
- case SDLK_RGUI:
- case SDLK_MODE:
- case SDLK_LEVEL5_SHIFT:
- X11_HandleModifierKeys(videodata, scancode, true, false);
- SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true);
- break;
- default:
- break;
- }
- } else if (!x11KeyPressed && sdlKeyPressed) {
- X11_HandleModifierKeys(videodata, scancode, false, false);
- SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false);
- }
- }
- X11_UpdateSystemKeyModifiers(videodata);
- X11_ReconcileModifiers(videodata);
- }
- static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data)
- {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: Dispatching FocusIn", data->xwindow);
- #endif
- SDL_SetKeyboardFocus(data->window);
- X11_ReconcileKeyboardState(_this);
- #ifdef X_HAVE_UTF8_STRING
- if (data->ic) {
- X11_XSetICFocus(data->ic);
- }
- #endif
- if (data->flashing_window) {
- X11_FlashWindow(_this, data->window, SDL_FLASH_CANCEL);
- }
- }
- static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data)
- {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: Dispatching FocusOut", data->xwindow);
- #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
- }
- static void X11_DispatchMapNotify(SDL_WindowData *data)
- {
- SDL_Window *window = data->window;
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
- if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
- SDL_UpdateWindowGrab(window);
- }
- }
- static void X11_DispatchUnmapNotify(SDL_WindowData *data)
- {
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
- }
- static void DispatchWindowMove(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point)
- {
- SDL_VideoData *videodata = _this->internal;
- SDL_Window *window = data->window;
- Display *display = videodata->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 = videodata->atoms._NET_WM_MOVERESIZE;
- evt.xclient.format = 32;
- evt.xclient.data.l[0] = (size_t)window->x + point->x;
- evt.xclient.data.l[1] = (size_t)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 ScheduleWindowMove(SDL_VideoDevice *_this, SDL_WindowData *data, const SDL_Point *point)
- {
- data->pending_move = true;
- data->pending_move_point = *point;
- }
- static void InitiateWindowResize(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point, int direction)
- {
- SDL_VideoData *videodata = _this->internal;
- SDL_Window *window = data->window;
- Display *display = videodata->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 = videodata->atoms._NET_WM_MOVERESIZE;
- evt.xclient.format = 32;
- evt.xclient.data.l[0] = (size_t)window->x + point->x;
- evt.xclient.data.l[1] = (size_t)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);
- }
- bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result)
- {
- SDL_Window *window = data->window;
- if (!window->hit_test) return false;
- const SDL_Point point = { (int)x, (int)y };
- SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
- if (!force_new_result && rc == data->hit_test_result) {
- return true;
- }
- X11_SetHitTestCursor(rc);
- data->hit_test_result = rc;
- return true;
- }
- bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y)
- {
- SDL_Window *window = data->window;
- if (window->hit_test) {
- const SDL_Point point = { (int)x, (int)y };
- 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 (data->hit_test_result) {
- case SDL_HITTEST_DRAGGABLE:
- /* Some window managers get in a bad state when a move event starts while input is transitioning
- to the SDL window. This can happen when clicking on a drag region of an unfocused window
- where the same mouse down event will trigger a drag event and a window activate. */
- if (data->window->flags & SDL_WINDOW_INPUT_FOCUS) {
- DispatchWindowMove(_this, data, &point);
- } else {
- ScheduleWindowMove(_this, data, &point);
- }
- return 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[data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]);
- return true;
- default:
- return false;
- }
- }
- return 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->atoms._NET_WM_USER_TIME,
- XA_CARDINAL, 32, PropModeReplace,
- (const unsigned char *)&latest, 1);
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: updating _NET_WM_USER_TIME to %lu", data->xwindow, latest);
- #endif
- data->user_time = latest;
- }
- }
- static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xevent)
- {
- int i;
- SDL_VideoData *videodata = _this->internal;
- Display *display = videodata->display;
- SDL_assert(videodata->clipboard_window != None);
- SDL_assert(xevent->xany.window == videodata->clipboard_window);
- switch (xevent->type) {
- // Copy the selection from our own CUTBUFFER to the requested property
- case SelectionRequest:
- {
- const XSelectionRequestEvent *req = &xevent->xselectionrequest;
- XEvent sevent;
- int mime_formats;
- unsigned char *seln_data;
- size_t seln_length = 0;
- Atom XA_TARGETS = videodata->atoms.TARGETS;
- SDLX11_ClipboardData *clipboard;
- #ifdef DEBUG_XEVENTS
- char *atom_name;
- atom_name = X11_XGetAtomName(display, req->target);
- SDL_Log("window CLIPBOARD: SelectionRequest (requestor = 0x%lx, target = 0x%lx, mime_type = %s)",
- req->requestor, req->target, atom_name);
- if (atom_name) {
- X11_XFree(atom_name);
- }
- #endif
- if (req->selection == XA_PRIMARY) {
- clipboard = &videodata->primary_selection;
- } else {
- clipboard = &videodata->clipboard;
- }
- SDL_zero(sevent);
- sevent.xany.type = SelectionNotify;
- sevent.xselection.selection = req->selection;
- sevent.xselection.target = None;
- sevent.xselection.property = None; // tell them no by default
- sevent.xselection.requestor = req->requestor;
- sevent.xselection.time = req->time;
- /* !!! FIXME: We were probably storing this on the root window
- because an SDL window might go away...? but we don't have to do
- this now (or ever, really). */
- if (req->target == XA_TARGETS) {
- Atom *supportedFormats;
- supportedFormats = SDL_malloc((clipboard->mime_count + 1) * sizeof(Atom));
- supportedFormats[0] = XA_TARGETS;
- mime_formats = 1;
- for (i = 0; i < clipboard->mime_count; ++i) {
- supportedFormats[mime_formats++] = X11_XInternAtom(display, clipboard->mime_types[i], False);
- }
- X11_XChangeProperty(display, req->requestor, req->property,
- XA_ATOM, 32, PropModeReplace,
- (unsigned char *)supportedFormats,
- mime_formats);
- sevent.xselection.property = req->property;
- sevent.xselection.target = XA_TARGETS;
- SDL_free(supportedFormats);
- } else {
- if (clipboard->callback) {
- for (i = 0; i < clipboard->mime_count; ++i) {
- const char *mime_type = clipboard->mime_types[i];
- if (X11_XInternAtom(display, mime_type, False) != req->target) {
- continue;
- }
- // FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? - Yes, yes we do.
- // This is a safe cast, XChangeProperty() doesn't take a const value, but it doesn't modify the data
- seln_data = (unsigned char *)clipboard->callback(clipboard->userdata, mime_type, &seln_length);
- if (seln_data) {
- X11_XChangeProperty(display, req->requestor, req->property,
- req->target, 8, PropModeReplace,
- seln_data, seln_length);
- sevent.xselection.property = req->property;
- sevent.xselection.target = req->target;
- }
- break;
- }
- }
- }
- X11_XSendEvent(display, req->requestor, False, 0, &sevent);
- X11_XSync(display, False);
- } break;
- case SelectionNotify:
- {
- const XSelectionEvent *xsel = &xevent->xselection;
- #ifdef DEBUG_XEVENTS
- const char *propName = xsel->property ? X11_XGetAtomName(display, xsel->property) : "None";
- const char *targetName = xsel->target ? X11_XGetAtomName(display, xsel->target) : "None";
- SDL_Log("window CLIPBOARD: SelectionNotify (requestor = 0x%lx, target = %s, property = %s)",
- xsel->requestor, targetName, propName);
- #endif
- if (xsel->target == videodata->atoms.TARGETS && xsel->property == videodata->atoms.SDL_FORMATS) {
- /* the new mime formats are the SDL_FORMATS property as an array of Atoms */
- Atom atom = None;
- Atom *patom;
- unsigned char* data = NULL;
- int format_property = 0;
- unsigned long length = 0;
- unsigned long bytes_left = 0;
- int j;
- X11_XGetWindowProperty(display, GetWindow(_this), videodata->atoms.SDL_FORMATS, 0, 200,
- 0, XA_ATOM, &atom, &format_property, &length, &bytes_left, &data);
- int allocationsize = (length + 1) * sizeof(char*);
- for (j = 0, patom = (Atom*)data; j < length; j++, patom++) {
- char *atomStr = X11_XGetAtomName(display, *patom);
- allocationsize += SDL_strlen(atomStr) + 1;
- X11_XFree(atomStr);
- }
- char **new_mime_types = SDL_AllocateTemporaryMemory(allocationsize);
- if (new_mime_types) {
- char *strPtr = (char *)(new_mime_types + length + 1);
- for (j = 0, patom = (Atom*)data; j < length; j++, patom++) {
- char *atomStr = X11_XGetAtomName(display, *patom);
- new_mime_types[j] = strPtr;
- strPtr = stpcpy(strPtr, atomStr) + 1;
- X11_XFree(atomStr);
- }
- new_mime_types[length] = NULL;
- SDL_SendClipboardUpdate(false, new_mime_types, length);
- }
- if (data) {
- X11_XFree(data);
- }
- }
- videodata->selection_waiting = false;
- } break;
- case SelectionClear:
- {
- Atom XA_CLIPBOARD = videodata->atoms.CLIPBOARD;
- SDLX11_ClipboardData *clipboard = NULL;
- #ifdef DEBUG_XEVENTS
- SDL_Log("window CLIPBOARD: SelectionClear (requestor = 0x%lx, target = 0x%lx)",
- xevent->xselection.requestor, xevent->xselection.target);
- #endif
- if (xevent->xselectionclear.selection == XA_PRIMARY) {
- clipboard = &videodata->primary_selection;
- } else if (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD) {
- clipboard = &videodata->clipboard;
- }
- if (clipboard && clipboard->callback) {
- if (clipboard->sequence) {
- SDL_CancelClipboardData(clipboard->sequence);
- } else {
- SDL_free(clipboard->userdata);
- }
- SDL_zerop(clipboard);
- }
- } break;
- case PropertyNotify:
- {
- char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
- if (SDL_strncmp(name_of_atom, "SDL_SELECTION", sizeof("SDL_SELECTION") - 1) == 0 && xevent->xproperty.state == PropertyNewValue) {
- videodata->selection_incr_waiting = false;
- }
- if (name_of_atom) {
- X11_XFree(name_of_atom);
- }
- } break;
- }
- }
- static void X11_HandleSettingsEvent(SDL_VideoDevice *_this, const XEvent *xevent)
- {
- SDL_VideoData *videodata = _this->internal;
- SDL_assert(videodata->xsettings_window != None);
- SDL_assert(xevent->xany.window == videodata->xsettings_window);
- X11_HandleXsettings(_this, xevent);
- }
- static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg)
- {
- XUnmapEvent *unmap;
- unmap = (XUnmapEvent *)arg;
- return ev->type == MapNotify &&
- ev->xmap.window == unmap->window &&
- ev->xmap.serial == unmap->serial;
- }
- static Bool isReparentNotify(Display *display, XEvent *ev, XPointer arg)
- {
- XUnmapEvent *unmap;
- unmap = (XUnmapEvent *)arg;
- return ev->type == ReparentNotify &&
- ev->xreparent.window == unmap->window &&
- ev->xreparent.serial == unmap->serial;
- }
- static bool IsHighLatin1(const char *string, int length)
- {
- while (length-- > 0) {
- Uint8 ch = (Uint8)*string;
- if (ch >= 0x80) {
- return true;
- }
- ++string;
- }
- return false;
- }
- static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out)
- {
- int result = X11_XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out);
- if (IsHighLatin1(buffer_return, result)) {
- char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result + 1);
- if (utf8_text) {
- SDL_strlcpy(buffer_return, utf8_text, bytes_buffer);
- SDL_free(utf8_text);
- return SDL_strlen(buffer_return);
- } else {
- return 0;
- }
- }
- return result;
- }
- SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window)
- {
- const SDL_VideoData *videodata = _this->internal;
- int i;
- if (videodata && videodata->windowlist) {
- for (i = 0; i < videodata->numwindows; ++i) {
- if ((videodata->windowlist[i] != NULL) &&
- (videodata->windowlist[i]->xwindow == window)) {
- return videodata->windowlist[i];
- }
- }
- }
- return NULL;
- }
- Uint64 X11_GetEventTimestamp(unsigned long time)
- {
- // FIXME: Get the event time in the SDL tick time base
- return SDL_GetTicksNS();
- }
- void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent)
- {
- SDL_VideoData *videodata = _this->internal;
- Display *display = videodata->display;
- KeyCode keycode = xevent->xkey.keycode;
- KeySym keysym = NoSymbol;
- int text_length = 0;
- char text[64];
- Status status = 0;
- bool handled_by_ime = false;
- bool pressed = (xevent->type == KeyPress);
- SDL_Scancode scancode = videodata->key_layout[keycode];
- Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time);
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx %s (X11 keycode = 0x%X)", xevent->xany.window, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode);
- #endif
- #ifdef DEBUG_SCANCODES
- if (scancode == 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);
- SDL_Log("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).",
- keycode, keycode - min_keycode, keysym,
- X11_XKeysymToString(keysym));
- }
- #endif // DEBUG SCANCODES
- text[0] = '\0';
- videodata->xkb.xkb_modifiers = xevent->xkey.state;
- if (SDL_TextInputActive(windowdata->window)) {
- // filter events catches XIM events and sends them to the correct handler
- if (X11_XFilterEvent(xevent, None)) {
- #ifdef DEBUG_XEVENTS
- SDL_Log("Filtered event type = %d display = %p window = 0x%lx",
- xevent->type, xevent->xany.display, xevent->xany.window);
- #endif
- handled_by_ime = true;
- }
- if (!handled_by_ime) {
- #ifdef X_HAVE_UTF8_STRING
- if (windowdata->ic && xevent->type == KeyPress) {
- text_length = X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text) - 1,
- &keysym, &status);
- } else {
- text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL);
- }
- #else
- text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL);
- #endif
- }
- }
- if (!handled_by_ime) {
- if (pressed) {
- X11_HandleModifierKeys(videodata, scancode, true, true);
- SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true);
- if (*text) {
- text[text_length] = '\0';
- X11_ClearComposition(windowdata);
- SDL_SendKeyboardText(text);
- }
- } else {
- if (X11_KeyRepeat(display, xevent)) {
- // We're about to get a repeated key down, ignore the key up
- return;
- }
- X11_HandleModifierKeys(videodata, scancode, false, true);
- SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false);
- }
- }
- if (pressed) {
- X11_UpdateUserTime(windowdata, xevent->xkey.time);
- }
- }
- void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time)
- {
- SDL_Window *window = windowdata->window;
- const SDL_VideoData *videodata = _this->internal;
- Display *display = videodata->display;
- int xticks = 0, yticks = 0;
- Uint64 timestamp = X11_GetEventTimestamp(time);
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: ButtonPress (X11 button = %d)", windowdata->xwindow, button);
- #endif
- SDL_Mouse *mouse = SDL_GetMouse();
- if (!mouse->relative_mode && (x != mouse->x || y != mouse->y)) {
- X11_ProcessHitTest(_this, windowdata, x, y, false);
- SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y);
- }
- if (X11_IsWheelEvent(display, button, &xticks, &yticks)) {
- SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL);
- } else {
- bool ignore_click = false;
- 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 (button == Button1) {
- if (X11_TriggerHitTestAction(_this, windowdata, x, y)) {
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0);
- return; // don't pass this event on to app.
- }
- }
- if (windowdata->last_focus_event_time) {
- const int X11_FOCUS_CLICK_TIMEOUT = 10;
- if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
- ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false);
- }
- windowdata->last_focus_event_time = 0;
- }
- if (!ignore_click) {
- SDL_SendMouseButton(timestamp, window, mouseID, button, true);
- }
- }
- X11_UpdateUserTime(windowdata, time);
- }
- void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time)
- {
- SDL_Window *window = windowdata->window;
- const SDL_VideoData *videodata = _this->internal;
- Display *display = videodata->display;
- // The X server sends a Release event for each Press for wheels. Ignore them.
- int xticks = 0, yticks = 0;
- Uint64 timestamp = X11_GetEventTimestamp(time);
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)", windowdata->xwindow, button);
- #endif
- if (!X11_IsWheelEvent(display, button, &xticks, &yticks)) {
- if (button > 7) {
- // see explanation at case ButtonPress
- button -= (8 - SDL_BUTTON_X1);
- }
- SDL_SendMouseButton(timestamp, window, mouseID, button, false);
- }
- }
- void X11_GetBorderValues(SDL_WindowData *data)
- {
- SDL_VideoData *videodata = data->videodata;
- Display *display = videodata->display;
- Atom type;
- int format;
- unsigned long nitems, bytes_after;
- unsigned char *property;
- // Some compositors will send extents even when the border hint is turned off. Ignore them in this case.
- if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) {
- if (X11_XGetWindowProperty(display, data->xwindow, videodata->atoms._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
- SDL_Log("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d", data->border_left, data->border_right, data->border_top, data->border_bottom);
- #endif
- }
- } else {
- data->border_left = data->border_top = data->border_right = data->border_bottom = 0;
- }
- }
- static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
- {
- SDL_VideoData *videodata = _this->internal;
- Display *display;
- SDL_WindowData *data;
- XClientMessageEvent m;
- int i;
- SDL_assert(videodata != NULL);
- display = videodata->display;
- // filter events catches XIM events and sends them to the correct handler
- // Key press/release events are filtered in X11_HandleKeyEvent()
- if (xevent->type != KeyPress && xevent->type != KeyRelease) {
- if (X11_XFilterEvent(xevent, None)) {
- #ifdef DEBUG_XEVENTS
- SDL_Log("Filtered event type = %d display = %p window = 0x%lx",
- xevent->type, xevent->xany.display, xevent->xany.window);
- #endif
- return;
- }
- }
- #ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
- if (xevent->type == GenericEvent) {
- X11_HandleGenericEvent(_this, xevent);
- return;
- }
- #endif
- // Calling the event hook for generic events happens in X11_HandleGenericEvent(), where the event data is available
- if (g_X11EventHook) {
- if (!g_X11EventHook(g_X11EventHookData, xevent)) {
- return;
- }
- }
- #ifdef SDL_VIDEO_DRIVER_X11_XRANDR
- if (videodata->xrandr_event_base && (xevent->type == (videodata->xrandr_event_base + RRNotify))) {
- X11_HandleXRandREvent(_this, xevent);
- }
- #endif
- #ifdef DEBUG_XEVENTS
- SDL_Log("X11 event type = %d display = %p window = 0x%lx",
- xevent->type, xevent->xany.display, xevent->xany.window);
- #endif
- #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
- if (SDL_X11_HAVE_XFIXES &&
- xevent->type == X11_GetXFixesSelectionNotifyEvent()) {
- XFixesSelectionNotifyEvent *ev = (XFixesSelectionNotifyEvent *)xevent;
- #ifdef DEBUG_XEVENTS
- SDL_Log("window CLIPBOARD: XFixesSelectionNotify (selection = %s)",
- X11_XGetAtomName(display, ev->selection));
- #endif
- if (ev->subtype == XFixesSetSelectionOwnerNotify)
- {
- if (ev->selection != videodata->atoms.CLIPBOARD)
- return;
- if (X11_XGetSelectionOwner(display, ev->selection) == videodata->clipboard_window)
- return;
- /* when here we're notified that the clipboard had an external change, we request the
- * available mime types by asking for a conversion to the TARGETS format. We should get a
- * SelectionNotify event later, and when treating these results, we will push a ClipboardUpdated
- * event
- */
- X11_XConvertSelection(display, videodata->atoms.CLIPBOARD, videodata->atoms.TARGETS,
- videodata->atoms.SDL_FORMATS, GetWindow(_this), CurrentTime);
- }
- return;
- }
- #endif // SDL_VIDEO_DRIVER_X11_XFIXES
- if ((videodata->clipboard_window != None) &&
- (videodata->clipboard_window == xevent->xany.window)) {
- X11_HandleClipboardEvent(_this, xevent);
- return;
- }
- if ((videodata->xsettings_window != None) &&
- (videodata->xsettings_window == xevent->xany.window)) {
- X11_HandleSettingsEvent(_this, xevent);
- return;
- }
- data = X11_FindWindow(_this, xevent->xany.window);
- if (!data) {
- // The window for KeymapNotify, etc events is 0
- if (xevent->type == KeymapNotify) {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: KeymapNotify!", xevent->xany.window);
- #endif
- if (SDL_GetKeyboardFocus() != NULL) {
- #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
- if (videodata->xkb.desc_ptr) {
- XkbStateRec state;
- if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) {
- if (state.group != videodata->xkb.current_group) {
- // Only rebuild the keymap if the layout has changed.
- videodata->xkb.current_group = state.group;
- X11_UpdateKeymap(_this, true);
- }
- }
- }
- #endif
- X11_ReconcileKeyboardState(_this);
- }
- } else if (xevent->type == MappingNotify) {
- // Has the keyboard layout changed?
- const int request = xevent->xmapping.request;
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window);
- #endif
- if ((request == MappingKeyboard) || (request == MappingModifier)) {
- X11_XRefreshKeyboardMapping(&xevent->xmapping);
- }
- X11_UpdateKeymap(_this, true);
- } else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) {
- char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
- if (SDL_strncmp(name_of_atom, "_ICC_PROFILE", sizeof("_ICC_PROFILE") - 1) == 0) {
- XWindowAttributes attrib;
- int screennum;
- for (i = 0; i < videodata->numwindows; ++i) {
- if (videodata->windowlist[i] != NULL) {
- data = videodata->windowlist[i];
- X11_XGetWindowAttributes(display, data->xwindow, &attrib);
- screennum = X11_XScreenNumberOfScreen(attrib.screen);
- if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) {
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
- } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) {
- int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]);
- if (screennum == iccscreennum) {
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
- }
- }
- }
- }
- }
- if (name_of_atom) {
- X11_XFree(name_of_atom);
- }
- }
- return;
- }
- switch (xevent->type) {
- // Gaining mouse coverage?
- case EnterNotify:
- {
- SDL_Mouse *mouse = SDL_GetMouse();
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: EnterNotify! (%d,%d,%d)", xevent->xany.window,
- xevent->xcrossing.x,
- xevent->xcrossing.y,
- xevent->xcrossing.mode);
- if (xevent->xcrossing.mode == NotifyGrab) {
- SDL_Log("Mode: NotifyGrab");
- }
- if (xevent->xcrossing.mode == NotifyUngrab) {
- SDL_Log("Mode: NotifyUngrab");
- }
- #endif
- SDL_SetMouseFocus(data->window);
- mouse->last_x = xevent->xcrossing.x;
- mouse->last_y = xevent->xcrossing.y;
- #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
- {
- // Only create the barriers if we have input focus
- SDL_WindowData *windowdata = data->window->internal;
- if ((data->pointer_barrier_active == true) && windowdata->window->flags & SDL_WINDOW_INPUT_FOCUS) {
- X11_ConfineCursorWithFlags(_this, windowdata->window, &windowdata->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
- }
- }
- #endif
- if (!mouse->relative_mode) {
- SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
- }
- // We ungrab in LeaveNotify, so we may need to grab again here
- SDL_UpdateWindowGrab(data->window);
- X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, true);
- } break;
- // Losing mouse coverage?
- case LeaveNotify:
- {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: LeaveNotify! (%d,%d,%d)", xevent->xany.window,
- xevent->xcrossing.x,
- xevent->xcrossing.y,
- xevent->xcrossing.mode);
- if (xevent->xcrossing.mode == NotifyGrab) {
- SDL_Log("Mode: NotifyGrab");
- }
- if (xevent->xcrossing.mode == NotifyUngrab) {
- SDL_Log("Mode: NotifyUngrab");
- }
- #endif
- if (!SDL_GetMouse()->relative_mode) {
- SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
- }
- if (xevent->xcrossing.mode != NotifyGrab &&
- xevent->xcrossing.mode != NotifyUngrab &&
- xevent->xcrossing.detail != NotifyInferior) {
- /* In order for interaction with the window decorations and menu to work properly
- on Mutter, we need to ungrab the keyboard when the the mouse leaves. */
- if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
- X11_SetWindowKeyboardGrab(_this, data->window, false);
- }
- 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
- SDL_Log("window 0x%lx: FocusIn (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window);
- #endif
- break;
- }
- if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: FocusIn (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window);
- #endif
- break;
- }
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: FocusIn!", xevent->xany.window);
- #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
- SDL_Log("window 0x%lx: FocusOut (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window);
- #endif
- break;
- }
- if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
- /* We still have focus if a child gets focus. We also don't
- care about the position of the pointer when the keyboard
- focus changed. */
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: FocusOut (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window);
- #endif
- break;
- }
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: FocusOut!", xevent->xany.window);
- #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;
- }
- #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
- // Disable confinement if it is activated.
- if (data->pointer_barrier_active == true) {
- X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
- }
- #endif // SDL_VIDEO_DRIVER_X11_XFIXES
- } break;
- // Have we been iconified?
- case UnmapNotify:
- {
- XEvent ev;
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: UnmapNotify!", xevent->xany.window);
- #endif
- if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent->xunmap)) {
- X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent->xunmap);
- } else {
- X11_DispatchUnmapNotify(data);
- }
- #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
- // Disable confinement if the window gets hidden.
- if (data->pointer_barrier_active == true) {
- X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
- }
- #endif // SDL_VIDEO_DRIVER_X11_XFIXES
- } break;
- // Have we been restored?
- case MapNotify:
- {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: MapNotify!", xevent->xany.window);
- #endif
- X11_DispatchMapNotify(data);
- #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
- // Enable confinement if it was activated.
- if (data->pointer_barrier_active == true) {
- X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
- }
- #endif // SDL_VIDEO_DRIVER_X11_XFIXES
- } break;
- // Have we been resized or moved?
- case ConfigureNotify:
- {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: ConfigureNotify! (position: %d,%d, size: %dx%d)", xevent->xany.window,
- 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 coordinates 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) {
- if (!data->size_move_event_flags) {
- SDL_Window *w;
- int x = xevent->xconfigure.x;
- int y = xevent->xconfigure.y;
- data->pending_operation &= ~X11_PENDING_OP_MOVE;
- SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
- for (w = data->window->first_child; w; w = w->next_sibling) {
- // Don't update hidden child popup windows, their relative position doesn't change
- if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
- X11_UpdateWindowPosition(w, true);
- }
- }
- }
- }
- #ifdef SDL_VIDEO_DRIVER_X11_XSYNC
- X11_HandleConfigure(data->window, &xevent->xconfigure);
- #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
- if (xevent->xconfigure.width != data->last_xconfigure.width ||
- xevent->xconfigure.height != data->last_xconfigure.height) {
- if (!data->size_move_event_flags) {
- data->pending_operation &= ~X11_PENDING_OP_RESIZE;
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_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->atoms.XdndEnter) {
- 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
- SDL_Log("XID of source window : 0x%lx", data->xdnd_source);
- SDL_Log("Protocol version to use : %d", xdnd_version);
- SDL_Log("More then 3 data types : %d", (int)use_list);
- #endif
- if (use_list) {
- // fetch conversion targets
- SDL_x11Prop p;
- X11_ReadProperty(&p, display, data->xdnd_source, videodata->atoms.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->atoms.XdndLeave) {
- #ifdef DEBUG_XEVENTS
- SDL_Log("XID of source window : 0x%lx", xevent->xclient.data.l[0]);
- #endif
- SDL_SendDropComplete(data->window);
- } else if (xevent->xclient.message_type == videodata->atoms.XdndPosition) {
- #ifdef DEBUG_XEVENTS
- Atom act = videodata->atoms.XdndActionCopy;
- if (xdnd_version >= 2) {
- act = xevent->xclient.data.l[4];
- }
- SDL_Log("Action requested by user is : %s", X11_XGetAtomName(display, act));
- #endif
- {
- // Drag and Drop position
- int root_x, root_y, window_x, window_y;
- Window ChildReturn;
- root_x = xevent->xclient.data.l[2] >> 16;
- root_y = xevent->xclient.data.l[2] & 0xffff;
- // Translate from root to current window position
- X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow,
- root_x, root_y, &window_x, &window_y, &ChildReturn);
- SDL_SendDropPosition(data->window, (float)window_x, (float)window_y);
- }
- // reply with status
- SDL_memset(&m, 0, sizeof(XClientMessageEvent));
- m.type = ClientMessage;
- m.display = xevent->xclient.display;
- m.window = xevent->xclient.data.l[0];
- m.message_type = videodata->atoms.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->atoms.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->atoms.XdndDrop) {
- if (data->xdnd_req == None) {
- // say again - not interested!
- SDL_memset(&m, 0, sizeof(XClientMessageEvent));
- m.type = ClientMessage;
- m.display = xevent->xclient.display;
- m.window = xevent->xclient.data.l[0];
- m.message_type = videodata->atoms.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->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, xevent->xclient.data.l[2]);
- } else {
- X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, CurrentTime);
- }
- }
- } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
- (xevent->xclient.format == 32) &&
- (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_PING)) {
- Window root = DefaultRootWindow(display);
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: _NET_WM_PING", xevent->xany.window);
- #endif
- xevent->xclient.window = root;
- X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, xevent);
- break;
- }
- else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
- (xevent->xclient.format == 32) &&
- (xevent->xclient.data.l[0] == videodata->atoms.WM_DELETE_WINDOW)) {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: WM_DELETE_WINDOW", xevent->xany.window);
- #endif
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0);
- break;
- } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) &&
- (xevent->xclient.format == 32) &&
- (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_SYNC_REQUEST)) {
- #ifdef DEBUG_XEVENTS
- printf("window %p: _NET_WM_SYNC_REQUEST\n", data);
- #endif
- #ifdef SDL_VIDEO_DRIVER_X11_XSYNC
- X11_HandleSyncRequest(data->window, &xevent->xclient);
- #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
- break;
- }
- } break;
- // Do we need to refresh ourselves?
- case Expose:
- {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: Expose (count = %d)", xevent->xany.window, xevent->xexpose.count);
- #endif
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
- } break;
- /* Use XInput2 instead of the xevents API if possible, for:
- - KeyPress
- - KeyRelease
- - MotionNotify
- - ButtonPress
- - ButtonRelease
- XInput2 has more precise information, e.g., to distinguish different input devices. */
- case KeyPress:
- case KeyRelease:
- {
- if (data->xinput2_keyboard_enabled) {
- // This input is being handled by XInput2
- break;
- }
- X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent);
- } break;
- case MotionNotify:
- {
- if (data->xinput2_mouse_enabled && !data->mouse_grabbed) {
- // This input is being handled by XInput2
- break;
- }
- SDL_Mouse *mouse = SDL_GetMouse();
- if (!mouse->relative_mode) {
- #ifdef DEBUG_MOTION
- SDL_Log("window 0x%lx: X11 motion: %d,%d", xevent->xany.window, xevent->xmotion.x, xevent->xmotion.y);
- #endif
- X11_ProcessHitTest(_this, data, (float)xevent->xmotion.x, (float)xevent->xmotion.y, false);
- SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xmotion.x, (float)xevent->xmotion.y);
- }
- } break;
- case ButtonPress:
- {
- if (data->xinput2_mouse_enabled) {
- // This input is being handled by XInput2
- break;
- }
- X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button,
- xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time);
- } break;
- case ButtonRelease:
- {
- if (data->xinput2_mouse_enabled) {
- // This input is being handled by XInput2
- break;
- }
- X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, xevent->xbutton.time);
- } 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) {
- SDL_Log("window 0x%lx: PropertyNotify: %s %s time=%lu", xevent->xany.window, 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;
- SDL_Log("{");
- for (i = 0; i < items_read; i++) {
- SDL_Log(" %d", values[i]);
- }
- SDL_Log(" }");
- } else if (real_type == XA_CARDINAL) {
- if (real_format == 32) {
- Uint32 *values = (Uint32 *)propdata;
- SDL_Log("{");
- for (i = 0; i < items_read; i++) {
- SDL_Log(" %d", values[i]);
- }
- SDL_Log(" }");
- } else if (real_format == 16) {
- Uint16 *values = (Uint16 *)propdata;
- SDL_Log("{");
- for (i = 0; i < items_read; i++) {
- SDL_Log(" %d", values[i]);
- }
- SDL_Log(" }");
- } else if (real_format == 8) {
- Uint8 *values = (Uint8 *)propdata;
- SDL_Log("{");
- for (i = 0; i < items_read; i++) {
- SDL_Log(" %d", values[i]);
- }
- SDL_Log(" }");
- }
- } else if (real_type == XA_STRING ||
- real_type == videodata->atoms.UTF8_STRING) {
- SDL_Log("{ \"%s\" }", propdata);
- } else if (real_type == XA_ATOM) {
- Atom *atoms = (Atom *)propdata;
- SDL_Log("{");
- for (i = 0; i < items_read; i++) {
- char *atomname = X11_XGetAtomName(display, atoms[i]);
- if (atomname) {
- SDL_Log(" %s", atomname);
- X11_XFree(atomname);
- }
- }
- SDL_Log(" }");
- } else {
- char *atomname = X11_XGetAtomName(display, real_type);
- SDL_Log("Unknown type: 0x%lx (%s)", 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->atoms._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 SDL_WindowFlags flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window);
- const SDL_WindowFlags changed = flags ^ data->window->flags;
- if ((changed & (SDL_WINDOW_HIDDEN | SDL_WINDOW_FULLSCREEN)) != 0) {
- if (flags & SDL_WINDOW_HIDDEN) {
- X11_DispatchUnmapNotify(data);
- } else {
- X11_DispatchMapNotify(data);
- }
- }
- if (!SDL_WINDOW_IS_POPUP(data->window)) {
- if (changed & SDL_WINDOW_FULLSCREEN) {
- data->pending_operation &= ~X11_PENDING_OP_FULLSCREEN;
- if (flags & SDL_WINDOW_FULLSCREEN) {
- if (!(flags & SDL_WINDOW_MINIMIZED)) {
- const bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
- if (commit) {
- /* This was initiated by the compositor, or the mode was changed between the request and the window
- * becoming fullscreen. Switch to the application requested mode if necessary.
- */
- SDL_copyp(&data->window->current_fullscreen_mode, &data->window->requested_fullscreen_mode);
- SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_UPDATE, true);
- } else {
- SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_ENTER, false);
- }
- }
- } else {
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
- SDL_UpdateFullscreenMode(data->window, false, false);
- SDL_zero(data->requested_fullscreen_mode);
- // Need to restore or update any limits changed while the window was fullscreen.
- X11_SetWindowMinMax(data->window, !!(flags & SDL_WINDOW_MAXIMIZED));
- // Toggle the borders if they were forced on while creating a borderless fullscreen window.
- if (data->fullscreen_borders_forced_on) {
- data->toggle_borders = true;
- data->fullscreen_borders_forced_on = false;
- }
- }
- if ((flags & SDL_WINDOW_FULLSCREEN) &&
- (data->border_top || data->border_left || data->border_bottom || data->border_right)) {
- /* If the window is entering fullscreen and the borders are
- * non-zero sized, turn off size events until the borders are
- * shut off to avoid bogus window sizes and positions, and
- * note that the old borders were non-zero for restoration.
- */
- data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
- data->previous_borders_nonzero = true;
- } else if (!(flags & SDL_WINDOW_FULLSCREEN) &&
- data->previous_borders_nonzero &&
- (!data->border_top && !data->border_left && !data->border_bottom && !data->border_right)) {
- /* If the window is leaving fullscreen and the current borders
- * are zero sized, but weren't when entering fullscreen, turn
- * off size events until the borders come back to avoid bogus
- * window sizes and positions.
- */
- data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
- data->previous_borders_nonzero = false;
- } else {
- data->size_move_event_flags = 0;
- data->previous_borders_nonzero = false;
- if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
- data->toggle_borders = false;
- X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
- }
- }
- }
- if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
- data->pending_operation &= ~X11_PENDING_OP_MAXIMIZE;
- if ((changed & SDL_WINDOW_MINIMIZED)) {
- data->pending_operation &= ~X11_PENDING_OP_RESTORE;
- // If coming out of minimized, send a restore event before sending maximized.
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
- }
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
- }
- if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) {
- data->pending_operation &= ~X11_PENDING_OP_MINIMIZE;
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
- }
- if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
- data->pending_operation &= ~X11_PENDING_OP_RESTORE;
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
- // Apply any pending state if restored.
- if (!(flags & SDL_WINDOW_FULLSCREEN)) {
- if (data->pending_position) {
- data->pending_position = false;
- data->pending_operation |= X11_PENDING_OP_MOVE;
- data->expected.x = data->window->pending.x - data->border_left;
- data->expected.y = data->window->pending.y - data->border_top;
- X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y);
- }
- if (data->pending_size) {
- data->pending_size = false;
- data->pending_operation |= X11_PENDING_OP_RESIZE;
- data->expected.w = data->window->pending.w;
- data->expected.h = data->window->pending.h;
- X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h);
- }
- }
- }
- if ((flags & SDL_WINDOW_INPUT_FOCUS)) {
- if (data->pending_move) {
- DispatchWindowMove(_this, data, &data->pending_move_point);
- data->pending_move = false;
- }
- }
- }
- if (changed & SDL_WINDOW_OCCLUDED) {
- SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0);
- }
- } else if (xevent->xproperty.atom == videodata->atoms.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, true);
- } else if (xevent->xproperty.atom == videodata->atoms._NET_FRAME_EXTENTS) {
- /* Events are disabled when leaving fullscreen until the borders appear to avoid
- * incorrect size/position events.
- */
- if (data->size_move_event_flags) {
- data->size_move_event_flags &= ~X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
- X11_GetBorderValues(data);
- }
- if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
- data->toggle_borders = false;
- X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
- }
- }
- } break;
- case SelectionNotify:
- {
- Atom target = xevent->xselection.target;
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: SelectionNotify (requestor = 0x%lx, target = 0x%lx)", xevent->xany.window,
- xevent->xselection.requestor, xevent->xselection.target);
- #endif
- if (target == data->xdnd_req) {
- // read data
- SDL_x11Prop p;
- X11_ReadProperty(&p, display, data->xwindow, videodata->atoms.PRIMARY);
- if (p.format == 8) {
- char *saveptr = NULL;
- char *name = X11_XGetAtomName(display, target);
- if (name) {
- char *token = SDL_strtok_r((char *)p.data, "\r\n", &saveptr);
- while (token) {
- if ((SDL_strcmp("text/plain;charset=utf-8", name) == 0) ||
- (SDL_strcmp("UTF8_STRING", name) == 0) ||
- (SDL_strcmp("text/plain", name) == 0) ||
- (SDL_strcmp("TEXT", name) == 0)) {
- SDL_SendDropText(data->window, token);
- } else if (SDL_strcmp("text/uri-list", name) == 0) {
- if (SDL_URIToLocal(token, token) >= 0) {
- SDL_SendDropFile(data->window, NULL, token);
- }
- }
- token = SDL_strtok_r(NULL, "\r\n", &saveptr);
- }
- X11_XFree(name);
- }
- 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->atoms.XdndFinished;
- m.format = 32;
- m.data.l[0] = data->xwindow;
- m.data.l[1] = 1;
- m.data.l[2] = videodata->atoms.XdndActionCopy;
- X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent *)&m);
- X11_XSync(display, False);
- }
- } break;
- default:
- {
- #ifdef DEBUG_XEVENTS
- SDL_Log("window 0x%lx: Unhandled event %d", xevent->xany.window, xevent->type);
- #endif
- } break;
- }
- }
- static void X11_HandleFocusChanges(SDL_VideoDevice *_this)
- {
- SDL_VideoData *videodata = _this->internal;
- 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) {
- Uint64 now = SDL_GetTicks();
- if (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;
- }
- }
- }
- }
- }
- static Bool isAnyEvent(Display *display, XEvent *ev, XPointer arg)
- {
- return True;
- }
- static bool X11_PollEvent(Display *display, XEvent *event)
- {
- if (!X11_XCheckIfEvent(display, event, isAnyEvent, NULL)) {
- return false;
- }
- return true;
- }
- void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_VideoData *data = _this->internal;
- Display *req_display = data->request_display;
- Window xwindow = window->internal->xwindow;
- XClientMessageEvent event;
- SDL_memset(&event, 0, sizeof(XClientMessageEvent));
- event.type = ClientMessage;
- event.display = req_display;
- event.send_event = True;
- event.message_type = data->atoms._SDL_WAKEUP;
- event.format = 8;
- X11_XSendEvent(req_display, xwindow, False, NoEventMask, (XEvent *)&event);
- /* XSendEvent returns a status and it could be BadValue or BadWindow. If an
- error happens it is an SDL's internal error and there is nothing we can do here. */
- X11_XFlush(req_display);
- }
- int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
- {
- SDL_VideoData *videodata = _this->internal;
- Display *display;
- XEvent xevent;
- display = videodata->display;
- SDL_zero(xevent);
- // Flush and poll to grab any events already read and queued
- X11_XFlush(display);
- if (X11_PollEvent(display, &xevent)) {
- // Fall through
- } else if (timeoutNS == 0) {
- return 0;
- } else {
- // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait
- int err = SDL_IOReady(ConnectionNumber(display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS);
- if (err > 0) {
- if (!X11_PollEvent(display, &xevent)) {
- /* Someone may have beat us to reading the fd. Return 1 here to
- * trigger the normal spurious wakeup logic in the event core. */
- return 1;
- }
- } else if (err == 0) {
- // Timeout
- return 0;
- } else {
- // Error returned from poll()/select()
- if (errno == EINTR) {
- /* If the wait was interrupted by a signal, we may have generated a
- * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */
- return 1;
- } else {
- return err;
- }
- }
- }
- X11_DispatchEvent(_this, &xevent);
- #ifdef SDL_USE_LIBDBUS
- SDL_DBus_PumpEvents();
- #endif
- return 1;
- }
- void X11_PumpEvents(SDL_VideoDevice *_this)
- {
- SDL_VideoData *data = _this->internal;
- XEvent xevent;
- int i;
- /* Check if a display had the mode changed and is waiting for a window to asynchronously become
- * fullscreen. If there is no fullscreen window past the elapsed timeout, revert the mode switch.
- */
- for (i = 0; i < _this->num_displays; ++i) {
- if (_this->displays[i]->internal->mode_switch_deadline_ns) {
- if (_this->displays[i]->fullscreen_window) {
- _this->displays[i]->internal->mode_switch_deadline_ns = 0;
- } else if (SDL_GetTicksNS() >= _this->displays[i]->internal->mode_switch_deadline_ns) {
- SDL_LogError(SDL_LOG_CATEGORY_VIDEO,
- "Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id);
- SDL_SetDisplayModeForDisplay(_this->displays[i], NULL);
- }
- }
- }
- if (data->last_mode_change_deadline) {
- if (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) {
- Uint64 now = SDL_GetTicks();
- if (!data->screensaver_activity || now >= (data->screensaver_activity + 30000)) {
- X11_XResetScreenSaver(data->display);
- #ifdef SDL_USE_LIBDBUS
- SDL_DBus_ScreensaverTickle();
- #endif
- data->screensaver_activity = now;
- }
- }
- SDL_zero(xevent);
- // Keep processing pending events
- while (X11_PollEvent(data->display, &xevent)) {
- X11_DispatchEvent(_this, &xevent);
- }
- #ifdef SDL_USE_LIBDBUS
- SDL_DBus_PumpEvents();
- #endif
- // FIXME: Only need to do this when there are pending focus changes
- X11_HandleFocusChanges(_this);
- // FIXME: Only need to do this when there are flashing windows
- for (i = 0; i < data->numwindows; ++i) {
- if (data->windowlist[i] != NULL &&
- data->windowlist[i]->flash_cancel_time &&
- SDL_GetTicks() >= data->windowlist[i]->flash_cancel_time) {
- X11_FlashWindow(_this, data->windowlist[i]->window, SDL_FLASH_CANCEL);
- }
- }
- if (data->xinput_hierarchy_changed) {
- X11_Xinput2UpdateDevices(_this, false);
- data->xinput_hierarchy_changed = false;
- }
- }
- bool X11_SuspendScreenSaver(SDL_VideoDevice *_this)
- {
- #ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
- SDL_VideoData *data = _this->internal;
- int dummy;
- int major_version, minor_version;
- #endif // SDL_VIDEO_DRIVER_X11_XSCRNSAVER
- #ifdef SDL_USE_LIBDBUS
- if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) {
- return true;
- }
- if (_this->suspend_screensaver) {
- SDL_DBus_ScreensaverTickle();
- }
- #endif
- #ifdef 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 SDL_Unsupported();
- }
- X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver);
- X11_XResetScreenSaver(data->display);
- return true;
- }
- #endif
- return SDL_Unsupported();
- }
- #endif // SDL_VIDEO_DRIVER_X11
|