12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416 |
- /*
- Simple DirectMedia Layer
- Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
- 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_WAYLAND
- #include "../../core/unix/SDL_poll.h"
- #include "../../events/SDL_events_c.h"
- #include "../../events/SDL_scancode_tables_c.h"
- #include "../../events/SDL_keysym_to_keycode_c.h"
- #include "../../core/linux/SDL_system_theme.h"
- #include "../SDL_sysvideo.h"
- #include "SDL_waylandvideo.h"
- #include "SDL_waylandevents_c.h"
- #include "SDL_waylandwindow.h"
- #include "SDL_waylandmouse.h"
- #include "pointer-constraints-unstable-v1-client-protocol.h"
- #include "relative-pointer-unstable-v1-client-protocol.h"
- #include "xdg-shell-client-protocol.h"
- #include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
- #include "text-input-unstable-v3-client-protocol.h"
- #include "tablet-v2-client-protocol.h"
- #include "primary-selection-unstable-v1-client-protocol.h"
- #include "input-timestamps-unstable-v1-client-protocol.h"
- #ifdef HAVE_LIBDECOR_H
- #include <libdecor.h>
- #endif
- #ifdef SDL_INPUT_LINUXEV
- #include <linux/input.h>
- #else
- #define BTN_LEFT (0x110)
- #define BTN_RIGHT (0x111)
- #define BTN_MIDDLE (0x112)
- #define BTN_SIDE (0x113)
- #define BTN_EXTRA (0x114)
- #endif
- #include "../../events/SDL_keysym_to_scancode_c.h"
- #include "../../events/imKStoUCS.h"
- #include <errno.h>
- #include <sys/mman.h>
- #include <unistd.h>
- #include <xkbcommon/xkbcommon-compose.h>
- #include <xkbcommon/xkbcommon.h>
- #include "cursor-shape-v1-client-protocol.h"
- // Weston uses a ratio of 10 units per scroll tick
- #define WAYLAND_WHEEL_AXIS_UNIT 10
- #ifndef XKB_MOD_NAME_MOD3
- #define XKB_MOD_NAME_MOD3 "Mod3"
- #endif
- #ifndef XKB_MOD_NAME_MOD5
- #define XKB_MOD_NAME_MOD5 "Mod5"
- #endif
- // Keyboard and mouse names to match XWayland
- #define WAYLAND_DEFAULT_KEYBOARD_NAME "Virtual core keyboard"
- #define WAYLAND_DEFAULT_POINTER_NAME "Virtual core pointer"
- // Focus clickthrough timeout
- #define WAYLAND_FOCUS_CLICK_TIMEOUT_NS SDL_MS_TO_NS(10)
- struct SDL_WaylandTouchPoint
- {
- SDL_TouchID id;
- wl_fixed_t fx;
- wl_fixed_t fy;
- struct wl_surface *surface;
- struct wl_list link;
- };
- static struct wl_list touch_points;
- static void touch_add(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface *surface)
- {
- struct SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint));
- SDL_zerop(tp);
- tp->id = id;
- tp->fx = fx;
- tp->fy = fy;
- tp->surface = surface;
- WAYLAND_wl_list_insert(&touch_points, &tp->link);
- }
- static void touch_update(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface **surface)
- {
- struct SDL_WaylandTouchPoint *tp;
- wl_list_for_each (tp, &touch_points, link) {
- if (tp->id == id) {
- tp->fx = fx;
- tp->fy = fy;
- if (surface) {
- *surface = tp->surface;
- }
- break;
- }
- }
- }
- static void touch_del(SDL_TouchID id, wl_fixed_t *fx, wl_fixed_t *fy, struct wl_surface **surface)
- {
- struct SDL_WaylandTouchPoint *tp;
- wl_list_for_each (tp, &touch_points, link) {
- if (tp->id == id) {
- if (fx) {
- *fx = tp->fx;
- }
- if (fy) {
- *fy = tp->fy;
- }
- if (surface) {
- *surface = tp->surface;
- }
- WAYLAND_wl_list_remove(&tp->link);
- SDL_free(tp);
- break;
- }
- }
- }
- static bool Wayland_SurfaceHasActiveTouches(struct wl_surface *surface)
- {
- struct SDL_WaylandTouchPoint *tp;
- wl_list_for_each (tp, &touch_points, link) {
- if (tp->surface == surface) {
- return true;
- }
- }
- return false;
- }
- static Uint64 Wayland_GetEventTimestamp(Uint64 nsTimestamp)
- {
- static Uint64 timestamp_offset = 0;
- const Uint64 now = SDL_GetTicksNS();
- if (!timestamp_offset) {
- timestamp_offset = (now - nsTimestamp);
- }
- nsTimestamp += timestamp_offset;
- if (nsTimestamp > now) {
- timestamp_offset -= (nsTimestamp - now);
- nsTimestamp = now;
- }
- return nsTimestamp;
- }
- static void Wayland_input_timestamp_listener(void *data, struct zwp_input_timestamps_v1 *zwp_input_timestamps_v1,
- uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec)
- {
- *((Uint64 *)data) = ((((Uint64)tv_sec_hi << 32) | (Uint64)tv_sec_lo) * SDL_NS_PER_SECOND) + tv_nsec;
- }
- static const struct zwp_input_timestamps_v1_listener timestamp_listener = {
- Wayland_input_timestamp_listener
- };
- static Uint64 Wayland_EventTimestampMSToNS(Uint32 wl_timestamp_ms)
- {
- static Uint64 timestamp_offset = 0;
- static Uint32 last = 0;
- // Handle 32-bit timer rollover.
- if (wl_timestamp_ms < last) {
- timestamp_offset += SDL_MS_TO_NS(SDL_UINT64_C(0x100000000));
- }
- last = wl_timestamp_ms;
- return SDL_MS_TO_NS(wl_timestamp_ms) + timestamp_offset;
- }
- static Uint64 Wayland_GetKeyboardTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
- {
- if (wl_timestamp_ms) {
- return Wayland_GetEventTimestamp(input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
- }
- return 0;
- }
- static Uint64 Wayland_GetPointerTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
- {
- if (wl_timestamp_ms) {
- return Wayland_GetEventTimestamp(input->pointer_timestamp_ns ? input->pointer_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
- }
- return 0;
- }
- Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
- {
- if (wl_timestamp_ms) {
- return Wayland_GetEventTimestamp(input->touch_timestamp_ns ? input->touch_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
- }
- return 0;
- }
- void Wayland_RegisterTimestampListeners(struct SDL_WaylandInput *input)
- {
- SDL_VideoData *viddata = input->display;
- if (viddata->input_timestamps_manager) {
- if (input->keyboard && !input->keyboard_timestamps) {
- input->keyboard_timestamps = zwp_input_timestamps_manager_v1_get_keyboard_timestamps(viddata->input_timestamps_manager, input->keyboard);
- zwp_input_timestamps_v1_add_listener(input->keyboard_timestamps, ×tamp_listener, &input->keyboard_timestamp_ns);
- }
- if (input->pointer && !input->pointer_timestamps) {
- input->pointer_timestamps = zwp_input_timestamps_manager_v1_get_pointer_timestamps(viddata->input_timestamps_manager, input->pointer);
- zwp_input_timestamps_v1_add_listener(input->pointer_timestamps, ×tamp_listener, &input->pointer_timestamp_ns);
- }
- if (input->touch && !input->touch_timestamps) {
- input->touch_timestamps = zwp_input_timestamps_manager_v1_get_touch_timestamps(viddata->input_timestamps_manager, input->touch);
- zwp_input_timestamps_v1_add_listener(input->touch_timestamps, ×tamp_listener, &input->touch_timestamp_ns);
- }
- }
- }
- void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input)
- {
- SDL_VideoData *viddata = input->display;
- if (viddata->cursor_shape_manager) {
- if (input->pointer && !input->cursor_shape) {
- input->cursor_shape = wp_cursor_shape_manager_v1_get_pointer(viddata->cursor_shape_manager, input->pointer);
- }
- }
- }
- // Returns true if a key repeat event was due
- static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed)
- {
- bool ret = false;
- while (elapsed >= repeat_info->next_repeat_ns) {
- if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
- const Uint64 timestamp = repeat_info->base_time_ns + repeat_info->next_repeat_ns;
- SDL_SendKeyboardKeyIgnoreModifiers(timestamp, repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true);
- }
- if (repeat_info->text[0]) {
- SDL_SendKeyboardText(repeat_info->text);
- }
- repeat_info->next_repeat_ns += SDL_NS_PER_SECOND / (Uint64)repeat_info->repeat_rate;
- ret = true;
- }
- return ret;
- }
- static void keyboard_repeat_clear(SDL_WaylandKeyboardRepeat *repeat_info)
- {
- if (!repeat_info->is_initialized) {
- return;
- }
- repeat_info->is_key_down = false;
- }
- static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint32 wl_press_time_ms,
- Uint64 base_time_ns, uint32_t scancode, bool has_text, char text[8])
- {
- if (!repeat_info->is_initialized || !repeat_info->repeat_rate) {
- return;
- }
- repeat_info->is_key_down = true;
- repeat_info->keyboard_id = keyboard_id;
- repeat_info->key = key;
- repeat_info->wl_press_time_ms = wl_press_time_ms;
- repeat_info->base_time_ns = base_time_ns;
- repeat_info->sdl_press_time_ns = SDL_GetTicksNS();
- repeat_info->next_repeat_ns = SDL_MS_TO_NS(repeat_info->repeat_delay_ms);
- repeat_info->scancode = scancode;
- if (has_text) {
- SDL_memcpy(repeat_info->text, text, sizeof(repeat_info->text));
- } else {
- repeat_info->text[0] = '\0';
- }
- }
- static uint32_t keyboard_repeat_get_key(SDL_WaylandKeyboardRepeat *repeat_info)
- {
- if (repeat_info->is_initialized && repeat_info->is_key_down) {
- return repeat_info->key;
- }
- return 0;
- }
- static void keyboard_repeat_set_text(SDL_WaylandKeyboardRepeat *repeat_info, const char text[8])
- {
- if (repeat_info->is_initialized) {
- SDL_copyp(repeat_info->text, text);
- }
- }
- static bool keyboard_repeat_is_set(SDL_WaylandKeyboardRepeat *repeat_info)
- {
- return repeat_info->is_initialized && repeat_info->is_key_down;
- }
- static bool keyboard_repeat_key_is_set(SDL_WaylandKeyboardRepeat *repeat_info, uint32_t key)
- {
- return repeat_info->is_initialized && repeat_info->is_key_down && key == repeat_info->key;
- }
- static void sync_done_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
- {
- // Nothing to do, just destroy the callback
- wl_callback_destroy(callback);
- }
- static struct wl_callback_listener sync_listener = {
- sync_done_handler
- };
- void Wayland_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_VideoData *d = _this->internal;
- /* Queue a sync event to unblock the event queue fd if it's empty and being waited on.
- * TODO: Maybe use a pipe to avoid the compositor roundtrip?
- */
- struct wl_callback *cb = wl_display_sync(d->display);
- wl_callback_add_listener(cb, &sync_listener, NULL);
- WAYLAND_wl_display_flush(d->display);
- }
- static int dispatch_queued_events(SDL_VideoData *viddata)
- {
- int rc;
- /*
- * NOTE: When reconnection is implemented, check if libdecor needs to be
- * involved in the reconnection process.
- */
- #ifdef HAVE_LIBDECOR_H
- if (viddata->shell.libdecor) {
- libdecor_dispatch(viddata->shell.libdecor, 0);
- }
- #endif
- rc = WAYLAND_wl_display_dispatch_pending(viddata->display);
- return rc >= 0 ? 1 : rc;
- }
- int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
- {
- SDL_VideoData *d = _this->internal;
- struct SDL_WaylandInput *input = d->input;
- bool key_repeat_active = false;
- WAYLAND_wl_display_flush(d->display);
- #ifdef SDL_USE_IME
- SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
- if (!d->text_input_manager && keyboard_focus && SDL_TextInputActive(keyboard_focus)) {
- SDL_IME_PumpEvents();
- }
- #endif
- #ifdef SDL_USE_LIBDBUS
- SDL_DBus_PumpEvents();
- #endif
- // If key repeat is active, we'll need to cap our maximum wait time to handle repeats
- if (input && keyboard_repeat_is_set(&input->keyboard_repeat)) {
- const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns;
- if (keyboard_repeat_handle(&input->keyboard_repeat, elapsed)) {
- // A repeat key event was already due
- return 1;
- } else {
- const Uint64 next_repeat_wait_time = (input->keyboard_repeat.next_repeat_ns - elapsed) + 1;
- if (timeoutNS >= 0) {
- timeoutNS = SDL_min(timeoutNS, next_repeat_wait_time);
- } else {
- timeoutNS = next_repeat_wait_time;
- }
- key_repeat_active = true;
- }
- }
- /* wl_display_prepare_read() will return -1 if the default queue is not empty.
- * If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
- if (WAYLAND_wl_display_prepare_read(d->display) == 0) {
- // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait
- int err = SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS);
- if (err > 0) {
- // There are new events available to read
- WAYLAND_wl_display_read_events(d->display);
- return dispatch_queued_events(d);
- } else if (err == 0) {
- // No events available within the timeout
- WAYLAND_wl_display_cancel_read(d->display);
- // If key repeat is active, we might have woken up to generate a key event
- if (key_repeat_active) {
- const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns;
- if (keyboard_repeat_handle(&input->keyboard_repeat, elapsed)) {
- return 1;
- }
- }
- return 0;
- } else {
- // Error returned from poll()/select()
- WAYLAND_wl_display_cancel_read(d->display);
- 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;
- }
- }
- } else {
- // We already had pending events
- return dispatch_queued_events(d);
- }
- }
- void Wayland_PumpEvents(SDL_VideoDevice *_this)
- {
- SDL_VideoData *d = _this->internal;
- struct SDL_WaylandInput *input = d->input;
- int err;
- #ifdef SDL_USE_IME
- SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
- if (!d->text_input_manager && keyboard_focus && SDL_TextInputActive(keyboard_focus)) {
- SDL_IME_PumpEvents();
- }
- #endif
- #ifdef SDL_USE_LIBDBUS
- SDL_DBus_PumpEvents();
- #endif
- #ifdef HAVE_LIBDECOR_H
- if (d->shell.libdecor) {
- libdecor_dispatch(d->shell.libdecor, 0);
- }
- #endif
- WAYLAND_wl_display_flush(d->display);
- /* wl_display_prepare_read() will return -1 if the default queue is not empty.
- * If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
- if (WAYLAND_wl_display_prepare_read(d->display) == 0) {
- if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_IOR_READ, 0) > 0) {
- WAYLAND_wl_display_read_events(d->display);
- } else {
- WAYLAND_wl_display_cancel_read(d->display);
- }
- }
- // Dispatch any pre-existing pending events or new events we may have read
- err = WAYLAND_wl_display_dispatch_pending(d->display);
- if (input && keyboard_repeat_is_set(&input->keyboard_repeat)) {
- const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns;
- keyboard_repeat_handle(&input->keyboard_repeat, elapsed);
- }
- if (err < 0 && !d->display_disconnected) {
- /* Something has failed with the Wayland connection -- for example,
- * the compositor may have shut down and closed its end of the socket,
- * or there is a library-specific error.
- *
- * Try to recover once, then quit.
- */
- if (!Wayland_VideoReconnect(_this)) {
- d->display_disconnected = 1;
- SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Wayland display connection closed by server (fatal)");
- /* Only send a single quit message, as application shutdown might call
- * SDL_PumpEvents
- */
- SDL_SendQuit();
- }
- }
- }
- static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
- uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
- {
- struct SDL_WaylandInput *input = data;
- SDL_WindowData *window_data = input->pointer_focus;
- SDL_Window *window = window_data ? window_data->sdlwindow : NULL;
- input->sx_w = sx_w;
- input->sy_w = sy_w;
- if (input->pointer_focus) {
- const float sx = (float)(wl_fixed_to_double(sx_w) * window_data->pointer_scale.x);
- const float sy = (float)(wl_fixed_to_double(sy_w) * window_data->pointer_scale.y);
- SDL_SendMouseMotion(Wayland_GetPointerTimestamp(input, time), window_data->sdlwindow, input->pointer_id, false, sx, sy);
- }
- if (window && window->hit_test) {
- const SDL_Point point = { (int)SDL_floor(wl_fixed_to_double(sx_w) * window_data->pointer_scale.x),
- (int)SDL_floor(wl_fixed_to_double(sy_w) * window_data->pointer_scale.y) };
- SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
- if (rc == window_data->hit_test_result) {
- return;
- }
- Wayland_SetHitTestCursor(rc);
- window_data->hit_test_result = rc;
- }
- }
- static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
- uint32_t serial, struct wl_surface *surface,
- wl_fixed_t sx_w, wl_fixed_t sy_w)
- {
- struct SDL_WaylandInput *input = data;
- SDL_WindowData *window;
- if (!surface) {
- // enter event for a window we've just destroyed
- return;
- }
- /* This handler will be called twice in Wayland 1.4
- * Once for the window surface which has valid user data
- * and again for the mouse cursor surface which does not have valid user data
- * We ignore the later
- */
- window = Wayland_GetWindowDataForOwnedSurface(surface);
- if (window) {
- input->pointer_focus = window;
- input->pointer_enter_serial = serial;
- SDL_SetMouseFocus(window->sdlwindow);
- /* In the case of e.g. a pointer confine warp, we may receive an enter
- * event with no following motion event, but with the new coordinates
- * as part of the enter event.
- *
- * FIXME: This causes a movement event with an anomalous timestamp when
- * the cursor enters the window.
- */
- pointer_handle_motion(data, pointer, 0, sx_w, sy_w);
- /* If the cursor was changed while our window didn't have pointer
- * focus, we might need to trigger another call to
- * wl_pointer_set_cursor() for the new cursor to be displayed. */
- Wayland_SetHitTestCursor(window->hit_test_result);
- }
- }
- static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
- uint32_t serial, struct wl_surface *surface)
- {
- struct SDL_WaylandInput *input = data;
- if (!surface) {
- return;
- }
- if (input->pointer_focus) {
- SDL_WindowData *wind = Wayland_GetWindowDataForOwnedSurface(surface);
- if (wind) {
- // Clear the capture flag and raise all buttons
- wind->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
- input->buttons_pressed = 0;
- SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_LEFT, false);
- SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_RIGHT, false);
- SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_MIDDLE, false);
- SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_X1, false);
- SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_X2, false);
- }
- /* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event.
- * Don't relinquish focus if the surface has active touches, as the compositor is just transitioning from mouse to touch mode.
- */
- if (!Wayland_SurfaceHasActiveTouches(surface)) {
- SDL_SetMouseFocus(NULL);
- }
- input->pointer_focus = NULL;
- }
- }
- static bool ProcessHitTest(SDL_WindowData *window_data,
- struct wl_seat *seat,
- wl_fixed_t sx_w, wl_fixed_t sy_w,
- uint32_t serial)
- {
- SDL_Window *window = window_data->sdlwindow;
- if (window->hit_test) {
- static const uint32_t directions[] = {
- XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_TOP,
- XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT, XDG_TOPLEVEL_RESIZE_EDGE_RIGHT,
- XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM,
- XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_LEFT
- };
- #ifdef HAVE_LIBDECOR_H
- static const uint32_t directions_libdecor[] = {
- LIBDECOR_RESIZE_EDGE_TOP_LEFT, LIBDECOR_RESIZE_EDGE_TOP,
- LIBDECOR_RESIZE_EDGE_TOP_RIGHT, LIBDECOR_RESIZE_EDGE_RIGHT,
- LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT, LIBDECOR_RESIZE_EDGE_BOTTOM,
- LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT, LIBDECOR_RESIZE_EDGE_LEFT
- };
- #endif
- switch (window_data->hit_test_result) {
- case SDL_HITTEST_DRAGGABLE:
- #ifdef HAVE_LIBDECOR_H
- if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) {
- if (window_data->shell_surface.libdecor.frame) {
- libdecor_frame_move(window_data->shell_surface.libdecor.frame,
- seat,
- serial);
- }
- } else
- #endif
- if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) {
- if (window_data->shell_surface.xdg.toplevel.xdg_toplevel) {
- xdg_toplevel_move(window_data->shell_surface.xdg.toplevel.xdg_toplevel,
- seat,
- serial);
- }
- }
- 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:
- #ifdef HAVE_LIBDECOR_H
- if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) {
- if (window_data->shell_surface.libdecor.frame) {
- libdecor_frame_resize(window_data->shell_surface.libdecor.frame,
- seat,
- serial,
- directions_libdecor[window_data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]);
- }
- } else
- #endif
- if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) {
- if (window_data->shell_surface.xdg.toplevel.xdg_toplevel) {
- xdg_toplevel_resize(window_data->shell_surface.xdg.toplevel.xdg_toplevel,
- seat,
- serial,
- directions[window_data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]);
- }
- }
- return true;
- default:
- return false;
- }
- }
- return false;
- }
- static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial,
- uint32_t time, uint32_t button, uint32_t state_w)
- {
- SDL_WindowData *window = input->pointer_focus;
- enum wl_pointer_button_state state = state_w;
- Uint64 timestamp = Wayland_GetPointerTimestamp(input, time);
- Uint8 sdl_button;
- const bool down = (state != 0);
- switch (button) {
- case BTN_LEFT:
- sdl_button = SDL_BUTTON_LEFT;
- break;
- case BTN_MIDDLE:
- sdl_button = SDL_BUTTON_MIDDLE;
- break;
- case BTN_RIGHT:
- sdl_button = SDL_BUTTON_RIGHT;
- break;
- case BTN_SIDE:
- sdl_button = SDL_BUTTON_X1;
- break;
- case BTN_EXTRA:
- sdl_button = SDL_BUTTON_X2;
- break;
- default:
- return;
- }
- if (window) {
- SDL_VideoData *viddata = window->waylandData;
- bool ignore_click = false;
- if (state) {
- Wayland_UpdateImplicitGrabSerial(input, serial);
- input->buttons_pressed |= SDL_BUTTON_MASK(sdl_button);
- } else {
- input->buttons_pressed &= ~(SDL_BUTTON_MASK(sdl_button));
- }
- if (sdl_button == SDL_BUTTON_LEFT &&
- ProcessHitTest(input->pointer_focus, input->seat, input->sx_w, input->sy_w, serial)) {
- return; // don't pass this event on to app.
- }
- // Possibly ignore this click if it was to gain focus.
- if (window->last_focus_event_time_ns) {
- if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
- (SDL_GetTicksNS() - window->last_focus_event_time_ns) < WAYLAND_FOCUS_CLICK_TIMEOUT_NS) {
- ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false);
- }
- window->last_focus_event_time_ns = 0;
- }
- /* Wayland won't let you "capture" the mouse, but it will automatically track
- * the mouse outside the window if you drag outside of it, until you let go
- * of all buttons (even if you add or remove presses outside the window, as
- * long as any button is still down, the capture remains).
- *
- * The mouse is not captured in relative mode.
- */
- if (!viddata->relative_mouse_mode) {
- if (input->buttons_pressed != 0) {
- window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
- } else {
- window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
- }
- }
- if (!ignore_click) {
- SDL_SendMouseButton(timestamp, window->sdlwindow, input->pointer_id, sdl_button, down);
- }
- }
- }
- static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
- uint32_t time, uint32_t button, uint32_t state_w)
- {
- struct SDL_WaylandInput *input = data;
- pointer_handle_button_common(input, serial, time, button, state_w);
- }
- static void pointer_handle_axis_common_v1(struct SDL_WaylandInput *input,
- uint32_t time, uint32_t axis, wl_fixed_t value)
- {
- SDL_WindowData *window = input->pointer_focus;
- const Uint64 timestamp = Wayland_GetPointerTimestamp(input, time);
- const enum wl_pointer_axis a = axis;
- if (input->pointer_focus) {
- float x, y;
- switch (a) {
- case WL_POINTER_AXIS_VERTICAL_SCROLL:
- x = 0;
- y = 0 - (float)wl_fixed_to_double(value);
- break;
- case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
- x = (float)wl_fixed_to_double(value);
- y = 0;
- break;
- default:
- return;
- }
- x /= WAYLAND_WHEEL_AXIS_UNIT;
- y /= WAYLAND_WHEEL_AXIS_UNIT;
- SDL_SendMouseWheel(timestamp, window->sdlwindow, input->pointer_id, x, y, SDL_MOUSEWHEEL_NORMAL);
- }
- }
- static void pointer_handle_axis_common(struct SDL_WaylandInput *input, enum SDL_WaylandAxisEvent type,
- uint32_t axis, wl_fixed_t value)
- {
- const enum wl_pointer_axis a = axis;
- if (input->pointer_focus) {
- switch (a) {
- case WL_POINTER_AXIS_VERTICAL_SCROLL:
- switch (type) {
- case AXIS_EVENT_VALUE120:
- /*
- * High resolution scroll event. The spec doesn't state that axis_value120
- * events are limited to one per frame, so the values are accumulated.
- */
- if (input->pointer_curr_axis_info.y_axis_type != AXIS_EVENT_VALUE120) {
- input->pointer_curr_axis_info.y_axis_type = AXIS_EVENT_VALUE120;
- input->pointer_curr_axis_info.y = 0.0f;
- }
- input->pointer_curr_axis_info.y += 0 - (float)wl_fixed_to_double(value);
- break;
- case AXIS_EVENT_DISCRETE:
- /*
- * This is a discrete axis event, so we process it and set the
- * flag to ignore future continuous axis events in this frame.
- */
- if (input->pointer_curr_axis_info.y_axis_type != AXIS_EVENT_DISCRETE) {
- input->pointer_curr_axis_info.y_axis_type = AXIS_EVENT_DISCRETE;
- input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value);
- }
- break;
- case AXIS_EVENT_CONTINUOUS:
- // Only process continuous events if no discrete events have been received.
- if (input->pointer_curr_axis_info.y_axis_type == AXIS_EVENT_CONTINUOUS) {
- input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value);
- }
- break;
- }
- break;
- case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
- switch (type) {
- case AXIS_EVENT_VALUE120:
- /*
- * High resolution scroll event. The spec doesn't state that axis_value120
- * events are limited to one per frame, so the values are accumulated.
- */
- if (input->pointer_curr_axis_info.x_axis_type != AXIS_EVENT_VALUE120) {
- input->pointer_curr_axis_info.x_axis_type = AXIS_EVENT_VALUE120;
- input->pointer_curr_axis_info.x = 0.0f;
- }
- input->pointer_curr_axis_info.x += (float)wl_fixed_to_double(value);
- break;
- case AXIS_EVENT_DISCRETE:
- /*
- * This is a discrete axis event, so we process it and set the
- * flag to ignore future continuous axis events in this frame.
- */
- if (input->pointer_curr_axis_info.x_axis_type != AXIS_EVENT_DISCRETE) {
- input->pointer_curr_axis_info.x_axis_type = AXIS_EVENT_DISCRETE;
- input->pointer_curr_axis_info.x = (float)wl_fixed_to_double(value);
- }
- break;
- case AXIS_EVENT_CONTINUOUS:
- // Only process continuous events if no discrete events have been received.
- if (input->pointer_curr_axis_info.x_axis_type == AXIS_EVENT_CONTINUOUS) {
- input->pointer_curr_axis_info.x = (float)wl_fixed_to_double(value);
- }
- break;
- }
- break;
- }
- }
- }
- static void pointer_handle_axis(void *data, struct wl_pointer *pointer,
- uint32_t time, uint32_t axis, wl_fixed_t value)
- {
- struct SDL_WaylandInput *input = data;
- if (wl_seat_get_version(input->seat) >= WL_POINTER_FRAME_SINCE_VERSION) {
- input->pointer_curr_axis_info.timestamp_ns = Wayland_GetPointerTimestamp(input, time);
- pointer_handle_axis_common(input, AXIS_EVENT_CONTINUOUS, axis, value);
- } else {
- pointer_handle_axis_common_v1(input, time, axis, value);
- }
- }
- static void pointer_handle_axis_relative_direction(void *data, struct wl_pointer *pointer,
- uint32_t axis, uint32_t axis_relative_direction)
- {
- struct SDL_WaylandInput *input = data;
- if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
- return;
- }
- switch (axis_relative_direction) {
- case WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL:
- input->pointer_curr_axis_info.direction = SDL_MOUSEWHEEL_NORMAL;
- break;
- case WL_POINTER_AXIS_RELATIVE_DIRECTION_INVERTED:
- input->pointer_curr_axis_info.direction = SDL_MOUSEWHEEL_FLIPPED;
- break;
- }
- }
- static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
- {
- struct SDL_WaylandInput *input = data;
- SDL_WindowData *window = input->pointer_focus;
- float x, y;
- SDL_MouseWheelDirection direction = input->pointer_curr_axis_info.direction;
- switch (input->pointer_curr_axis_info.x_axis_type) {
- case AXIS_EVENT_CONTINUOUS:
- x = input->pointer_curr_axis_info.x / WAYLAND_WHEEL_AXIS_UNIT;
- break;
- case AXIS_EVENT_DISCRETE:
- x = input->pointer_curr_axis_info.x;
- break;
- case AXIS_EVENT_VALUE120:
- x = input->pointer_curr_axis_info.x / 120.0f;
- break;
- default:
- x = 0.0f;
- break;
- }
- switch (input->pointer_curr_axis_info.y_axis_type) {
- case AXIS_EVENT_CONTINUOUS:
- y = input->pointer_curr_axis_info.y / WAYLAND_WHEEL_AXIS_UNIT;
- break;
- case AXIS_EVENT_DISCRETE:
- y = input->pointer_curr_axis_info.y;
- break;
- case AXIS_EVENT_VALUE120:
- y = input->pointer_curr_axis_info.y / 120.0f;
- break;
- default:
- y = 0.0f;
- break;
- }
- // clear pointer_curr_axis_info for next frame
- SDL_memset(&input->pointer_curr_axis_info, 0, sizeof(input->pointer_curr_axis_info));
- if (x != 0.0f || y != 0.0f) {
- SDL_SendMouseWheel(input->pointer_curr_axis_info.timestamp_ns,
- window->sdlwindow, input->pointer_id, x, y, direction);
- }
- }
- static void pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
- uint32_t axis_source)
- {
- // unimplemented
- }
- static void pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
- uint32_t time, uint32_t axis)
- {
- // unimplemented
- }
- static void pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
- uint32_t axis, int32_t discrete)
- {
- struct SDL_WaylandInput *input = data;
- pointer_handle_axis_common(input, AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete));
- }
- static void pointer_handle_axis_value120(void *data, struct wl_pointer *pointer,
- uint32_t axis, int32_t value120)
- {
- struct SDL_WaylandInput *input = data;
- pointer_handle_axis_common(input, AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120));
- }
- static const struct wl_pointer_listener pointer_listener = {
- pointer_handle_enter,
- pointer_handle_leave,
- pointer_handle_motion,
- pointer_handle_button,
- pointer_handle_axis,
- pointer_handle_frame, // Version 5
- pointer_handle_axis_source, // Version 5
- pointer_handle_axis_stop, // Version 5
- pointer_handle_axis_discrete, // Version 5
- pointer_handle_axis_value120, // Version 8
- pointer_handle_axis_relative_direction // Version 9
- };
- static void relative_pointer_handle_relative_motion(void *data,
- struct zwp_relative_pointer_v1 *pointer,
- uint32_t time_hi,
- uint32_t time_lo,
- wl_fixed_t dx_w,
- wl_fixed_t dy_w,
- wl_fixed_t dx_unaccel_w,
- wl_fixed_t dy_unaccel_w)
- {
- struct SDL_WaylandInput *input = data;
- SDL_VideoData *d = input->display;
- SDL_WindowData *window = input->pointer_focus;
- // Relative pointer event times are in microsecond granularity.
- const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
- if (input->pointer_focus && d->relative_mouse_mode) {
- double dx;
- double dy;
- if (!SDL_GetMouse()->enable_relative_system_scale) {
- dx = wl_fixed_to_double(dx_unaccel_w);
- dy = wl_fixed_to_double(dy_unaccel_w);
- } else {
- dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x;
- dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y;
- }
- SDL_SendMouseMotion(timestamp, window->sdlwindow, input->pointer_id, true, (float)dx, (float)dy);
- }
- }
- static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
- relative_pointer_handle_relative_motion,
- };
- static void locked_pointer_locked(void *data,
- struct zwp_locked_pointer_v1 *locked_pointer)
- {
- }
- static void locked_pointer_unlocked(void *data,
- struct zwp_locked_pointer_v1 *locked_pointer)
- {
- }
- static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
- locked_pointer_locked,
- locked_pointer_unlocked,
- };
- bool Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
- {
- SDL_WindowData *w = window->internal;
- SDL_VideoData *d = input->display;
- if (!d->pointer_constraints || !input->pointer) {
- return false;
- }
- if (!w->locked_pointer) {
- if (w->confined_pointer) {
- // If the pointer is already confined to the surface, the lock will fail with a protocol error.
- Wayland_input_unconfine_pointer(input, window);
- }
- w->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints,
- w->surface,
- input->pointer,
- NULL,
- ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
- zwp_locked_pointer_v1_add_listener(w->locked_pointer,
- &locked_pointer_listener,
- window);
- }
- return true;
- }
- bool Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
- {
- SDL_WindowData *w = window->internal;
- if (w->locked_pointer) {
- zwp_locked_pointer_v1_destroy(w->locked_pointer);
- w->locked_pointer = NULL;
- }
- // Restore existing pointer confinement.
- Wayland_input_confine_pointer(input, window);
- return true;
- }
- static void pointer_confine_destroy(SDL_Window *window)
- {
- SDL_WindowData *w = window->internal;
- if (w->confined_pointer) {
- zwp_confined_pointer_v1_destroy(w->confined_pointer);
- w->confined_pointer = NULL;
- }
- }
- static void confined_pointer_confined(void *data,
- struct zwp_confined_pointer_v1 *confined_pointer)
- {
- }
- static void confined_pointer_unconfined(void *data,
- struct zwp_confined_pointer_v1 *confined_pointer)
- {
- }
- static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = {
- confined_pointer_confined,
- confined_pointer_unconfined,
- };
- static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t serial,
- uint32_t timestamp, struct wl_surface *surface,
- int id, wl_fixed_t fx, wl_fixed_t fy)
- {
- struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data;
- SDL_WindowData *window_data;
- // Check that this surface is valid.
- if (!surface) {
- return;
- }
- touch_add(id, fx, fy, surface);
- Wayland_UpdateImplicitGrabSerial(input, serial);
- window_data = Wayland_GetWindowDataForOwnedSurface(surface);
- if (window_data) {
- float x, y;
- if (window_data->current.logical_width <= 1) {
- x = 0.5f;
- } else {
- x = (float)wl_fixed_to_double(fx) / (window_data->current.logical_width - 1);
- }
- if (window_data->current.logical_height <= 1) {
- y = 0.5f;
- } else {
- y = (float)wl_fixed_to_double(fy) / (window_data->current.logical_height - 1);
- }
- SDL_SetMouseFocus(window_data->sdlwindow);
- SDL_SendTouch(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
- (SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_DOWN, x, y, 1.0f);
- }
- }
- static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial,
- uint32_t timestamp, int id)
- {
- struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data;
- wl_fixed_t fx = 0, fy = 0;
- struct wl_surface *surface = NULL;
- touch_del(id, &fx, &fy, &surface);
- if (surface) {
- SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(surface);
- if (window_data) {
- const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width;
- const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height;
- SDL_SendTouch(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
- (SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_UP, x, y, 0.0f);
- /* If the seat lacks pointer focus, the seat's keyboard focus is another window or NULL, this window currently
- * has mouse focus, and the surface has no active touch events, consider mouse focus to be lost.
- */
- if (!input->pointer_focus && input->keyboard_focus != window_data &&
- SDL_GetMouseFocus() == window_data->sdlwindow && !Wayland_SurfaceHasActiveTouches(surface)) {
- SDL_SetMouseFocus(NULL);
- }
- }
- }
- }
- static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t timestamp,
- int id, wl_fixed_t fx, wl_fixed_t fy)
- {
- struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data;
- struct wl_surface *surface = NULL;
- touch_update(id, fx, fy, &surface);
- if (surface) {
- SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(surface);
- if (window_data) {
- const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width;
- const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height;
- SDL_SendTouchMotion(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
- (SDL_FingerID)(id + 1), window_data->sdlwindow, x, y, 1.0f);
- }
- }
- }
- static void touch_handler_frame(void *data, struct wl_touch *touch)
- {
- }
- static void touch_handler_cancel(void *data, struct wl_touch *touch)
- {
- struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data;
- struct SDL_WaylandTouchPoint *tp, *temp;
- wl_list_for_each_safe (tp, temp, &touch_points, link) {
- bool removed = false;
- if (tp->surface) {
- SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(tp->surface);
- if (window_data) {
- const float x = (float)(wl_fixed_to_double(tp->fx) / window_data->current.logical_width);
- const float y = (float)(wl_fixed_to_double(tp->fy) / window_data->current.logical_height);
- SDL_SendTouch(0, (SDL_TouchID)(uintptr_t)touch,
- (SDL_FingerID)(tp->id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_CANCELED, x, y, 0.0f);
- // Remove the touch from the list before checking for still-active touches on the surface.
- WAYLAND_wl_list_remove(&tp->link);
- removed = true;
- /* If the seat lacks pointer focus, the seat's keyboard focus is another window or NULL, this window currently
- * has mouse focus, and the surface has no active touch events, consider mouse focus to be lost.
- */
- if (!input->pointer_focus && input->keyboard_focus != window_data &&
- SDL_GetMouseFocus() == window_data->sdlwindow && !Wayland_SurfaceHasActiveTouches(tp->surface)) {
- SDL_SetMouseFocus(NULL);
- }
- }
- }
- if (!removed) {
- WAYLAND_wl_list_remove(&tp->link);
- }
- SDL_free(tp);
- }
- }
- static const struct wl_touch_listener touch_listener = {
- touch_handler_down,
- touch_handler_up,
- touch_handler_motion,
- touch_handler_frame,
- touch_handler_cancel,
- NULL, // shape
- NULL, // orientation
- };
- typedef struct Wayland_Keymap
- {
- SDL_Keymap *keymap;
- struct xkb_state *state;
- SDL_Keymod modstate;
- } Wayland_Keymap;
- static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)
- {
- Wayland_Keymap *sdlKeymap = (Wayland_Keymap *)data;
- const xkb_keysym_t *syms;
- const SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8));
- if (scancode == SDL_SCANCODE_UNKNOWN) {
- return;
- }
- if (WAYLAND_xkb_state_key_get_syms(sdlKeymap->state, key, &syms) > 0) {
- SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(syms[0], key, sdlKeymap->modstate);
- if (!keycode) {
- switch (scancode) {
- case SDL_SCANCODE_RETURN:
- keycode = SDLK_RETURN;
- break;
- case SDL_SCANCODE_ESCAPE:
- keycode = SDLK_ESCAPE;
- break;
- case SDL_SCANCODE_BACKSPACE:
- keycode = SDLK_BACKSPACE;
- break;
- case SDL_SCANCODE_DELETE:
- keycode = SDLK_DELETE;
- break;
- default:
- keycode = SDL_SCANCODE_TO_KEYCODE(scancode);
- break;
- }
- }
- SDL_SetKeymapEntry(sdlKeymap->keymap, scancode, sdlKeymap->modstate, keycode);
- }
- }
- static void Wayland_UpdateKeymap(struct SDL_WaylandInput *input)
- {
- struct Keymod_masks
- {
- SDL_Keymod sdl_mask;
- xkb_mod_mask_t xkb_mask;
- } const keymod_masks[] = {
- { SDL_KMOD_NONE, 0 },
- { SDL_KMOD_SHIFT, input->xkb.idx_shift },
- { SDL_KMOD_CAPS, input->xkb.idx_caps },
- { SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_shift | input->xkb.idx_caps },
- { SDL_KMOD_MODE, input->xkb.idx_mod5 },
- { SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mod5 | input->xkb.idx_shift },
- { SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mod5 | input->xkb.idx_caps },
- { SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod5 | input->xkb.idx_shift | input->xkb.idx_caps },
- { SDL_KMOD_LEVEL5, input->xkb.idx_mod3 },
- { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, input->xkb.idx_mod3 | input->xkb.idx_shift },
- { SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_caps },
- { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_shift | input->xkb.idx_caps },
- { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, input->xkb.idx_mod3 | input->xkb.idx_mod5 },
- { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_shift },
- { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_caps },
- { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_shift | input->xkb.idx_caps },
- };
- if (!input->keyboard_is_virtual) {
- Wayland_Keymap keymap;
- keymap.keymap = SDL_CreateKeymap();
- if (!keymap.keymap) {
- return;
- }
- keymap.state = WAYLAND_xkb_state_new(input->xkb.keymap);
- if (!keymap.state) {
- SDL_SetError("failed to create XKB state");
- SDL_DestroyKeymap(keymap.keymap);
- return;
- }
- for (int i = 0; i < SDL_arraysize(keymod_masks); ++i) {
- keymap.modstate = keymod_masks[i].sdl_mask;
- WAYLAND_xkb_state_update_mask(keymap.state,
- keymod_masks[i].xkb_mask & (input->xkb.idx_shift | input->xkb.idx_mod5 | input->xkb.idx_mod3), 0, keymod_masks[i].xkb_mask & input->xkb.idx_caps,
- 0, 0, input->xkb.current_group);
- WAYLAND_xkb_keymap_key_for_each(input->xkb.keymap,
- Wayland_keymap_iter,
- &keymap);
- }
- WAYLAND_xkb_state_unref(keymap.state);
- SDL_SetKeymap(keymap.keymap, true);
- } else {
- // Virtual keyboards use the default keymap.
- SDL_SetKeymap(NULL, true);
- }
- }
- static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
- uint32_t format, int fd, uint32_t size)
- {
- struct SDL_WaylandInput *input = data;
- char *map_str;
- const char *locale;
- if (!data) {
- close(fd);
- return;
- }
- if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
- close(fd);
- return;
- }
- map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (map_str == MAP_FAILED) {
- close(fd);
- return;
- }
- if (input->xkb.keymap != NULL) {
- /* if there's already a keymap loaded, throw it away rather than leaking it before
- * parsing the new one
- */
- WAYLAND_xkb_keymap_unref(input->xkb.keymap);
- input->xkb.keymap = NULL;
- }
- input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context,
- map_str,
- XKB_KEYMAP_FORMAT_TEXT_V1,
- 0);
- munmap(map_str, size);
- close(fd);
- if (!input->xkb.keymap) {
- SDL_SetError("failed to compile keymap");
- return;
- }
- #define GET_MOD_INDEX(mod) \
- WAYLAND_xkb_keymap_mod_get_index(input->xkb.keymap, XKB_MOD_NAME_##mod)
- input->xkb.idx_shift = 1 << GET_MOD_INDEX(SHIFT);
- input->xkb.idx_ctrl = 1 << GET_MOD_INDEX(CTRL);
- input->xkb.idx_alt = 1 << GET_MOD_INDEX(ALT);
- input->xkb.idx_gui = 1 << GET_MOD_INDEX(LOGO);
- input->xkb.idx_mod3 = 1 << GET_MOD_INDEX(MOD3);
- input->xkb.idx_mod5 = 1 << GET_MOD_INDEX(MOD5);
- input->xkb.idx_num = 1 << GET_MOD_INDEX(NUM);
- input->xkb.idx_caps = 1 << GET_MOD_INDEX(CAPS);
- #undef GET_MOD_INDEX
- if (input->xkb.state != NULL) {
- /* if there's already a state, throw it away rather than leaking it before
- * trying to create a new one with the new keymap.
- */
- WAYLAND_xkb_state_unref(input->xkb.state);
- input->xkb.state = NULL;
- }
- input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap);
- if (!input->xkb.state) {
- SDL_SetError("failed to create XKB state");
- WAYLAND_xkb_keymap_unref(input->xkb.keymap);
- input->xkb.keymap = NULL;
- return;
- }
- /*
- * Assume that a nameless layout implies a virtual keyboard with an arbitrary layout.
- * TODO: Use a better method of detection?
- */
- input->keyboard_is_virtual = WAYLAND_xkb_keymap_layout_get_name(input->xkb.keymap, 0) == NULL;
- // Update the keymap if changed.
- if (input->xkb.current_group != XKB_GROUP_INVALID) {
- Wayland_UpdateKeymap(input);
- }
- /*
- * See https://blogs.s-osg.org/compose-key-support-weston/
- * for further explanation on dead keys in Wayland.
- */
- // Look up the preferred locale, falling back to "C" as default
- locale = SDL_getenv("LC_ALL");
- if (!locale) {
- locale = SDL_getenv("LC_CTYPE");
- if (!locale) {
- locale = SDL_getenv("LANG");
- if (!locale) {
- locale = "C";
- }
- }
- }
- // Set up XKB compose table
- if (input->xkb.compose_table != NULL) {
- WAYLAND_xkb_compose_table_unref(input->xkb.compose_table);
- input->xkb.compose_table = NULL;
- }
- input->xkb.compose_table = WAYLAND_xkb_compose_table_new_from_locale(input->display->xkb_context,
- locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
- if (input->xkb.compose_table) {
- // Set up XKB compose state
- if (input->xkb.compose_state != NULL) {
- WAYLAND_xkb_compose_state_unref(input->xkb.compose_state);
- input->xkb.compose_state = NULL;
- }
- input->xkb.compose_state = WAYLAND_xkb_compose_state_new(input->xkb.compose_table,
- XKB_COMPOSE_STATE_NO_FLAGS);
- if (!input->xkb.compose_state) {
- SDL_SetError("could not create XKB compose state");
- WAYLAND_xkb_compose_table_unref(input->xkb.compose_table);
- input->xkb.compose_table = NULL;
- }
- }
- }
- /*
- * Virtual keyboards can have arbitrary layouts, arbitrary scancodes/keycodes, etc...
- * Key presses from these devices must be looked up by their keysym value.
- */
- static SDL_Scancode Wayland_GetScancodeForKey(struct SDL_WaylandInput *input, uint32_t key)
- {
- SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
- if (!input->keyboard_is_virtual) {
- scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, key);
- } else {
- const xkb_keysym_t *syms;
- if (WAYLAND_xkb_keymap_key_get_syms_by_level(input->xkb.keymap, key + 8, input->xkb.current_group, 0, &syms) > 0) {
- scancode = SDL_GetScancodeFromKeySym(syms[0], key);
- }
- }
- return scancode;
- }
- static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_pressed)
- {
- /* Handle explicit pressed modifier state. This will correct the modifier state
- * if common modifier keys were remapped and the modifiers presumed to be set
- * during a key press event were incorrect, or if the modifier was set to the
- * pressed state via means other than pressing the physical key.
- */
- if (!key_pressed) {
- if (input->xkb.wl_pressed_modifiers & input->xkb.idx_shift) {
- if (!(input->pressed_modifiers & SDL_KMOD_SHIFT)) {
- input->pressed_modifiers |= SDL_KMOD_SHIFT;
- }
- } else {
- input->pressed_modifiers &= ~SDL_KMOD_SHIFT;
- }
- if (input->xkb.wl_pressed_modifiers & input->xkb.idx_ctrl) {
- if (!(input->pressed_modifiers & SDL_KMOD_CTRL)) {
- input->pressed_modifiers |= SDL_KMOD_CTRL;
- }
- } else {
- input->pressed_modifiers &= ~SDL_KMOD_CTRL;
- }
- if (input->xkb.wl_pressed_modifiers & input->xkb.idx_alt) {
- if (!(input->pressed_modifiers & SDL_KMOD_ALT)) {
- input->pressed_modifiers |= SDL_KMOD_ALT;
- }
- } else {
- input->pressed_modifiers &= ~SDL_KMOD_ALT;
- }
- if (input->xkb.wl_pressed_modifiers & input->xkb.idx_gui) {
- if (!(input->pressed_modifiers & SDL_KMOD_GUI)) {
- input->pressed_modifiers |= SDL_KMOD_GUI;
- }
- } else {
- input->pressed_modifiers &= ~SDL_KMOD_GUI;
- }
- /* Note: This is not backwards: in the default keymap, Mod5 is typically
- * level 3 shift, and Mod3 is typically level 5 shift.
- */
- if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod3) {
- if (!(input->pressed_modifiers & SDL_KMOD_LEVEL5)) {
- input->pressed_modifiers |= SDL_KMOD_LEVEL5;
- }
- } else {
- input->pressed_modifiers &= ~SDL_KMOD_LEVEL5;
- }
- if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod5) {
- if (!(input->pressed_modifiers & SDL_KMOD_MODE)) {
- input->pressed_modifiers |= SDL_KMOD_MODE;
- }
- } else {
- input->pressed_modifiers &= ~SDL_KMOD_MODE;
- }
- }
- /* If a latch or lock was activated by a keypress, the latch/lock will
- * be tied to the specific left/right key that initiated it. Otherwise,
- * the ambiguous left/right combo is used.
- *
- * The modifier will remain active until the latch/lock is released by
- * the system.
- */
- if (input->xkb.wl_locked_modifiers & input->xkb.idx_shift) {
- if (input->pressed_modifiers & SDL_KMOD_SHIFT) {
- input->locked_modifiers &= ~SDL_KMOD_SHIFT;
- input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_SHIFT);
- } else if (!(input->locked_modifiers & SDL_KMOD_SHIFT)) {
- input->locked_modifiers |= SDL_KMOD_SHIFT;
- }
- } else {
- input->locked_modifiers &= ~SDL_KMOD_SHIFT;
- }
- if (input->xkb.wl_locked_modifiers & input->xkb.idx_ctrl) {
- if (input->pressed_modifiers & SDL_KMOD_CTRL) {
- input->locked_modifiers &= ~SDL_KMOD_CTRL;
- input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_CTRL);
- } else if (!(input->locked_modifiers & SDL_KMOD_CTRL)) {
- input->locked_modifiers |= SDL_KMOD_CTRL;
- }
- } else {
- input->locked_modifiers &= ~SDL_KMOD_CTRL;
- }
- if (input->xkb.wl_locked_modifiers & input->xkb.idx_alt) {
- if (input->pressed_modifiers & SDL_KMOD_ALT) {
- input->locked_modifiers &= ~SDL_KMOD_ALT;
- input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_ALT);
- } else if (!(input->locked_modifiers & SDL_KMOD_ALT)) {
- input->locked_modifiers |= SDL_KMOD_ALT;
- }
- } else {
- input->locked_modifiers &= ~SDL_KMOD_ALT;
- }
- if (input->xkb.wl_locked_modifiers & input->xkb.idx_gui) {
- if (input->pressed_modifiers & SDL_KMOD_GUI) {
- input->locked_modifiers &= ~SDL_KMOD_GUI;
- input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_GUI);
- } else if (!(input->locked_modifiers & SDL_KMOD_GUI)) {
- input->locked_modifiers |= SDL_KMOD_GUI;
- }
- } else {
- input->locked_modifiers &= ~SDL_KMOD_GUI;
- }
- // As above, this is correct: Mod3 is typically level 5 shift, and Mod5 is typically level 3 shift.
- if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod3) {
- input->locked_modifiers |= SDL_KMOD_LEVEL5;
- } else {
- input->locked_modifiers &= ~SDL_KMOD_LEVEL5;
- }
- if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod5) {
- input->locked_modifiers |= SDL_KMOD_MODE;
- } else {
- input->locked_modifiers &= ~SDL_KMOD_MODE;
- }
- // Capslock and Numlock can only be locked, not pressed.
- if (input->xkb.wl_locked_modifiers & input->xkb.idx_caps) {
- input->locked_modifiers |= SDL_KMOD_CAPS;
- } else {
- input->locked_modifiers &= ~SDL_KMOD_CAPS;
- }
- if (input->xkb.wl_locked_modifiers & input->xkb.idx_num) {
- input->locked_modifiers |= SDL_KMOD_NUM;
- } else {
- input->locked_modifiers &= ~SDL_KMOD_NUM;
- }
- SDL_SetModState(input->pressed_modifiers | input->locked_modifiers);
- }
- static void Wayland_HandleModifierKeys(struct SDL_WaylandInput *input, SDL_Scancode scancode, bool pressed)
- {
- const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
- SDL_Keymod mod;
- /* 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 sent at a later time.
- */
- 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;
- default:
- return;
- }
- if (pressed) {
- input->pressed_modifiers |= mod;
- } else {
- input->pressed_modifiers &= ~mod;
- }
- Wayland_ReconcileModifiers(input, true);
- }
- static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface,
- struct wl_array *keys)
- {
- struct SDL_WaylandInput *input = data;
- SDL_WindowData *window;
- uint32_t *key;
- if (!surface) {
- // enter event for a window we've just destroyed
- return;
- }
- window = Wayland_GetWindowDataForOwnedSurface(surface);
- if (!window) {
- return;
- }
- input->keyboard_focus = window;
- window->keyboard_device = input;
- // Restore the keyboard focus to the child popup that was holding it
- SDL_SetKeyboardFocus(window->sdlwindow->keyboard_focus ? window->sdlwindow->keyboard_focus : window->sdlwindow);
- #ifdef SDL_USE_IME
- if (!input->text_input) {
- SDL_IME_SetFocus(true);
- }
- #endif
- Uint64 timestamp = SDL_GetTicksNS();
- window->last_focus_event_time_ns = timestamp;
- wl_array_for_each (key, keys) {
- const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, *key);
- const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
- switch (keycode) {
- case SDLK_LSHIFT:
- case SDLK_RSHIFT:
- case SDLK_LCTRL:
- case SDLK_RCTRL:
- case SDLK_LALT:
- case SDLK_RALT:
- case SDLK_LGUI:
- case SDLK_RGUI:
- case SDLK_MODE:
- case SDLK_LEVEL5_SHIFT:
- Wayland_HandleModifierKeys(input, scancode, true);
- SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, *key, scancode, true);
- break;
- default:
- break;
- }
- }
- }
- static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface)
- {
- struct SDL_WaylandInput *input = data;
- SDL_WindowData *wind;
- SDL_Window *window = NULL;
- if (!surface) {
- return;
- }
- wind = Wayland_GetWindowDataForOwnedSurface(surface);
- if (!wind) {
- return;
- }
- wind->keyboard_device = NULL;
- window = wind->sdlwindow;
- // Stop key repeat before clearing keyboard focus
- keyboard_repeat_clear(&input->keyboard_repeat);
- // This will release any keys still pressed
- SDL_SetKeyboardFocus(NULL);
- input->keyboard_focus = NULL;
- // Clear the pressed modifiers.
- input->pressed_modifiers = SDL_KMOD_NONE;
- #ifdef SDL_USE_IME
- if (!input->text_input) {
- SDL_IME_SetFocus(false);
- }
- #endif
- /* If the surface had a pointer leave event while still having active touch events, it retained mouse focus.
- * Clear it now if all touch events are raised.
- */
- if (!input->pointer_focus && SDL_GetMouseFocus() == window && !Wayland_SurfaceHasActiveTouches(surface)) {
- SDL_SetMouseFocus(NULL);
- }
- }
- static bool keyboard_input_get_text(char text[8], const struct SDL_WaylandInput *input, uint32_t key, bool down, bool *handled_by_ime)
- {
- SDL_WindowData *window = input->keyboard_focus;
- const xkb_keysym_t *syms;
- xkb_keysym_t sym;
- if (!window || window->keyboard_device != input || !input->xkb.state) {
- return false;
- }
- // TODO: Can this happen?
- if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1) {
- return false;
- }
- sym = syms[0];
- #ifdef SDL_USE_IME
- if (SDL_IME_ProcessKeyEvent(sym, key + 8, down)) {
- if (handled_by_ime) {
- *handled_by_ime = true;
- }
- return true;
- }
- #endif
- if (!down) {
- return false;
- }
- if (input->xkb.compose_state && WAYLAND_xkb_compose_state_feed(input->xkb.compose_state, sym) == XKB_COMPOSE_FEED_ACCEPTED) {
- switch (WAYLAND_xkb_compose_state_get_status(input->xkb.compose_state)) {
- case XKB_COMPOSE_COMPOSING:
- if (handled_by_ime) {
- *handled_by_ime = true;
- }
- return true;
- case XKB_COMPOSE_CANCELLED:
- default:
- sym = XKB_KEY_NoSymbol;
- break;
- case XKB_COMPOSE_NOTHING:
- break;
- case XKB_COMPOSE_COMPOSED:
- sym = WAYLAND_xkb_compose_state_get_one_sym(input->xkb.compose_state);
- break;
- }
- }
- return WAYLAND_xkb_keysym_to_utf8(sym, text, 8) > 0;
- }
- static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t time, uint32_t key,
- uint32_t state_w)
- {
- struct SDL_WaylandInput *input = data;
- enum wl_keyboard_key_state state = state_w;
- char text[8];
- bool has_text = false;
- bool handled_by_ime = false;
- const Uint64 timestamp_ns = Wayland_GetKeyboardTimestamp(input, time);
- Wayland_UpdateImplicitGrabSerial(input, serial);
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
- if (keyboard_focus && SDL_TextInputActive(keyboard_focus)) {
- has_text = keyboard_input_get_text(text, input, key, true, &handled_by_ime);
- }
- } else {
- if (keyboard_repeat_key_is_set(&input->keyboard_repeat, key)) {
- /* Send any due key repeat events before stopping the repeat and generating the key up event.
- * Compute time based on the Wayland time, as it reports when the release event happened.
- * Using SDL_GetTicks would be wrong, as it would report when the release event is processed,
- * which may be off if the application hasn't pumped events for a while.
- */
- const Uint64 elapsed = SDL_MS_TO_NS(time - input->keyboard_repeat.wl_press_time_ms);
- keyboard_repeat_handle(&input->keyboard_repeat, elapsed);
- keyboard_repeat_clear(&input->keyboard_repeat);
- }
- keyboard_input_get_text(text, input, key, false, &handled_by_ime);
- }
- const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, key);
- Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
- SDL_SendKeyboardKeyIgnoreModifiers(timestamp_ns, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- if (has_text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) {
- if (!handled_by_ime) {
- SDL_SendKeyboardText(text);
- }
- }
- if (input->xkb.keymap && WAYLAND_xkb_keymap_key_repeats(input->xkb.keymap, key + 8)) {
- keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, time, timestamp_ns, scancode, has_text, text);
- }
- }
- }
- static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t mods_depressed,
- uint32_t mods_latched, uint32_t mods_locked,
- uint32_t group)
- {
- struct SDL_WaylandInput *input = data;
- if (input->xkb.state == NULL) {
- /* if we get a modifier notification before the keymap, there's nothing we can do with the information
- */
- return;
- }
- WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
- mods_locked, 0, 0, group);
- input->xkb.wl_pressed_modifiers = mods_depressed;
- input->xkb.wl_locked_modifiers = mods_latched | mods_locked;
- Wayland_ReconcileModifiers(input, false);
- // If a key is repeating, update the text to apply the modifier.
- if (keyboard_repeat_is_set(&input->keyboard_repeat)) {
- char text[8];
- const uint32_t key = keyboard_repeat_get_key(&input->keyboard_repeat);
- if (keyboard_input_get_text(text, input, key, true, NULL)) {
- keyboard_repeat_set_text(&input->keyboard_repeat, text);
- }
- }
- if (group == input->xkb.current_group) {
- return;
- }
- // The layout changed, remap and fire an event. Virtual keyboards use the default keymap.
- input->xkb.current_group = group;
- Wayland_UpdateKeymap(input);
- }
- static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
- int32_t rate, int32_t delay)
- {
- struct SDL_WaylandInput *input = data;
- input->keyboard_repeat.repeat_rate = SDL_clamp(rate, 0, 1000);
- input->keyboard_repeat.repeat_delay_ms = delay;
- input->keyboard_repeat.is_initialized = true;
- }
- static const struct wl_keyboard_listener keyboard_listener = {
- keyboard_handle_keymap,
- keyboard_handle_enter,
- keyboard_handle_leave,
- keyboard_handle_key,
- keyboard_handle_modifiers,
- keyboard_handle_repeat_info, // Version 4
- };
- void Wayland_input_init_relative_pointer(SDL_VideoData *d)
- {
- struct SDL_WaylandInput *input = d->input;
- if (!d->relative_pointer_manager) {
- return;
- }
- if (input->pointer && !input->relative_pointer) {
- input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(input->display->relative_pointer_manager, input->pointer);
- zwp_relative_pointer_v1_add_listener(input->relative_pointer,
- &relative_pointer_listener,
- input);
- }
- }
- static void seat_handle_capabilities(void *data, struct wl_seat *seat,
- enum wl_seat_capability caps)
- {
- struct SDL_WaylandInput *input = data;
- if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
- input->pointer = wl_seat_get_pointer(seat);
- SDL_memset(&input->pointer_curr_axis_info, 0, sizeof(input->pointer_curr_axis_info));
- input->display->pointer = input->pointer;
- Wayland_CreateCursorShapeDevice(input);
- wl_pointer_set_user_data(input->pointer, input);
- wl_pointer_add_listener(input->pointer, &pointer_listener, input);
- Wayland_input_init_relative_pointer(input->display);
- input->pointer_id = SDL_GetNextObjectID();
- SDL_AddMouse(input->pointer_id, WAYLAND_DEFAULT_POINTER_NAME, !input->display->initializing);
- } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
- if (input->relative_pointer) {
- zwp_relative_pointer_v1_destroy(input->relative_pointer);
- input->relative_pointer = NULL;
- }
- if (input->cursor_shape) {
- wp_cursor_shape_device_v1_destroy(input->cursor_shape);
- input->cursor_shape = NULL;
- }
- if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) {
- wl_pointer_release(input->pointer);
- } else {
- wl_pointer_destroy(input->pointer);
- }
- input->pointer = NULL;
- input->display->pointer = NULL;
- SDL_RemoveMouse(input->pointer_id, true);
- input->pointer_id = 0;
- }
- if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
- input->touch = wl_seat_get_touch(seat);
- SDL_AddTouch((SDL_TouchID)(uintptr_t)input->touch, SDL_TOUCH_DEVICE_DIRECT, "wayland_touch");
- wl_touch_set_user_data(input->touch, input);
- wl_touch_add_listener(input->touch, &touch_listener,
- input);
- } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
- SDL_DelTouch((SDL_TouchID)(intptr_t)input->touch);
- wl_touch_destroy(input->touch);
- input->touch = NULL;
- }
- if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
- input->keyboard = wl_seat_get_keyboard(seat);
- wl_keyboard_set_user_data(input->keyboard, input);
- wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
- input);
- input->keyboard_id = SDL_GetNextObjectID();
- SDL_AddKeyboard(input->keyboard_id, WAYLAND_DEFAULT_KEYBOARD_NAME, !input->display->initializing);
- } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
- wl_keyboard_destroy(input->keyboard);
- input->keyboard = NULL;
- SDL_RemoveKeyboard(input->keyboard_id, true);
- input->keyboard_id = 0;
- }
- Wayland_RegisterTimestampListeners(input);
- }
- static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
- {
- // unimplemented
- }
- static const struct wl_seat_listener seat_listener = {
- seat_handle_capabilities,
- seat_handle_name, // Version 2
- };
- static void data_source_handle_target(void *data, struct wl_data_source *wl_data_source,
- const char *mime_type)
- {
- }
- static void data_source_handle_send(void *data, struct wl_data_source *wl_data_source,
- const char *mime_type, int32_t fd)
- {
- Wayland_data_source_send((SDL_WaylandDataSource *)data, mime_type, fd);
- }
- static void data_source_handle_cancelled(void *data, struct wl_data_source *wl_data_source)
- {
- SDL_WaylandDataSource *source = data;
- if (source) {
- Wayland_data_source_destroy(source);
- }
- }
- static void data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source)
- {
- }
- static void data_source_handle_dnd_finished(void *data, struct wl_data_source *wl_data_source)
- {
- }
- static void data_source_handle_action(void *data, struct wl_data_source *wl_data_source,
- uint32_t dnd_action)
- {
- }
- static const struct wl_data_source_listener data_source_listener = {
- data_source_handle_target,
- data_source_handle_send,
- data_source_handle_cancelled,
- data_source_handle_dnd_drop_performed, // Version 3
- data_source_handle_dnd_finished, // Version 3
- data_source_handle_action, // Version 3
- };
- static void primary_selection_source_send(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1,
- const char *mime_type, int32_t fd)
- {
- Wayland_primary_selection_source_send((SDL_WaylandPrimarySelectionSource *)data,
- mime_type, fd);
- }
- static void primary_selection_source_cancelled(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1)
- {
- Wayland_primary_selection_source_destroy(data);
- }
- static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener = {
- primary_selection_source_send,
- primary_selection_source_cancelled,
- };
- SDL_WaylandDataSource *Wayland_data_source_create(SDL_VideoDevice *_this)
- {
- SDL_WaylandDataSource *data_source = NULL;
- SDL_VideoData *driver_data = NULL;
- struct wl_data_source *id = NULL;
- if (!_this || !_this->internal) {
- SDL_SetError("Video driver uninitialized");
- } else {
- driver_data = _this->internal;
- if (driver_data->data_device_manager) {
- id = wl_data_device_manager_create_data_source(
- driver_data->data_device_manager);
- }
- if (!id) {
- SDL_SetError("Wayland unable to create data source");
- } else {
- data_source = SDL_calloc(1, sizeof(*data_source));
- if (!data_source) {
- wl_data_source_destroy(id);
- } else {
- data_source->source = id;
- wl_data_source_set_user_data(id, data_source);
- wl_data_source_add_listener(id, &data_source_listener,
- data_source);
- }
- }
- }
- return data_source;
- }
- SDL_WaylandPrimarySelectionSource *Wayland_primary_selection_source_create(SDL_VideoDevice *_this)
- {
- SDL_WaylandPrimarySelectionSource *primary_selection_source = NULL;
- SDL_VideoData *driver_data = NULL;
- struct zwp_primary_selection_source_v1 *id = NULL;
- if (!_this || !_this->internal) {
- SDL_SetError("Video driver uninitialized");
- } else {
- driver_data = _this->internal;
- if (driver_data->primary_selection_device_manager) {
- id = zwp_primary_selection_device_manager_v1_create_source(
- driver_data->primary_selection_device_manager);
- }
- if (!id) {
- SDL_SetError("Wayland unable to create primary selection source");
- } else {
- primary_selection_source = SDL_calloc(1, sizeof(*primary_selection_source));
- if (!primary_selection_source) {
- zwp_primary_selection_source_v1_destroy(id);
- } else {
- primary_selection_source->source = id;
- zwp_primary_selection_source_v1_add_listener(id, &primary_selection_source_listener,
- primary_selection_source);
- }
- }
- }
- return primary_selection_source;
- }
- static void data_offer_handle_offer(void *data, struct wl_data_offer *wl_data_offer,
- const char *mime_type)
- {
- SDL_WaylandDataOffer *offer = data;
- Wayland_data_offer_add_mime(offer, mime_type);
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_offer_listener . data_offer_handle_offer on data_offer 0x%08x for MIME '%s'",
- (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1),
- mime_type);
- }
- static void data_offer_handle_source_actions(void *data, struct wl_data_offer *wl_data_offer,
- uint32_t source_actions)
- {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_offer_listener . data_offer_handle_source_actions on data_offer 0x%08x for Source Actions '%d'",
- (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1),
- source_actions);
- }
- static void data_offer_handle_actions(void *data, struct wl_data_offer *wl_data_offer,
- uint32_t dnd_action)
- {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_offer_listener . data_offer_handle_actions on data_offer 0x%08x for DND Actions '%d'",
- (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1),
- dnd_action);
- }
- static const struct wl_data_offer_listener data_offer_listener = {
- data_offer_handle_offer,
- data_offer_handle_source_actions, // Version 3
- data_offer_handle_actions, // Version 3
- };
- static void primary_selection_offer_handle_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1,
- const char *mime_type)
- {
- SDL_WaylandPrimarySelectionOffer *offer = data;
- Wayland_primary_selection_offer_add_mime(offer, mime_type);
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In zwp_primary_selection_offer_v1_listener . primary_selection_offer_handle_offer on primary_selection_offer 0x%08x for MIME '%s'",
- (zwp_primary_selection_offer_v1 ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)zwp_primary_selection_offer_v1) : -1),
- mime_type);
- }
- static const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = {
- primary_selection_offer_handle_offer,
- };
- static void data_device_handle_data_offer(void *data, struct wl_data_device *wl_data_device,
- struct wl_data_offer *id)
- {
- SDL_WaylandDataOffer *data_offer = SDL_calloc(1, sizeof(*data_offer));
- if (data_offer) {
- data_offer->offer = id;
- data_offer->data_device = data;
- WAYLAND_wl_list_init(&(data_offer->mimes));
- wl_data_offer_set_user_data(id, data_offer);
- wl_data_offer_add_listener(id, &data_offer_listener, data_offer);
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_data_offer on data_offer 0x%08x",
- (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
- }
- }
- static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_device,
- uint32_t serial, struct wl_surface *surface,
- wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id)
- {
- SDL_WaylandDataDevice *data_device = data;
- data_device->has_mime_file = false;
- data_device->has_mime_text = false;
- uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
- data_device->drag_serial = serial;
- if (id) {
- data_device->drag_offer = wl_data_offer_get_user_data(id);
- // TODO: SDL Support more mime types
- #ifdef SDL_USE_LIBDBUS
- if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) {
- data_device->has_mime_file = true;
- wl_data_offer_accept(id, serial, FILE_PORTAL_MIME);
- }
- #endif
- if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_MIME)) {
- data_device->has_mime_file = true;
- wl_data_offer_accept(id, serial, FILE_MIME);
- }
- if (Wayland_data_offer_has_mime(data_device->drag_offer, TEXT_MIME)) {
- data_device->has_mime_text = true;
- wl_data_offer_accept(id, serial, TEXT_MIME);
- }
- // SDL only supports "copy" style drag and drop
- if (data_device->has_mime_file || data_device->has_mime_text) {
- dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
- } else {
- // drag_mime is NULL this will decline the offer
- wl_data_offer_accept(id, serial, NULL);
- }
- if (wl_data_offer_get_version(data_device->drag_offer->offer) >=
- WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) {
- wl_data_offer_set_actions(data_device->drag_offer->offer,
- dnd_action, dnd_action);
- }
- // find the current window
- if (surface) {
- SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface);
- if (window) {
- data_device->dnd_window = window->sdlwindow;
- const float dx = (float)wl_fixed_to_double(x);
- const float dy = (float)wl_fixed_to_double(y);
- SDL_SendDropPosition(data_device->dnd_window, dx, dy);
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d into window %d for serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
- wl_fixed_to_int(x), wl_fixed_to_int(y), SDL_GetWindowID(data_device->dnd_window), serial);
- } else {
- data_device->dnd_window = NULL;
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
- wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
- }
- } else {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
- wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
- }
- } else {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
- -1, wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
- }
- }
- static void data_device_handle_leave(void *data, struct wl_data_device *wl_data_device)
- {
- SDL_WaylandDataDevice *data_device = data;
- if (data_device->drag_offer) {
- if (data_device->dnd_window) {
- SDL_SendDropComplete(data_device->dnd_window);
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x from window %d for serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
- SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial);
- } else {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x for serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
- data_device->drag_serial);
- }
- Wayland_data_offer_destroy(data_device->drag_offer);
- data_device->drag_offer = NULL;
- } else {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x for serial %d",
- -1, -1);
- }
- data_device->has_mime_file = false;
- data_device->has_mime_text = false;
- }
- static void data_device_handle_motion(void *data, struct wl_data_device *wl_data_device,
- uint32_t time, wl_fixed_t x, wl_fixed_t y)
- {
- SDL_WaylandDataDevice *data_device = data;
- if (data_device->drag_offer && data_device->dnd_window && (data_device->has_mime_file || data_device->has_mime_text)) {
- const float dx = (float)wl_fixed_to_double(x);
- const float dy = (float)wl_fixed_to_double(y);
- /* XXX: Send the filename here if the event system ever starts passing it though.
- * Any future implementation should cache the filenames, as otherwise this could
- * hammer the DBus interface hundreds or even thousands of times per second.
- */
- SDL_SendDropPosition(data_device->dnd_window, dx, dy);
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_motion on data_offer 0x%08x at %d x %d in window %d serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
- wl_fixed_to_int(x), wl_fixed_to_int(y),
- SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial);
- } else {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_motion on data_offer 0x%08x at %d x %d serial %d",
- -1, wl_fixed_to_int(x), wl_fixed_to_int(y), -1);
- }
- }
- static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_device)
- {
- SDL_WaylandDataDevice *data_device = data;
- if (data_device->drag_offer && data_device->dnd_window && (data_device->has_mime_file || data_device->has_mime_text)) {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_drop on data_offer 0x%08x in window %d serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
- SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial);
- // TODO: SDL Support more mime types
- size_t length;
- bool drop_handled = false;
- #ifdef SDL_USE_LIBDBUS
- if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) {
- void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
- FILE_PORTAL_MIME, &length);
- if (buffer) {
- SDL_DBusContext *dbus = SDL_DBus_GetContext();
- if (dbus) {
- int path_count = 0;
- char **paths = SDL_DBus_DocumentsPortalRetrieveFiles(buffer, &path_count);
- // If dropped files contain a directory the list is empty
- if (paths && path_count > 0) {
- int i;
- for (i = 0; i < path_count; i++) {
- SDL_SendDropFile(data_device->dnd_window, NULL, paths[i]);
- }
- dbus->free_string_array(paths);
- SDL_SendDropComplete(data_device->dnd_window);
- drop_handled = true;
- }
- }
- SDL_free(buffer);
- }
- }
- #endif
- /* If XDG document portal fails fallback.
- * When running a flatpak sandbox this will most likely be a list of
- * non paths that are not visible to the application
- */
- if (!drop_handled) {
- const char *mime_type = data_device->has_mime_file ? FILE_MIME : (data_device->has_mime_text ? TEXT_MIME : "");
- void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
- mime_type, &length);
- if (data_device->has_mime_file) {
- if (buffer) {
- char *saveptr = NULL;
- char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
- while (token) {
- if (SDL_URIToLocal(token, token) >= 0) {
- SDL_SendDropFile(data_device->dnd_window, NULL, token);
- }
- token = SDL_strtok_r(NULL, "\r\n", &saveptr);
- }
- SDL_free(buffer);
- SDL_SendDropComplete(data_device->dnd_window);
- } else {
- SDL_SendDropComplete(data_device->dnd_window);
- }
- drop_handled = true;
- } else if (data_device->has_mime_text) {
- if (buffer) {
- char *saveptr = NULL;
- char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
- while (token) {
- SDL_SendDropText(data_device->dnd_window, token);
- token = SDL_strtok_r(NULL, "\r\n", &saveptr);
- }
- SDL_free(buffer);
- SDL_SendDropComplete(data_device->dnd_window);
- } else {
- /* Even though there has been a valid data offer,
- * and there have been valid Enter, Motion, and Drop callbacks,
- * Wayland_data_offer_receive may return an empty buffer,
- * because the data is actually in the primary selection device,
- * not in the data device.
- */
- SDL_SendDropComplete(data_device->dnd_window);
- }
- drop_handled = true;
- }
- }
- if (drop_handled && wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_FINISH_SINCE_VERSION) {
- wl_data_offer_finish(data_device->drag_offer->offer);
- }
- } else {
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_drop on data_offer 0x%08x serial %d",
- -1, -1);
- }
- Wayland_data_offer_destroy(data_device->drag_offer);
- data_device->drag_offer = NULL;
- }
- static void notifyFromMimes(struct wl_list *mimes)
- {
- int nformats = 0;
- char **new_mime_types = NULL;
- if (mimes) {
- nformats = WAYLAND_wl_list_length(mimes);
- size_t alloc_size = (nformats + 1) * sizeof(char *);
- /* do a first pass to compute allocation size */
- SDL_MimeDataList *item = NULL;
- wl_list_for_each(item, mimes, link) {
- alloc_size += SDL_strlen(item->mime_type) + 1;
- }
- new_mime_types = SDL_AllocateTemporaryMemory(alloc_size);
- if (!new_mime_types) {
- SDL_LogError(SDL_LOG_CATEGORY_INPUT, "unable to allocate new_mime_types");
- return;
- }
- /* second pass to fill*/
- char *strPtr = (char *)(new_mime_types + nformats + 1);
- item = NULL;
- int i = 0;
- wl_list_for_each(item, mimes, link) {
- new_mime_types[i] = strPtr;
- strPtr = stpcpy(strPtr, item->mime_type) + 1;
- i++;
- }
- new_mime_types[nformats] = NULL;
- }
- SDL_SendClipboardUpdate(false, new_mime_types, nformats);
- }
- static void data_device_handle_selection(void *data, struct wl_data_device *wl_data_device,
- struct wl_data_offer *id)
- {
- SDL_WaylandDataDevice *data_device = data;
- SDL_WaylandDataOffer *offer = NULL;
- if (id) {
- offer = wl_data_offer_get_user_data(id);
- }
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In data_device_listener . data_device_handle_selection on data_offer 0x%08x",
- (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
- if (data_device->selection_offer != offer) {
- Wayland_data_offer_destroy(data_device->selection_offer);
- data_device->selection_offer = offer;
- }
- notifyFromMimes(offer ? &offer->mimes : NULL);
- }
- static const struct wl_data_device_listener data_device_listener = {
- data_device_handle_data_offer,
- data_device_handle_enter,
- data_device_handle_leave,
- data_device_handle_motion,
- data_device_handle_drop,
- data_device_handle_selection
- };
- static void primary_selection_device_handle_offer(void *data, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
- struct zwp_primary_selection_offer_v1 *id)
- {
- SDL_WaylandPrimarySelectionOffer *primary_selection_offer = SDL_calloc(1, sizeof(*primary_selection_offer));
- if (primary_selection_offer) {
- primary_selection_offer->offer = id;
- primary_selection_offer->primary_selection_device = data;
- WAYLAND_wl_list_init(&(primary_selection_offer->mimes));
- zwp_primary_selection_offer_v1_set_user_data(id, primary_selection_offer);
- zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, primary_selection_offer);
- }
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In zwp_primary_selection_device_v1_listener . primary_selection_device_handle_offer on primary_selection_offer 0x%08x",
- (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
- }
- static void primary_selection_device_handle_selection(void *data, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
- struct zwp_primary_selection_offer_v1 *id)
- {
- SDL_WaylandPrimarySelectionDevice *primary_selection_device = data;
- SDL_WaylandPrimarySelectionOffer *offer = NULL;
- if (id) {
- offer = zwp_primary_selection_offer_v1_get_user_data(id);
- }
- if (primary_selection_device->selection_offer != offer) {
- Wayland_primary_selection_offer_destroy(primary_selection_device->selection_offer);
- primary_selection_device->selection_offer = offer;
- }
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In zwp_primary_selection_device_v1_listener . primary_selection_device_handle_selection on primary_selection_offer 0x%08x",
- (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
- }
- static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = {
- primary_selection_device_handle_offer,
- primary_selection_device_handle_selection
- };
- static void text_input_enter(void *data,
- struct zwp_text_input_v3 *zwp_text_input_v3,
- struct wl_surface *surface)
- {
- // No-op
- }
- static void text_input_leave(void *data,
- struct zwp_text_input_v3 *zwp_text_input_v3,
- struct wl_surface *surface)
- {
- // No-op
- }
- static void text_input_preedit_string(void *data,
- struct zwp_text_input_v3 *zwp_text_input_v3,
- const char *text,
- int32_t cursor_begin,
- int32_t cursor_end)
- {
- SDL_WaylandTextInput *text_input = data;
- text_input->has_preedit = true;
- if (text) {
- int cursor_begin_utf8 = cursor_begin >= 0 ? (int)SDL_utf8strnlen(text, cursor_begin) : -1;
- int cursor_end_utf8 = cursor_end >= 0 ? (int)SDL_utf8strnlen(text, cursor_end) : -1;
- int cursor_size_utf8;
- if (cursor_end_utf8 >= 0) {
- if (cursor_begin_utf8 >= 0) {
- cursor_size_utf8 = cursor_end_utf8 - cursor_begin_utf8;
- } else {
- cursor_size_utf8 = cursor_end_utf8;
- }
- } else {
- cursor_size_utf8 = -1;
- }
- SDL_SendEditingText(text, cursor_begin_utf8, cursor_size_utf8);
- } else {
- SDL_SendEditingText("", 0, 0);
- }
- }
- static void text_input_commit_string(void *data,
- struct zwp_text_input_v3 *zwp_text_input_v3,
- const char *text)
- {
- SDL_SendKeyboardText(text);
- }
- static void text_input_delete_surrounding_text(void *data,
- struct zwp_text_input_v3 *zwp_text_input_v3,
- uint32_t before_length,
- uint32_t after_length)
- {
- // FIXME: Do we care about this event?
- }
- static void text_input_done(void *data,
- struct zwp_text_input_v3 *zwp_text_input_v3,
- uint32_t serial)
- {
- SDL_WaylandTextInput *text_input = data;
- if (!text_input->has_preedit) {
- SDL_SendEditingText("", 0, 0);
- }
- text_input->has_preedit = false;
- }
- static const struct zwp_text_input_v3_listener text_input_listener = {
- text_input_enter,
- text_input_leave,
- text_input_preedit_string,
- text_input_commit_string,
- text_input_delete_surrounding_text,
- text_input_done
- };
- void Wayland_create_data_device(SDL_VideoData *d)
- {
- SDL_WaylandDataDevice *data_device = NULL;
- if (!d->input->seat) {
- // No seat yet, will be initialized later.
- return;
- }
- data_device = SDL_calloc(1, sizeof(*data_device));
- if (!data_device) {
- return;
- }
- data_device->data_device = wl_data_device_manager_get_data_device(
- d->data_device_manager, d->input->seat);
- data_device->video_data = d;
- if (!data_device->data_device) {
- SDL_free(data_device);
- } else {
- wl_data_device_set_user_data(data_device->data_device, data_device);
- wl_data_device_add_listener(data_device->data_device,
- &data_device_listener, data_device);
- d->input->data_device = data_device;
- }
- }
- void Wayland_create_primary_selection_device(SDL_VideoData *d)
- {
- SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
- if (!d->input->seat) {
- // No seat yet, will be initialized later.
- return;
- }
- primary_selection_device = SDL_calloc(1, sizeof(*primary_selection_device));
- if (!primary_selection_device) {
- return;
- }
- primary_selection_device->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(
- d->primary_selection_device_manager, d->input->seat);
- primary_selection_device->video_data = d;
- if (!primary_selection_device->primary_selection_device) {
- SDL_free(primary_selection_device);
- } else {
- zwp_primary_selection_device_v1_set_user_data(primary_selection_device->primary_selection_device,
- primary_selection_device);
- zwp_primary_selection_device_v1_add_listener(primary_selection_device->primary_selection_device,
- &primary_selection_device_listener, primary_selection_device);
- d->input->primary_selection_device = primary_selection_device;
- }
- }
- static void Wayland_create_text_input(SDL_VideoData *d)
- {
- SDL_WaylandTextInput *text_input = NULL;
- if (!d->input->seat) {
- // No seat yet, will be initialized later.
- return;
- }
- text_input = SDL_calloc(1, sizeof(*text_input));
- if (!text_input) {
- return;
- }
- text_input->text_input = zwp_text_input_manager_v3_get_text_input(
- d->text_input_manager, d->input->seat);
- if (!text_input->text_input) {
- SDL_free(text_input);
- } else {
- zwp_text_input_v3_set_user_data(text_input->text_input, text_input);
- zwp_text_input_v3_add_listener(text_input->text_input,
- &text_input_listener, text_input);
- d->input->text_input = text_input;
- }
- }
- void Wayland_create_text_input_manager(SDL_VideoData *d, uint32_t id)
- {
- #ifdef HAVE_FCITX
- const char *im_module = SDL_getenv("SDL_IM_MODULE");
- if (im_module && SDL_strcmp(im_module, "fcitx") == 0) {
- /* Override the Wayland text-input protocol when Fcitx is enabled, like how GTK_IM_MODULE does.
- *
- * The Fcitx wiki discourages enabling it under Wayland via SDL_IM_MODULE, so its presence must
- * be intentional, and this workaround is needed for fixing key repeat detection.
- */
- return;
- }
- #endif
- d->text_input_manager = wl_registry_bind(d->registry, id, &zwp_text_input_manager_v3_interface, 1);
- Wayland_create_text_input(d);
- }
- // Pen/Tablet support...
- typedef struct SDL_WaylandPenTool // a stylus, etc, on a tablet.
- {
- SDL_PenID instance_id;
- SDL_PenInfo info;
- SDL_Window *tool_focus;
- struct zwp_tablet_tool_v2 *wltool;
- float x;
- float y;
- bool frame_motion_set;
- float frame_axes[SDL_PEN_AXIS_COUNT];
- Uint32 frame_axes_set;
- int frame_pen_down;
- int frame_buttons[3];
- } SDL_WaylandPenTool;
- static void tablet_tool_handle_type(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t type)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- switch (type) {
- #define CASE(typ) case ZWP_TABLET_TOOL_V2_TYPE_##typ: sdltool->info.subtype = SDL_PEN_TYPE_##typ; return
- CASE(ERASER);
- CASE(PEN);
- CASE(PENCIL);
- CASE(AIRBRUSH);
- CASE(BRUSH);
- #undef CASE
- default: sdltool->info.subtype = SDL_PEN_TYPE_UNKNOWN; // we'll decline to add this when the `done` event comes through.
- }
- }
- static void tablet_tool_handle_hardware_serial(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial_hi, uint32_t serial_lo)
- {
- // don't care about this atm.
- }
- static void tablet_tool_handle_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t id_hi, uint32_t id_lo)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->info.wacom_id = id_lo;
- }
- static void tablet_tool_handle_capability(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t capability)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- switch (capability) {
- #define CASE(wltyp,sdltyp) case ZWP_TABLET_TOOL_V2_CAPABILITY_##wltyp: sdltool->info.capabilities |= sdltyp; return
- CASE(TILT, SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT);
- CASE(PRESSURE, SDL_PEN_CAPABILITY_PRESSURE);
- CASE(DISTANCE, SDL_PEN_CAPABILITY_DISTANCE);
- CASE(ROTATION, SDL_PEN_CAPABILITY_ROTATION);
- CASE(SLIDER, SDL_PEN_CAPABILITY_SLIDER);
- #undef CASE
- default: break; // unsupported here.
- }
- }
- static void tablet_tool_handle_done(void *data, struct zwp_tablet_tool_v2 *tool)
- {
- }
- static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *tool)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- if (sdltool->instance_id) {
- SDL_RemovePenDevice(0, sdltool->instance_id);
- }
- zwp_tablet_tool_v2_destroy(tool);
- SDL_free(sdltool);
- }
- static void tablet_tool_handle_proximity_in(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- SDL_WindowData *windowdata = surface ? Wayland_GetWindowDataForOwnedSurface(surface) : NULL;
- sdltool->tool_focus = windowdata ? windowdata->sdlwindow : NULL;
- SDL_assert(sdltool->instance_id == 0); // shouldn't be added at this point.
- if (sdltool->info.subtype != SDL_PEN_TYPE_UNKNOWN) { // don't tell SDL about it if we don't know its role.
- sdltool->instance_id = SDL_AddPenDevice(0, NULL, &sdltool->info, sdltool);
- }
- // According to the docs, this should be followed by a motion event, where we'll send our SDL events.
- }
- static void tablet_tool_handle_proximity_out(void *data, struct zwp_tablet_tool_v2 *tool)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->tool_focus = NULL;
- if (sdltool->instance_id) {
- SDL_RemovePenDevice(0, sdltool->instance_id);
- sdltool->instance_id = 0;
- }
- }
- static void tablet_tool_handle_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_pen_down = 1;
- }
- static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 *tool)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_pen_down = 0;
- }
- static void tablet_tool_handle_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t sx_w, wl_fixed_t sy_w)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- SDL_Window *window = sdltool->tool_focus;
- if (window) {
- const SDL_WindowData *windowdata = window->internal;
- sdltool->x = (float)(wl_fixed_to_double(sx_w) * windowdata->pointer_scale.x);
- sdltool->y = (float)(wl_fixed_to_double(sy_w) * windowdata->pointer_scale.y);
- sdltool->frame_motion_set = true;
- }
- }
- static void tablet_tool_handle_pressure(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t pressure)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_axes[SDL_PEN_AXIS_PRESSURE] = ((float) pressure) / 65535.0f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
- if (pressure) {
- sdltool->frame_axes[SDL_PEN_AXIS_DISTANCE] = 0.0f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
- }
- }
- static void tablet_tool_handle_distance(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t distance)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_axes[SDL_PEN_AXIS_DISTANCE] = ((float) distance) / 65535.0f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
- if (distance) {
- sdltool->frame_axes[SDL_PEN_AXIS_PRESSURE] = 0.0f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
- }
- }
- static void tablet_tool_handle_tilt(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t xtilt, wl_fixed_t ytilt)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_axes[SDL_PEN_AXIS_XTILT] = (float)(wl_fixed_to_double(xtilt));
- sdltool->frame_axes[SDL_PEN_AXIS_YTILT] = (float)(wl_fixed_to_double(ytilt));
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_XTILT) | (1u << SDL_PEN_AXIS_YTILT);
- }
- static void tablet_tool_handle_button(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, uint32_t button, uint32_t state)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- int sdlbutton;
- switch (button) {
- // see %{_includedir}/linux/input-event-codes.h
- case 0x14b: // BTN_STYLUS
- sdlbutton = 1;
- break;
- case 0x14c: // BTN_STYLUS2
- sdlbutton = 2;
- break;
- case 0x149: // BTN_STYLUS3
- sdlbutton = 3;
- break;
- default:
- return; // don't care about this button, I guess.
- }
- SDL_assert((sdlbutton >= 1) && (sdlbutton <= SDL_arraysize(sdltool->frame_buttons)));
- sdltool->frame_buttons[sdlbutton-1] = (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) ? 1 : 0;
- }
- static void tablet_tool_handle_rotation(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t degrees)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- const float rotation = (float)(wl_fixed_to_double(degrees));
- sdltool->frame_axes[SDL_PEN_AXIS_ROTATION] = (rotation > 180.0f) ? (rotation - 360.0f) : rotation; // map to -180.0f ... 179.0f range
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_ROTATION);
- }
- static void tablet_tool_handle_slider(void *data, struct zwp_tablet_tool_v2 *tool, int32_t position)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- sdltool->frame_axes[SDL_PEN_AXIS_SLIDER] = position / 65535.f;
- sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_SLIDER);
- }
- static void tablet_tool_handle_wheel(void *data, struct zwp_tablet_tool_v2 *tool, int32_t degrees, int32_t clicks)
- {
- // not supported at the moment
- }
- static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t time)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
- if (!sdltool->instance_id) {
- return; // Not a pen we report on.
- }
- const Uint64 timestamp = Wayland_GetEventTimestamp(Wayland_EventTimestampMSToNS(time));
- const SDL_PenID instance_id = sdltool->instance_id;
- SDL_Window *window = sdltool->tool_focus;
- // I don't know if this is necessary (or makes sense), but send motion before pen downs, but after pen ups, so you don't get unexpected lines drawn.
- if (sdltool->frame_motion_set && (sdltool->frame_pen_down != -1)) {
- if (sdltool->frame_pen_down) {
- SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
- SDL_SendPenTouch(timestamp, instance_id, window, false, true); // !!! FIXME: how do we know what tip is in use?
- } else {
- SDL_SendPenTouch(timestamp, instance_id, window, false, false); // !!! FIXME: how do we know what tip is in use?
- SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
- }
- } else {
- if (sdltool->frame_pen_down != -1) {
- SDL_SendPenTouch(timestamp, instance_id, window, false, (sdltool->frame_pen_down != 0)); // !!! FIXME: how do we know what tip is in use?
- }
- if (sdltool->frame_motion_set) {
- SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
- }
- }
- for (SDL_PenAxis i = 0; i < SDL_PEN_AXIS_COUNT; i++) {
- if (sdltool->frame_axes_set & (1u << i)) {
- SDL_SendPenAxis(timestamp, instance_id, window, i, sdltool->frame_axes[i]);
- }
- }
- for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) {
- const int state = sdltool->frame_buttons[i];
- if (state != -1) {
- SDL_SendPenButton(timestamp, instance_id, window, (Uint8)(i + 1), (state != 0));
- sdltool->frame_buttons[i] = -1;
- }
- }
- // reset for next frame.
- sdltool->frame_pen_down = -1;
- sdltool->frame_motion_set = false;
- sdltool->frame_axes_set = 0;
- }
- static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
- tablet_tool_handle_type,
- tablet_tool_handle_hardware_serial,
- tablet_tool_handle_hardware_id_wacom,
- tablet_tool_handle_capability,
- tablet_tool_handle_done,
- tablet_tool_handle_removed,
- tablet_tool_handle_proximity_in,
- tablet_tool_handle_proximity_out,
- tablet_tool_handle_down,
- tablet_tool_handle_up,
- tablet_tool_handle_motion,
- tablet_tool_handle_pressure,
- tablet_tool_handle_distance,
- tablet_tool_handle_tilt,
- tablet_tool_handle_rotation,
- tablet_tool_handle_slider,
- tablet_tool_handle_wheel,
- tablet_tool_handle_button,
- tablet_tool_handle_frame
- };
- static void tablet_seat_handle_tablet_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_v2 *tablet)
- {
- // don't care atm.
- }
- static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_tool_v2 *tool)
- {
- SDL_WaylandPenTool *sdltool = SDL_calloc(1, sizeof(*sdltool));
- if (sdltool) { // if allocation failed, oh well, we won't report this device.
- sdltool->wltool = tool;
- sdltool->info.max_tilt = -1.0f;
- sdltool->info.num_buttons = -1;
- sdltool->frame_pen_down = -1;
- for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) {
- sdltool->frame_buttons[i] = -1;
- }
- // this will send a bunch of zwp_tablet_tool_v2 events right up front to tell
- // us device details, with a "done" event to let us know we have everything.
- zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, sdltool);
- }
- }
- static void tablet_seat_handle_pad_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_pad_v2 *pad)
- {
- // we don't care atm.
- }
- static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
- tablet_seat_handle_tablet_added,
- tablet_seat_handle_tool_added,
- tablet_seat_handle_pad_added
- };
- void Wayland_input_init_tablet_support(struct SDL_WaylandInput *input, struct zwp_tablet_manager_v2 *tablet_manager)
- {
- if (!tablet_manager || !input->seat) {
- return;
- }
- SDL_WaylandTabletInput *tablet_input = SDL_calloc(1, sizeof(*tablet_input));
- if (!tablet_input) {
- return;
- }
- tablet_input->input = input;
- tablet_input->seat = zwp_tablet_manager_v2_get_tablet_seat(tablet_manager, input->seat);
- zwp_tablet_seat_v2_add_listener(tablet_input->seat, &tablet_seat_listener, tablet_input);
- input->tablet_input = tablet_input;
- }
- static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle, void *userdata)
- {
- SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) handle;
- zwp_tablet_tool_v2_destroy(sdltool->wltool);
- SDL_free(sdltool);
- }
- void Wayland_input_quit_tablet_support(struct SDL_WaylandInput *input)
- {
- SDL_RemoveAllPenDevices(Wayland_remove_all_pens_callback, NULL);
- if (input && input->tablet_input) {
- zwp_tablet_seat_v2_destroy(input->tablet_input->seat);
- SDL_free(input->tablet_input);
- input->tablet_input = NULL;
- }
- }
- void Wayland_input_initialize_seat(SDL_VideoData *d)
- {
- struct SDL_WaylandInput *input = d->input;
- WAYLAND_wl_list_init(&touch_points);
- if (d->data_device_manager) {
- Wayland_create_data_device(d);
- }
- if (d->primary_selection_device_manager) {
- Wayland_create_primary_selection_device(d);
- }
- if (d->text_input_manager) {
- Wayland_create_text_input(d);
- }
- wl_seat_add_listener(input->seat, &seat_listener, input);
- wl_seat_set_user_data(input->seat, input);
- if (d->tablet_manager) {
- Wayland_input_init_tablet_support(d->input, d->tablet_manager);
- }
- WAYLAND_wl_display_flush(d->display);
- }
- void Wayland_display_destroy_input(SDL_VideoData *d)
- {
- struct SDL_WaylandInput *input = d->input;
- if (input->keyboard_timestamps) {
- zwp_input_timestamps_v1_destroy(input->keyboard_timestamps);
- }
- if (input->pointer_timestamps) {
- zwp_input_timestamps_v1_destroy(input->pointer_timestamps);
- }
- if (input->touch_timestamps) {
- zwp_input_timestamps_v1_destroy(input->touch_timestamps);
- }
- if (input->data_device) {
- Wayland_data_device_clear_selection(input->data_device);
- if (input->data_device->selection_offer) {
- Wayland_data_offer_destroy(input->data_device->selection_offer);
- }
- if (input->data_device->drag_offer) {
- Wayland_data_offer_destroy(input->data_device->drag_offer);
- }
- if (input->data_device->data_device) {
- if (wl_data_device_get_version(input->data_device->data_device) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) {
- wl_data_device_release(input->data_device->data_device);
- } else {
- wl_data_device_destroy(input->data_device->data_device);
- }
- }
- SDL_free(input->data_device);
- }
- if (input->primary_selection_device) {
- if (input->primary_selection_device->selection_offer) {
- Wayland_primary_selection_offer_destroy(input->primary_selection_device->selection_offer);
- }
- if (input->primary_selection_device->selection_source) {
- Wayland_primary_selection_source_destroy(input->primary_selection_device->selection_source);
- }
- if (input->primary_selection_device->primary_selection_device) {
- zwp_primary_selection_device_v1_destroy(input->primary_selection_device->primary_selection_device);
- }
- SDL_free(input->primary_selection_device);
- }
- if (input->text_input) {
- zwp_text_input_v3_destroy(input->text_input->text_input);
- SDL_free(input->text_input);
- }
- if (input->keyboard) {
- if (wl_keyboard_get_version(input->keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) {
- wl_keyboard_release(input->keyboard);
- } else {
- wl_keyboard_destroy(input->keyboard);
- }
- }
- if (input->relative_pointer) {
- zwp_relative_pointer_v1_destroy(input->relative_pointer);
- }
- if (input->cursor_shape) {
- wp_cursor_shape_device_v1_destroy(input->cursor_shape);
- }
- if (input->pointer) {
- if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) {
- wl_pointer_release(input->pointer);
- } else {
- wl_pointer_destroy(input->pointer);
- }
- }
- if (input->touch) {
- struct SDL_WaylandTouchPoint *tp, *tmp;
- SDL_DelTouch(1);
- if (wl_touch_get_version(input->touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) {
- wl_touch_release(input->touch);
- } else {
- wl_touch_destroy(input->touch);
- }
- wl_list_for_each_safe (tp, tmp, &touch_points, link) {
- WAYLAND_wl_list_remove(&tp->link);
- SDL_free(tp);
- }
- }
- if (input->tablet_input) {
- Wayland_input_quit_tablet_support(input);
- }
- if (input->seat) {
- if (wl_seat_get_version(input->seat) >= WL_SEAT_RELEASE_SINCE_VERSION) {
- wl_seat_release(input->seat);
- } else {
- wl_seat_destroy(input->seat);
- }
- }
- if (input->xkb.compose_state) {
- WAYLAND_xkb_compose_state_unref(input->xkb.compose_state);
- }
- if (input->xkb.compose_table) {
- WAYLAND_xkb_compose_table_unref(input->xkb.compose_table);
- }
- if (input->xkb.state) {
- WAYLAND_xkb_state_unref(input->xkb.state);
- }
- if (input->xkb.keymap) {
- WAYLAND_xkb_keymap_unref(input->xkb.keymap);
- }
- SDL_free(input);
- d->input = NULL;
- }
- bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input)
- {
- SDL_VideoDevice *vd = SDL_GetVideoDevice();
- SDL_VideoData *d = input->display;
- SDL_Window *window;
- if (!d->relative_pointer_manager) {
- return false;
- }
- if (!d->pointer_constraints) {
- return false;
- }
- if (!input->pointer) {
- return false;
- }
- /* If we have a pointer confine active, we must destroy it here because
- * creating a locked pointer otherwise would be a protocol error.
- */
- for (window = vd->windows; window; window = window->next) {
- pointer_confine_destroy(window);
- }
- for (window = vd->windows; window; window = window->next) {
- Wayland_input_lock_pointer(input, window);
- }
- d->relative_mouse_mode = 1;
- return true;
- }
- bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input)
- {
- SDL_VideoDevice *vd = SDL_GetVideoDevice();
- SDL_VideoData *d = input->display;
- SDL_Window *window;
- for (window = vd->windows; window; window = window->next) {
- Wayland_input_unlock_pointer(input, window);
- }
- d->relative_mouse_mode = 0;
- for (window = vd->windows; window; window = window->next) {
- Wayland_input_confine_pointer(input, window);
- }
- return true;
- }
- bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
- {
- SDL_WindowData *w = window->internal;
- SDL_VideoData *d = input->display;
- struct wl_region *confine_rect;
- if (!d->pointer_constraints) {
- return SDL_SetError("Failed to confine pointer: compositor lacks support for the required zwp_pointer_constraints_v1 protocol");
- }
- if (!input->pointer) {
- return SDL_SetError("No pointer to confine");
- }
- // The confinement region will be created when the window is mapped.
- if (w->shell_surface_status != WAYLAND_SHELL_SURFACE_STATUS_SHOWN) {
- return true;
- }
- /* A confine may already be active, in which case we should destroy it and
- * create a new one.
- */
- pointer_confine_destroy(window);
- /* We cannot create a confine if the pointer is already locked. Defer until
- * the pointer is unlocked.
- */
- if (d->relative_mouse_mode) {
- return true;
- }
- // Don't confine the pointer if it shouldn't be confined.
- if (SDL_RectEmpty(&window->mouse_rect) && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) {
- return true;
- }
- if (SDL_RectEmpty(&window->mouse_rect)) {
- confine_rect = NULL;
- } else {
- SDL_Rect scaled_mouse_rect;
- scaled_mouse_rect.x = (int)SDL_floor(window->mouse_rect.x / w->pointer_scale.x);
- scaled_mouse_rect.y = (int)SDL_floor(window->mouse_rect.y / w->pointer_scale.y);
- scaled_mouse_rect.w = (int)SDL_ceil(window->mouse_rect.w / w->pointer_scale.x);
- scaled_mouse_rect.h = (int)SDL_ceil(window->mouse_rect.h / w->pointer_scale.y);
- confine_rect = wl_compositor_create_region(d->compositor);
- wl_region_add(confine_rect,
- scaled_mouse_rect.x,
- scaled_mouse_rect.y,
- scaled_mouse_rect.w,
- scaled_mouse_rect.h);
- }
- struct zwp_confined_pointer_v1 *confined_pointer =
- zwp_pointer_constraints_v1_confine_pointer(d->pointer_constraints,
- w->surface,
- input->pointer,
- confine_rect,
- ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
- zwp_confined_pointer_v1_add_listener(confined_pointer,
- &confined_pointer_listener,
- window);
- if (confine_rect) {
- wl_region_destroy(confine_rect);
- }
- // Commit the double-buffered confinement region.
- wl_surface_commit(w->surface);
- w->confined_pointer = confined_pointer;
- return true;
- }
- bool Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
- {
- pointer_confine_destroy(window);
- return true;
- }
- bool Wayland_input_grab_keyboard(SDL_Window *window, struct SDL_WaylandInput *input)
- {
- SDL_WindowData *w = window->internal;
- SDL_VideoData *d = input->display;
- if (!d->key_inhibitor_manager) {
- return SDL_SetError("Failed to grab keyboard: compositor lacks support for the required zwp_keyboard_shortcuts_inhibit_manager_v1 protocol");
- }
- if (w->key_inhibitor) {
- return true;
- }
- w->key_inhibitor =
- zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(d->key_inhibitor_manager,
- w->surface,
- input->seat);
- return true;
- }
- bool Wayland_input_ungrab_keyboard(SDL_Window *window)
- {
- SDL_WindowData *w = window->internal;
- if (w->key_inhibitor) {
- zwp_keyboard_shortcuts_inhibitor_v1_destroy(w->key_inhibitor);
- w->key_inhibitor = NULL;
- }
- return true;
- }
- void Wayland_UpdateImplicitGrabSerial(struct SDL_WaylandInput *input, Uint32 serial)
- {
- if (serial > input->last_implicit_grab_serial) {
- input->last_implicit_grab_serial = serial;
- Wayland_data_device_set_serial(input->data_device, serial);
- Wayland_primary_selection_device_set_serial(input->primary_selection_device, serial);
- }
- }
- #endif // SDL_VIDEO_DRIVER_WAYLAND
|