| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971 |
- /**************************************************************************/
- /* wayland_embedder.cpp */
- /**************************************************************************/
- /* This file is part of: */
- /* GODOT ENGINE */
- /* https://godotengine.org */
- /**************************************************************************/
- /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
- /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
- /* */
- /* Permission is hereby granted, free of charge, to any person obtaining */
- /* a copy of this software and associated documentation files (the */
- /* "Software"), to deal in the Software without restriction, including */
- /* without limitation the rights to use, copy, modify, merge, publish, */
- /* distribute, sublicense, and/or sell copies of the Software, and to */
- /* permit persons to whom the Software is furnished to do so, subject to */
- /* the following conditions: */
- /* */
- /* The above copyright notice and this permission notice shall be */
- /* included in all copies or substantial portions of the Software. */
- /* */
- /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
- /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
- /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
- /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
- /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
- /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
- /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
- /**************************************************************************/
- #include "wayland_embedder.h"
- #ifdef WAYLAND_ENABLED
- #ifdef TOOLS_ENABLED
- #include <sys/stat.h>
- #ifdef __FreeBSD__
- #include <dev/evdev/input-event-codes.h>
- #else
- // Assume Linux.
- #include <linux/input-event-codes.h>
- #endif
- #include "core/os/os.h"
- #include <fcntl.h>
- #include <sys/file.h>
- #include <unistd.h>
- #define WAYLAND_EMBED_ID_MAX 1000
- //#define WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- // Gotta flush as we're doing this mess from a thread without any
- // synchronization. It's awful, I know, but the `print_*` utilities hang for
- // some reason during editor startup and I need some quick and dirty debugging.
- #define DEBUG_LOG_WAYLAND_EMBED(...) \
- if (1) { \
- printf("[PROXY] %s\n", vformat(__VA_ARGS__).utf8().ptr()); \
- fflush(stdout); \
- } else \
- ((void)0)
- #else
- #define DEBUG_LOG_WAYLAND_EMBED(...)
- #endif
- // Wayland messages are structured with 32-bit words.
- #define WL_WORD_SIZE (sizeof(uint32_t))
- // Event opcodes. Request opcodes are defined in the generated client headers.
- // We could generate server headers but they would clash (without modifications)
- // and we use just a few constants anyways.
- #define WL_DISPLAY_ERROR 0
- #define WL_DISPLAY_DELETE_ID 1
- #define WL_REGISTRY_GLOBAL 0
- #define WL_REGISTRY_GLOBAL_REMOVE 1
- #define WL_CALLBACK_DONE 0
- #define WL_KEYBOARD_ENTER 1
- #define WL_KEYBOARD_LEAVE 2
- #define WL_KEYBOARD_KEY 3
- #define WL_POINTER_ENTER 0
- #define WL_POINTER_LEAVE 1
- #define WL_POINTER_BUTTON 3
- #define WL_SHM_FORMAT 0
- #define WL_DRM_DEVICE 0
- #define WL_DRM_FORMAT 1
- #define WL_DRM_AUTHENTICATED 2
- #define WL_DRM_CAPABILITIES 3
- #define XDG_POPUP_CONFIGURE 0
- size_t WaylandEmbedder::wl_array_word_offset(uint32_t p_size) {
- uint32_t pad = (WL_WORD_SIZE - (p_size % WL_WORD_SIZE)) % WL_WORD_SIZE;
- return (p_size + pad) / WL_WORD_SIZE;
- }
- const struct wl_interface *WaylandEmbedder::wl_interface_from_string(const char *name, size_t size) {
- for (size_t i = 0; i < (sizeof interfaces / sizeof *interfaces); ++i) {
- if (strncmp(name, interfaces[i]->name, size) == 0) {
- return interfaces[i];
- }
- }
- return nullptr;
- }
- int WaylandEmbedder::wl_interface_get_destructor_opcode(const struct wl_interface *p_iface, uint32_t version) {
- ERR_FAIL_NULL_V(p_iface, -1);
- // FIXME: Figure out how to extract the destructor from the XML files. This
- // value is not currently exposed by wayland-scanner.
- for (int i = 0; i < p_iface->method_count; ++i) {
- const struct wl_message &m = p_iface->methods[i];
- uint32_t destructor_version = String::to_int(m.signature);
- if (destructor_version <= version && (strcmp(m.name, "destroy") == 0 || strcmp(m.name, "release") == 0)) {
- return i;
- }
- }
- return -1;
- }
- struct WaylandEmbedder::WaylandObject *WaylandEmbedder::get_object(uint32_t p_global_id) {
- if (p_global_id == 0) {
- return nullptr;
- }
- // Server-allocated stuff starts at 0xff000000.
- bool is_server = p_global_id & 0xff000000;
- if (is_server) {
- p_global_id &= ~(0xff000000);
- }
- #ifdef DEV_ENABLED
- if (p_global_id >= WAYLAND_EMBED_ID_MAX) {
- // Oh no. Time for debug info!
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- for (uint32_t id = 1; id < objects.reserved_size(); ++id) {
- WaylandObject &object = objects[id];
- DEBUG_LOG_WAYLAND_EMBED(vformat(" - g0x%x (#%d): %s version %d, data 0x%x", id, id, object.interface->name, object.version, (uintptr_t)object.data));
- }
- #endif // WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- CRASH_NOW_MSG(vformat("Tried to access ID bigger than debug cap (%d > %d).", p_global_id, WAYLAND_EMBED_ID_MAX));
- }
- #endif // DEV_ENABLED
- if (is_server) {
- if (server_objects.size() <= p_global_id) {
- return nullptr;
- }
- return &server_objects[p_global_id];
- } else {
- if (objects.reserved_size() <= p_global_id) {
- return nullptr;
- }
- return &objects[p_global_id];
- }
- }
- Error WaylandEmbedder::delete_object(uint32_t p_global_id) {
- WaylandObject *object = get_object(p_global_id);
- ERR_FAIL_NULL_V(object, ERR_DOES_NOT_EXIST);
- if (object->shared) {
- ERR_FAIL_V_MSG(FAILED, vformat("Tried to delete shared object g0x%x.", p_global_id));
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("Deleting object %s g0x%x", object->interface ? object->interface->name : "UNKNOWN", p_global_id));
- if (object->data) {
- memdelete(object->data);
- object->data = nullptr;
- }
- bool is_server = p_global_id & 0xff000000;
- if (is_server) {
- server_objects[p_global_id & ~(0xff000000)] = WaylandObject();
- } else {
- objects.free(p_global_id);
- }
- registry_globals_names.erase(p_global_id);
- return OK;
- }
- uint32_t WaylandEmbedder::Client::allocate_server_id() {
- uint32_t new_id = INVALID_ID;
- if (free_server_ids.size() > 0) {
- int new_size = free_server_ids.size() - 1;
- new_id = free_server_ids[new_size] | 0xff000000;
- free_server_ids.resize_uninitialized(new_size);
- } else {
- new_id = allocated_server_ids | 0xff000000;
- ++allocated_server_ids;
- #ifdef DEV_ENABLED
- CRASH_COND_MSG(allocated_server_ids > WAYLAND_EMBED_ID_MAX, "Max server ID reached. This might indicate a leak.");
- #endif // DEV_ENABLED
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("Allocated server-side id 0x%x.", new_id));
- return new_id;
- }
- struct WaylandEmbedder::WaylandObject *WaylandEmbedder::Client::get_object(uint32_t p_local_id) {
- if (p_local_id == INVALID_ID) {
- return nullptr;
- }
- if (global_instances.has(p_local_id)) {
- return &global_instances[p_local_id];
- }
- if (fake_objects.has(p_local_id)) {
- return &fake_objects[p_local_id];
- }
- if (!global_ids.has(p_local_id)) {
- return nullptr;
- }
- ERR_FAIL_NULL_V(embedder, nullptr);
- return embedder->get_object(get_global_id(p_local_id));
- }
- Error WaylandEmbedder::Client::bind_global_id(uint32_t p_global_id, uint32_t p_local_id) {
- ERR_FAIL_COND_V(local_ids.has(p_global_id), ERR_ALREADY_EXISTS);
- ERR_FAIL_COND_V(global_ids.has(p_local_id), ERR_ALREADY_EXISTS);
- GlobalIdInfo gid_info;
- gid_info.id = p_global_id;
- DEBUG_LOG_WAYLAND_EMBED(vformat("Pushing g0x%x in the global id history", p_global_id));
- gid_info.history_elem = global_id_history.push_back(p_global_id);
- global_ids[p_local_id] = gid_info;
- local_ids[p_global_id] = p_local_id;
- return OK;
- }
- Error WaylandEmbedder::Client::delete_object(uint32_t p_local_id) {
- if (fake_objects.has(p_local_id)) {
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- WaylandObject *object = &fake_objects[p_local_id];
- DEBUG_LOG_WAYLAND_EMBED(vformat("Deleting fake object %s l0x%x", object->interface ? object->interface->name : "UNKNOWN", p_local_id));
- #endif
- if (!(p_local_id & 0xff000000)) {
- // wl_display::delete_id
- send_wayland_message(socket, DISPLAY_ID, 1, { p_local_id });
- }
- fake_objects.erase(p_local_id);
- // We can skip everything else below, as fake objects don't have a global id.
- return OK;
- }
- ERR_FAIL_COND_V(!global_ids.has(p_local_id), ERR_DOES_NOT_EXIST);
- GlobalIdInfo gid_info = global_ids[p_local_id];
- uint32_t global_id = gid_info.id;
- DEBUG_LOG_WAYLAND_EMBED(vformat("Erasing g0x%x from the global id history", global_id));
- global_id_history.erase(gid_info.history_elem);
- if (global_instances.has(p_local_id)) {
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- WaylandObject *object = &global_instances[p_local_id];
- DEBUG_LOG_WAYLAND_EMBED(vformat("Deleting global instance %s l0x%x", object->interface ? object->interface->name : "UNKNOWN", p_local_id));
- #endif
- // wl_display::delete_id
- send_wayland_message(socket, DISPLAY_ID, 1, { p_local_id });
- // We don't want to delete the global object tied to this instance, so we'll only get rid of the local stuff.
- global_instances.erase(p_local_id);
- global_ids.erase(p_local_id);
- if (global_id != INVALID_ID) {
- local_ids.erase(global_id);
- }
- // We're done here.
- return OK;
- }
- if (wl_registry_instances.has(p_local_id)) {
- wl_registry_instances.erase(p_local_id);
- }
- WaylandObject *object = embedder->get_object(global_id);
- ERR_FAIL_NULL_V(object, ERR_DOES_NOT_EXIST);
- ERR_FAIL_COND_V_MSG(object->shared, ERR_INVALID_PARAMETER, vformat("Tried to delete shared object g0x%x.", global_id));
- global_ids.erase(p_local_id);
- local_ids.erase(global_id);
- if (p_local_id & 0xff000000) {
- free_server_ids.push_back(p_local_id & ~(0xff000000));
- }
- uint32_t *global_name = embedder->registry_globals_names.getptr(global_id);
- if (global_name) {
- {
- RegistryGlobalInfo &info = embedder->registry_globals[*global_name];
- ERR_FAIL_COND_V_MSG(info.instance_counter == 0, ERR_BUG, "Instance counter inconsistency.");
- --info.instance_counter;
- if (info.destroyed && info.instance_counter == 0) {
- embedder->registry_globals.erase(*global_name);
- }
- }
- registry_globals_instances[*global_name].erase(p_local_id);
- }
- return embedder->delete_object(global_id);
- }
- // Returns INVALID_ID if the creation fails. In that case, the user can assume
- // that the client got kicked out.
- uint32_t WaylandEmbedder::Client::new_object(uint32_t p_local_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {
- if (embedder == nullptr) {
- socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "No embedder set.");
- ERR_FAIL_V(INVALID_ID);
- }
- if (get_object(p_local_id) != nullptr) {
- socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, vformat("Tried to create %s l0x%x but it already exists as %s", p_interface->name, p_local_id, get_object(p_local_id)->interface->name));
- ERR_FAIL_V(INVALID_ID);
- }
- uint32_t new_global_id = embedder->new_object(p_interface, p_version, p_data);
- bind_global_id(new_global_id, p_local_id);
- return new_global_id;
- }
- uint32_t WaylandEmbedder::Client::new_server_object(uint32_t p_global_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {
- if (embedder == nullptr) {
- socket_error(socket, get_local_id(p_global_id), WL_DISPLAY_ERROR_IMPLEMENTATION, "No embedder set.");
- ERR_FAIL_V(INVALID_ID);
- }
- uint32_t new_local_id = allocate_server_id();
- embedder->new_server_object(p_global_id, p_interface, p_version, p_data);
- bind_global_id(p_global_id, new_local_id);
- return new_local_id;
- }
- WaylandEmbedder::WaylandObject *WaylandEmbedder::Client::new_fake_object(uint32_t p_local_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {
- if (embedder == nullptr) {
- socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "No embedder set.");
- ERR_FAIL_V(nullptr);
- }
- if (get_object(p_local_id) != nullptr) {
- socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, vformat("Object l0x%x already exists", p_local_id));
- ERR_FAIL_V(nullptr);
- }
- WaylandObject &new_object = fake_objects[p_local_id];
- new_object.interface = p_interface;
- new_object.version = p_version;
- new_object.data = p_data;
- return &new_object;
- }
- WaylandEmbedder::WaylandObject *WaylandEmbedder::Client::new_global_instance(uint32_t p_local_id, uint32_t p_global_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {
- if (embedder == nullptr) {
- socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "No embedder set.");
- ERR_FAIL_V(nullptr);
- }
- if (get_object(p_local_id) != nullptr) {
- socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, vformat("Object l0x%x already exists", p_local_id));
- ERR_FAIL_V(nullptr);
- }
- WaylandObject &new_object = global_instances[p_local_id];
- new_object.interface = p_interface;
- new_object.version = p_version;
- new_object.data = p_data;
- // FIXME: Track each instance properly. Global instances (the compatibility
- // mechanism) are particular as they're the only case where a global ID might
- // map to multiple local objects. In that case we need to mirror each event
- // which passes a registry object as an argument for each instance.
- GlobalIdInfo gid_info;
- gid_info.id = p_global_id;
- gid_info.history_elem = global_id_history.push_back(p_global_id);
- global_ids[p_local_id] = gid_info;
- // NOTE: Normally, for each client, there's a single local object per global
- // object, but global instances break this expectation. This is technically
- // wrong but should work fine, as we have special logic whenever needed.
- //
- // TODO: it might be nice to enforce that this table is never looked up for
- // global instances or even just log attempts.
- local_ids[p_global_id] = p_local_id;
- return &new_object;
- }
- Error WaylandEmbedder::Client::send_wl_drm_state(uint32_t p_id, WaylandDrmGlobalData *p_state) {
- ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
- if (p_state->device.is_empty()) {
- // Not yet initialized.
- return OK;
- }
- LocalVector<union wl_argument> args;
- args.push_back(wl_arg_string(p_state->device.utf8().get_data()));
- send_wayland_event(socket, p_id, wl_drm_interface, WL_DRM_DEVICE, args);
- for (uint32_t format : p_state->formats) {
- Error err = send_wayland_message(socket, p_id, WL_DRM_FORMAT, { format });
- ERR_FAIL_COND_V(err != OK, err);
- }
- if (p_state->authenticated) {
- Error err = send_wayland_message(socket, p_id, WL_DRM_AUTHENTICATED, {});
- ERR_FAIL_COND_V(err != OK, err);
- }
- Error err = send_wayland_message(socket, p_id, WL_DRM_CAPABILITIES, { p_state->capabilities });
- ERR_FAIL_COND_V(err != OK, err);
- return OK;
- }
- void WaylandEmbedder::cleanup_socket(int p_socket) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Cleaning up socket %d.", p_socket));
- close(p_socket);
- for (size_t i = 0; i < pollfds.size(); ++i) {
- if (pollfds[i].fd == p_socket) {
- pollfds.remove_at_unordered(i);
- break;
- }
- }
- ERR_FAIL_COND(!clients.has(p_socket));
- Client &client = clients[p_socket];
- for (KeyValue<uint32_t, WaylandObject> &pair : client.fake_objects) {
- WaylandObject &object = pair.value;
- if (object.interface == &xdg_toplevel_interface) {
- XdgToplevelData *data = (XdgToplevelData *)object.data;
- CRASH_COND(data == nullptr);
- if (data->wl_subsurface_id != INVALID_ID) {
- // wl_subsurface::destroy() - xdg_toplevels are mapped to subsurfaces.
- send_wayland_message(compositor_socket, data->wl_subsurface_id, 0, {});
- }
- if (!data->xdg_surface_handle.get()) {
- continue;
- }
- XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)data->xdg_surface_handle.get()->data;
- if (xdg_surf_data == nullptr) {
- continue;
- }
- if (!data->parent_handle.get()) {
- continue;
- }
- XdgToplevelData *parent_data = (XdgToplevelData *)data->parent_handle.get()->data;
- if (parent_data == nullptr) {
- continue;
- }
- if (!parent_data->xdg_surface_handle.get()) {
- continue;
- }
- XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)parent_data->xdg_surface_handle.get()->data;
- if (parent_xdg_surf_data == nullptr) {
- continue;
- }
- for (uint32_t wl_seat_name : wl_seat_names) {
- WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;
- if (global_seat_data == nullptr) {
- continue;
- }
- if (global_seat_data->focused_surface_id == xdg_surf_data->wl_surface_id) {
- seat_name_leave_surface(wl_seat_name, xdg_surf_data->wl_surface_id);
- seat_name_enter_surface(wl_seat_name, parent_xdg_surf_data->wl_surface_id);
- }
- }
- }
- }
- for (List<uint32_t>::Element *E = client.global_id_history.back(); E;) {
- uint32_t global_id = E->get();
- E = E->prev();
- WaylandObject *object = get_object(global_id);
- if (object == nullptr) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Skipping deletability check of object g0x%x as it's null.", global_id));
- continue;
- }
- if (object->interface == nullptr) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Skipping deletability check of object g0x%x as it's invalid.", global_id));
- continue;
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("Checking deletability of %s#g0x%x version %s", object->interface->name, global_id, object->version));
- if (object->shared) {
- DEBUG_LOG_WAYLAND_EMBED("Shared, skipping.");
- continue;
- }
- if (object->interface == &wl_callback_interface) {
- // Those things self-destruct.
- DEBUG_LOG_WAYLAND_EMBED("wl_callback self destructs.");
- continue;
- }
- if (object->destroyed) {
- DEBUG_LOG_WAYLAND_EMBED("Already destroyed, skipping.");
- continue;
- }
- int destructor = wl_interface_get_destructor_opcode(object->interface, object->version);
- if (destructor >= 0) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Destroying %s#g0x%x", object->interface->name, global_id));
- if (object->interface == &wl_surface_interface) {
- for (uint32_t wl_seat_name : wl_seat_names) {
- WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;
- if (global_seat_data) {
- if (global_seat_data->pointed_surface_id == global_id) {
- global_seat_data->pointed_surface_id = INVALID_ID;
- }
- if (global_seat_data->focused_surface_id == global_id) {
- global_seat_data->focused_surface_id = INVALID_ID;
- }
- }
- }
- }
- send_wayland_message(compositor_socket, global_id, destructor, {});
- object->destroyed = true;
- if (global_id & 0xff000000) {
- delete_object(global_id);
- object = nullptr;
- }
- }
- if (object && !object->destroyed) {
- ERR_PRINT(vformat("Unreferenced object %s g0x%x (leak!)", object->interface->name, global_id));
- }
- }
- uint32_t eclient_id = client.embedded_client_id;
- clients.erase(client.socket);
- WaylandObject *eclient = main_client->get_object(eclient_id);
- if (eclient) {
- EmbeddedClientData *eclient_data = (EmbeddedClientData *)eclient->data;
- ERR_FAIL_NULL(eclient_data);
- if (!eclient_data->disconnected) {
- // godot_embedded_client::disconnected
- send_wayland_message(main_client->socket, eclient_id, 0, {});
- }
- eclient_data->disconnected = true;
- }
- }
- void WaylandEmbedder::socket_error(int p_socket, uint32_t p_object_id, uint32_t p_code, const String &p_message) {
- const char *err_name = "unknown";
- switch (p_code) {
- case WL_DISPLAY_ERROR_INVALID_OBJECT: {
- err_name = "invalid_object";
- } break;
- case WL_DISPLAY_ERROR_INVALID_METHOD: {
- err_name = "invalid_method";
- } break;
- case WL_DISPLAY_ERROR_NO_MEMORY: {
- err_name = "no_memory";
- } break;
- case WL_DISPLAY_ERROR_IMPLEMENTATION: {
- err_name = "implementation";
- } break;
- }
- ERR_PRINT(vformat("Socket %d %s error: %s", p_socket, err_name, p_message));
- LocalVector<union wl_argument> args;
- args.push_back(wl_arg_object(p_object_id));
- args.push_back(wl_arg_uint(p_code));
- args.push_back(wl_arg_string(vformat("[Godot Embedder] %s", p_message).utf8().get_data()));
- send_wayland_event(p_socket, DISPLAY_ID, wl_display_interface, WL_DISPLAY_ERROR, args);
- // So, here's the deal: from some extensive research I did, there are
- // absolutely zero safeguards for ensuring that the error message ends to the
- // client. It's absolutely tiny and takes _nothing_ to get there (less than
- // 4µs with a debug build on my machine), but still enough to get truncated in
- // the distance between `send_wayland_event` and `close`.
- //
- // Because of this we're going to give the client some slack: we're going to
- // wait for its socket to close (or whatever) or 1s, whichever happens first.
- //
- // Hopefully it's good enough for <1000 bytes :P
- struct pollfd pollfd = {};
- pollfd.fd = p_socket;
- int ret = poll(&pollfd, 1, 1'000);
- if (ret == 0) {
- ERR_PRINT("Client timeout while disconnecting.");
- }
- if (ret < 0) {
- ERR_PRINT(vformat("Client error while disconnecting: %s", strerror(errno)));
- }
- close(p_socket);
- }
- void WaylandEmbedder::poll_sockets() {
- if (poll(pollfds.ptr(), pollfds.size(), -1) == -1) {
- CRASH_NOW_MSG(vformat("poll() failed, errno %d.", errno));
- }
- // First handle everything but the listening socket (which is always the first
- // element), so that we can cleanup closed sockets before accidentally reusing
- // them (and breaking everything).
- for (size_t i = 1; i < pollfds.size(); ++i) {
- handle_fd(pollfds[i].fd, pollfds[i].revents);
- }
- handle_fd(pollfds[0].fd, pollfds[0].revents);
- }
- Error WaylandEmbedder::send_raw_message(int p_socket, std::initializer_list<struct iovec> p_vecs, const LocalVector<int> &p_fds) {
- struct msghdr msg = {};
- msg.msg_iov = (struct iovec *)p_vecs.begin();
- msg.msg_iovlen = p_vecs.size();
- if (!p_fds.is_empty()) {
- size_t data_size = p_fds.size() * sizeof(int);
- msg.msg_control = Memory::alloc_aligned_static(CMSG_SPACE(data_size), CMSG_ALIGN(1));
- msg.msg_controllen = CMSG_SPACE(data_size);
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(data_size);
- // NOTE: According to the linux man page cmsg(5), we shall not access the
- // pointer returned CMSG_DATA directly, due to alignment concerns. We should
- // copy data from a suitably aligned object instead.
- memcpy(CMSG_DATA(cmsg), p_fds.ptr(), data_size);
- }
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- printf("[PROXY] Sending: ");
- for (const struct iovec &vec : p_vecs) {
- for (size_t i = 0; i < vec.iov_len; ++i) {
- printf("%.2x", ((const uint8_t *)vec.iov_base)[i]);
- }
- }
- printf("\n");
- #endif
- sendmsg(p_socket, &msg, MSG_NOSIGNAL);
- if (msg.msg_control) {
- Memory::free_aligned_static(msg.msg_control);
- }
- return OK;
- }
- Error WaylandEmbedder::send_wayland_message(int p_socket, uint32_t p_id, uint32_t p_opcode, const uint32_t *p_args, const size_t p_args_words) {
- ERR_FAIL_COND_V(p_socket < 0, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(p_id == INVALID_ID, ERR_INVALID_PARAMETER);
- uint32_t args_size = p_args_words * sizeof *p_args;
- // Header is always 8 bytes long.
- uint32_t total_size = 8 + (args_size);
- uint32_t header[2] = { p_id, (total_size << 16) + p_opcode };
- struct iovec vecs[2] = {
- { header, 8 },
- // According to the sendmsg manual, these buffers should never be written to,
- // so this cast should be safe.
- { (void *)p_args, args_size },
- };
- struct msghdr msg = {};
- msg.msg_iov = vecs;
- msg.msg_iovlen = std_size(vecs);
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- printf("[PROXY] Sending: ");
- for (struct iovec &vec : vecs) {
- for (size_t i = 0; i < vec.iov_len; ++i) {
- printf("%.2x", ((const uint8_t *)vec.iov_base)[i]);
- }
- }
- printf("\n");
- #endif
- if (sendmsg(p_socket, &msg, MSG_NOSIGNAL) < 0) {
- return FAILED;
- }
- return OK;
- }
- Error WaylandEmbedder::send_wayland_message(ProxyDirection p_direction, int p_socket, uint32_t p_id, const struct wl_interface &p_interface, uint32_t p_opcode, const LocalVector<union wl_argument> &p_args) {
- ERR_FAIL_COND_V(p_direction == ProxyDirection::CLIENT && p_opcode >= (uint32_t)p_interface.event_count, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(p_direction == ProxyDirection::COMPOSITOR && p_opcode >= (uint32_t)p_interface.method_count, ERR_INVALID_PARAMETER);
- const struct wl_message &msg = p_direction == ProxyDirection::CLIENT ? p_interface.events[p_opcode] : p_interface.methods[p_opcode];
- LocalVector<uint32_t> arg_buf;
- size_t arg_idx = 0;
- for (size_t sig_idx = 0; sig_idx < strlen(msg.signature); ++sig_idx) {
- if (arg_idx >= p_args.size()) {
- String err_msg = vformat("Not enough arguments for r0x%d %s.%s(%s) (only got %d)", p_id, p_interface.name, msg.name, msg.signature, p_args.size());
- ERR_FAIL_COND_V_MSG(arg_idx >= p_args.size(), ERR_INVALID_PARAMETER, err_msg);
- }
- char sym = msg.signature[sig_idx];
- if (sym >= '0' && sym <= '?') {
- // We don't care about version notices and nullability symbols. We can skip
- // those.
- continue;
- }
- const union wl_argument &arg = p_args[arg_idx];
- switch (sym) {
- case 'i': {
- arg_buf.push_back((uint32_t)arg.i);
- } break;
- case 'u': {
- arg_buf.push_back(arg.u);
- } break;
- case 'f': {
- arg_buf.push_back((uint32_t)arg.f);
- } break;
- case 'o': {
- // We're encoding object arguments as uints because I don't think we can
- // reuse the whole opaque struct thing.
- arg_buf.push_back(arg.u);
- } break;
- case 'n': {
- arg_buf.push_back(arg.n);
- } break;
- case 's': {
- const char *str = p_args[arg_idx].s;
- // Wayland requires the string length to include the null terminator.
- uint32_t str_len = strlen(str) + 1;
- arg_buf.push_back(str_len);
- size_t data_begin_idx = arg_buf.size();
- uint32_t str_words = wl_array_word_offset(str_len);
- arg_buf.resize(arg_buf.size() + str_words);
- strcpy((char *)(arg_buf.ptr() + data_begin_idx), str);
- } break;
- case 'a': {
- const wl_array *arr = p_args[arg_idx].a;
- arg_buf.push_back(arr->size);
- size_t data_begin_idx = arg_buf.size();
- uint32_t words = wl_array_word_offset(arr->size);
- arg_buf.resize(arg_buf.size() + words);
- memcpy(arg_buf.ptr() + data_begin_idx, arr->data, arr->size);
- } break;
- // FDs (h) are encoded out-of-band.
- }
- ++arg_idx;
- }
- send_wayland_message(p_socket, p_id, p_opcode, arg_buf.ptr(), arg_buf.size());
- return OK;
- }
- uint32_t WaylandEmbedder::new_object(const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {
- uint32_t new_global_id = allocate_global_id();
- DEBUG_LOG_WAYLAND_EMBED(vformat("New object g0x%x %s", new_global_id, p_interface->name));
- WaylandObject *new_object = get_object(new_global_id);
- new_object->interface = p_interface;
- new_object->version = p_version;
- new_object->data = p_data;
- return new_global_id;
- }
- WaylandEmbedder::WaylandObject *WaylandEmbedder::new_server_object(uint32_t p_global_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {
- // The max ID will never increment more than one at a time, due to the
- // packed nature of IDs. libwayland already does similar assertions so it
- // just makes sense to double-check to avoid messing memory up or
- // allocating a huge buffer for nothing.
- uint32_t stripped_id = p_global_id & ~(0xff000000);
- ERR_FAIL_COND_V_MSG(stripped_id > server_objects.size(), nullptr, "Invalid new server id requested.");
- ERR_FAIL_COND_V_MSG(get_object(p_global_id) && get_object(p_global_id)->interface, nullptr, vformat("Tried to create %s g0x%x but it already exists as %s.", p_interface->name, p_global_id, get_object(p_global_id)->interface->name));
- if (stripped_id == server_objects.size()) {
- server_objects.resize(server_objects.size() + 1);
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("New server object %s g0x%x", p_interface->name, p_global_id));
- WaylandObject *new_object = get_object(p_global_id);
- new_object->interface = p_interface;
- new_object->version = p_version;
- new_object->data = p_data;
- return new_object;
- }
- void WaylandEmbedder::sync() {
- CRASH_COND_MSG(sync_callback_id, "Sync already in progress.");
- sync_callback_id = allocate_global_id();
- get_object(sync_callback_id)->interface = &wl_callback_interface;
- get_object(sync_callback_id)->version = 1;
- send_wayland_message(compositor_socket, DISPLAY_ID, 0, { sync_callback_id });
- DEBUG_LOG_WAYLAND_EMBED("Synchronizing");
- while (true) {
- poll_sockets();
- if (!sync_callback_id) {
- // Obj got deleted - sync is done.
- return;
- }
- }
- }
- // Returns the gid for the newly bound object, or an existing shared object if
- // necessary.
- uint32_t WaylandEmbedder::wl_registry_bind(uint32_t p_registry_id, uint32_t p_name, int p_version) {
- RegistryGlobalInfo &info = registry_globals[p_name];
- uint32_t id = INVALID_ID;
- if (wl_interface_get_destructor_opcode(info.interface, p_version) < 0) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Binding instanced global %s %d", info.interface->name, p_version));
- // Reusable object.
- if (info.reusable_objects.has(p_version) && info.reusable_objects[p_version] != INVALID_ID) {
- DEBUG_LOG_WAYLAND_EMBED("Already bound.");
- return info.reusable_objects[p_version];
- }
- id = new_object(info.interface, p_version);
- ERR_FAIL_COND_V(id == INVALID_ID, INVALID_ID);
- info.reusable_objects[p_version] = id;
- get_object(id)->shared = true;
- } else {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Binding global %s as g0x%x version %d", info.interface->name, id, p_version));
- id = new_object(info.interface, p_version);
- }
- ERR_FAIL_COND_V(id == INVALID_ID, INVALID_ID);
- registry_globals_names[id] = p_name;
- LocalVector<union wl_argument> args;
- args.push_back(wl_arg_uint(info.compositor_name));
- args.push_back(wl_arg_string(info.interface->name));
- args.push_back(wl_arg_int(p_version));
- args.push_back(wl_arg_new_id(id));
- Error err = send_wayland_method(compositor_socket, p_registry_id, wl_registry_interface, WL_REGISTRY_BIND, args);
- ERR_FAIL_COND_V_MSG(err != OK, INVALID_ID, "Error while sending bind request.");
- return id;
- }
- void WaylandEmbedder::seat_name_enter_surface(uint32_t p_seat_name, uint32_t p_wl_surface_id) {
- WaylandSurfaceData *surf_data = (WaylandSurfaceData *)get_object(p_wl_surface_id)->data;
- CRASH_COND(surf_data == nullptr);
- Client *client = surf_data->client;
- CRASH_COND(client == nullptr);
- if (!client->local_ids.has(p_wl_surface_id)) {
- DEBUG_LOG_WAYLAND_EMBED("Called seat_name_enter_surface with an unknown surface");
- return;
- }
- uint32_t local_surface_id = client->get_local_id(p_wl_surface_id);
- DEBUG_LOG_WAYLAND_EMBED(vformat("KB: Entering surface g0x%x", p_wl_surface_id));
- for (uint32_t local_seat_id : client->registry_globals_instances[p_seat_name]) {
- WaylandSeatInstanceData *seat_data = (WaylandSeatInstanceData *)client->get_object(local_seat_id)->data;
- CRASH_COND(seat_data == nullptr);
- uint32_t local_keyboard_id = client->get_local_id(seat_data->wl_keyboard_id);
- if (local_keyboard_id != INVALID_ID) {
- // TODO: track keys. Not super important at the time of writing, since we
- // don't use that in the engine, although we should.
- // wl_keyboard::enter(serial, surface, keys) - keys will be empty for now
- send_wayland_message(client->socket, local_keyboard_id, 1, { serial_counter++, local_surface_id, 0 });
- }
- }
- if (client->socket != main_client->socket) {
- // godot_embedded_client::window_focus_in
- send_wayland_message(main_client->socket, client->embedded_client_id, 2, {});
- }
- }
- void WaylandEmbedder::seat_name_leave_surface(uint32_t p_seat_name, uint32_t p_wl_surface_id) {
- WaylandSurfaceData *surf_data = (WaylandSurfaceData *)get_object(p_wl_surface_id)->data;
- CRASH_COND(surf_data == nullptr);
- Client *client = surf_data->client;
- CRASH_COND(client == nullptr);
- if (!client->local_ids.has(p_wl_surface_id)) {
- DEBUG_LOG_WAYLAND_EMBED("Called seat_name_leave_surface with an unknown surface!");
- return;
- }
- uint32_t local_surface_id = client->get_local_id(p_wl_surface_id);
- DEBUG_LOG_WAYLAND_EMBED(vformat("KB: Leaving surface g0x%x", p_wl_surface_id));
- for (uint32_t local_seat_id : client->registry_globals_instances[p_seat_name]) {
- WaylandSeatInstanceData *seat_data = (WaylandSeatInstanceData *)client->get_object(local_seat_id)->data;
- CRASH_COND(seat_data == nullptr);
- uint32_t local_keyboard_id = client->get_local_id(seat_data->wl_keyboard_id);
- if (local_keyboard_id != INVALID_ID) {
- // wl_keyboard::enter(serial, surface, keys) - keys will be empty for now
- send_wayland_message(client->socket, local_keyboard_id, 2, { serial_counter++, local_surface_id });
- }
- }
- if (client != main_client) {
- // godot_embedded_client::window_focus_out
- send_wayland_message(main_client->socket, client->embedded_client_id, 3, {});
- }
- }
- int WaylandEmbedder::allocate_global_id() {
- uint32_t id = INVALID_ID;
- objects.request(id);
- objects[id] = WaylandObject();
- DEBUG_LOG_WAYLAND_EMBED(vformat("Allocated new global id g0x%x", id));
- #ifdef DEV_ENABLED
- if (id > WAYLAND_EMBED_ID_MAX) {
- // Oh no. Time for debug info!
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- for (uint32_t i = 1; i < objects.reserved_size(); ++i) {
- WaylandObject &object = objects[id];
- DEBUG_LOG_WAYLAND_EMBED(vformat(" - g0x%x (#%d): %s version %d, data 0x%x", i, i, object.interface->name, object.version, (uintptr_t)object.data));
- }
- #endif // WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- CRASH_NOW_MSG("Max ID reached. This might indicate a leak.");
- }
- #endif // DEV_ENABLED
- return id;
- }
- bool WaylandEmbedder::global_surface_is_window(uint32_t p_wl_surface_id) {
- WaylandObject *surface_object = get_object(p_wl_surface_id);
- ERR_FAIL_NULL_V(surface_object, false);
- if (surface_object->interface != &wl_surface_interface || surface_object->data == nullptr) {
- return false;
- }
- WaylandSurfaceData *surface_data = (WaylandSurfaceData *)surface_object->data;
- if (!surface_data->role_object_handle.get()) {
- return false;
- }
- WaylandObject *role_object = surface_data->role_object_handle.get();
- return (role_object && role_object->interface == &xdg_toplevel_interface);
- }
- bool WaylandEmbedder::handle_generic_msg(Client *client, const WaylandObject *p_object, const struct wl_message *message, const struct msg_info *info, uint32_t *buf, uint32_t instance_id) {
- // We allow client-less events.
- CRASH_COND(client == nullptr && info->direction == ProxyDirection::COMPOSITOR);
- ERR_FAIL_NULL_V(p_object, false);
- bool valid = true;
- // Let's strip the header.
- uint32_t *body = buf + 2;
- size_t arg_idx = 0;
- size_t buf_idx = 0;
- size_t last_str_buf_idx = -1;
- uint32_t last_str_len = 0;
- for (size_t i = 0; i < strlen(message->signature); ++i) {
- ERR_FAIL_COND_V(buf_idx > (info->size / sizeof *body), false);
- char sym = message->signature[i];
- if (sym >= '0' && sym <= '?') {
- // We don't care about version notices and nullability symbols. We can skip
- // those.
- continue;
- }
- switch (sym) {
- case 'a': {
- uint32_t array_len = body[buf_idx];
- // We can't obviously go forward by just one byte. Let's skip to the end of
- // the array.
- buf_idx += wl_array_word_offset(array_len);
- } break;
- case 's': {
- uint32_t string_len = body[buf_idx];
- last_str_buf_idx = buf_idx;
- last_str_len = string_len;
- // Same as the array.
- buf_idx += wl_array_word_offset(string_len);
- } break;
- case 'n': {
- uint32_t arg = body[buf_idx];
- const struct wl_interface *new_interface = message->types[arg_idx];
- uint32_t new_version = p_object->version;
- if (!new_interface && last_str_len != 0) {
- // When the protocol definition does not define an interface it reports a
- // string and an unsigned integer representing the interface and the
- // version requested.
- new_interface = wl_interface_from_string((char *)(body + last_str_buf_idx + 1), last_str_len);
- new_version = body[arg_idx - 1];
- }
- if (new_interface == nullptr) {
- if (last_str_len > 0) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Unknown interface %s, marking packet as invalid.", (char *)(body + last_str_buf_idx + 1)));
- } else {
- DEBUG_LOG_WAYLAND_EMBED("Unknown interface, marking packet as invalid.");
- }
- valid = false;
- break;
- }
- if (info->direction == ProxyDirection::COMPOSITOR) {
- // FIXME: Create objects only if the packet is valid.
- uint32_t new_local_id = arg;
- body[buf_idx] = client->new_object(new_local_id, new_interface, new_version);
- if (body[buf_idx] == INVALID_ID) {
- valid = false;
- break;
- }
- } else if (info->direction == ProxyDirection::CLIENT) {
- uint32_t new_global_id = arg;
- if (client) {
- body[buf_idx] = client->new_server_object(new_global_id, new_interface, new_version);
- } else {
- new_server_object(new_global_id, new_interface, new_version);
- }
- if (body[buf_idx] == INVALID_ID) {
- valid = false;
- break;
- }
- }
- } break;
- case 'o': {
- if (!client) {
- break;
- }
- uint32_t obj_id = body[buf_idx];
- if (obj_id == 0) {
- // Object arguments can be nil.
- break;
- }
- if (info->direction == ProxyDirection::CLIENT) {
- if (!client->local_ids.has(obj_id)) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Object argument g0x%x not found, marking packet as invalid.", obj_id));
- valid = false;
- break;
- }
- body[buf_idx] = instance_id != INVALID_ID ? instance_id : client->get_local_id(obj_id);
- } else if (info->direction == ProxyDirection::COMPOSITOR) {
- if (!client->global_ids.has(obj_id)) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Object argument l0x%x not found, marking packet as invalid.", obj_id));
- valid = false;
- break;
- }
- body[buf_idx] = client->get_global_id(obj_id);
- }
- } break;
- }
- ++arg_idx;
- ++buf_idx;
- }
- return valid;
- }
- WaylandEmbedder::MessageStatus WaylandEmbedder::handle_request(LocalObjectHandle p_object, uint32_t p_opcode, const uint32_t *msg_data, size_t msg_len) {
- ERR_FAIL_COND_V(!p_object.is_valid(), MessageStatus::HANDLED);
- WaylandObject *object = p_object.get();
- Client *client = p_object.get_client();
- ERR_FAIL_NULL_V(object, MessageStatus::HANDLED);
- // NOTE: Global ID may be null.
- uint32_t global_id = p_object.get_global_id();
- uint32_t local_id = p_object.get_local_id();
- ERR_FAIL_NULL_V(object->interface, MessageStatus::ERROR);
- const struct wl_interface *interface = object->interface;
- ERR_FAIL_COND_V((int)p_opcode >= interface->method_count, MessageStatus::ERROR);
- const struct wl_message message = interface->methods[p_opcode];
- DEBUG_LOG_WAYLAND_EMBED(vformat("Client #%d -> %s::%s(%s) l0x%x g0x%x", client->socket, interface->name, message.name, message.signature, local_id, global_id));
- const uint32_t *body = msg_data + 2;
- if (registry_globals_names.has(global_id)) {
- int global_name = registry_globals_names[global_id];
- ERR_FAIL_COND_V(!registry_globals.has(global_name), MessageStatus::ERROR);
- RegistryGlobalInfo &global_info = registry_globals[global_name];
- if (global_info.destroyed) {
- DEBUG_LOG_WAYLAND_EMBED("Skipping request for destroyed global object");
- return MessageStatus::HANDLED;
- }
- }
- if (object->interface == &wl_display_interface && p_opcode == WL_DISPLAY_GET_REGISTRY) {
- // The gist of this is that the registry is a global and the compositor can
- // quite simply take for granted that a single client can access any global
- // bound from any registry. Let's remove all doubts by using a single
- // registry (also for efficiency) and doing fancy remaps.
- uint32_t local_registry_id = body[0];
- // Note that the registry has already been allocated in the initialization
- // routine.
- for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {
- uint32_t global_name = pair.key;
- RegistryGlobalInfo &global_info = pair.value;
- if (global_info.destroyed) {
- continue;
- }
- const struct wl_interface *global_interface = global_info.interface;
- if (client != main_client && embedded_interface_deny_list.has(global_interface)) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Skipped global announcement %s for embedded client.", global_interface->name));
- continue;
- }
- LocalVector<union wl_argument> args;
- args.push_back(wl_arg_uint(global_name));
- args.push_back(wl_arg_string(global_interface->name));
- args.push_back(wl_arg_uint(global_info.version));
- send_wayland_event(client->socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL, args);
- }
- client->wl_registry_instances.insert(local_registry_id);
- client->new_global_instance(local_registry_id, REGISTRY_ID, &wl_registry_interface, 1);
- return MessageStatus::HANDLED;
- }
- if (object->interface == &wl_registry_interface) {
- if (p_opcode == WL_REGISTRY_BIND) {
- // [Request] wl_registry::bind(usun)
- uint32_t global_name = body[0];
- uint32_t interface_name_len = body[1];
- //const char *interface_name = (const char *)(body + 2);
- uint32_t version = body[2 + wl_array_word_offset(interface_name_len)];
- uint32_t new_local_id_idx = 2 + wl_array_word_offset(interface_name_len) + 1;
- uint32_t new_local_id = body[new_local_id_idx];
- if (!registry_globals.has(global_name)) {
- socket_error(client->socket, local_id, WL_DISPLAY_ERROR_INVALID_METHOD, vformat("Invalid global object #%d", global_name));
- return MessageStatus::HANDLED;
- }
- RegistryGlobalInfo &global_info = registry_globals[global_name];
- ERR_FAIL_NULL_V(global_info.interface, MessageStatus::ERROR);
- version = MIN(global_info.version, version);
- if (global_info.interface == &godot_embedding_compositor_interface) {
- if (!client->registry_globals_instances.has(global_name)) {
- client->registry_globals_instances[global_name] = {};
- }
- client->registry_globals_instances[global_name].insert(new_local_id);
- ++global_info.instance_counter;
- DEBUG_LOG_WAYLAND_EMBED("Bound embedded compositor interface.");
- client->new_fake_object(new_local_id, &godot_embedding_compositor_interface, 1);
- return MessageStatus::HANDLED;
- }
- WaylandObject *instance = nullptr;
- client->registry_globals_instances[global_name].insert(new_local_id);
- ++global_info.instance_counter;
- if (!client->registry_globals_instances.has(global_name)) {
- client->registry_globals_instances[global_name] = {};
- }
- uint32_t bind_gid = wl_registry_bind(REGISTRY_ID, global_name, version);
- if (bind_gid == INVALID_ID) {
- socket_error(client->socket, local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "Bind failed.");
- return MessageStatus::HANDLED;
- }
- WaylandObject *bind_obj = get_object(bind_gid);
- if (bind_obj == nullptr) {
- socket_error(client->socket, local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "Bind failed.");
- return MessageStatus::HANDLED;
- }
- if (!bind_obj->shared) {
- client->bind_global_id(bind_gid, new_local_id);
- instance = bind_obj;
- } else {
- instance = client->new_global_instance(new_local_id, global_info.reusable_objects[version], global_info.interface, version);
- DEBUG_LOG_WAYLAND_EMBED(vformat("Instancing global #%d iface %s ver %d new id l0x%x g0x%x", global_name, global_info.interface->name, version, new_local_id, global_info.reusable_objects[version]));
- // Some interfaces report their state as soon as they're bound. Since
- // instances are handled by us, we need to track and report the relevant
- // data ourselves.
- if (global_info.interface == &wl_drm_interface) {
- Error err = client->send_wl_drm_state(new_local_id, (WaylandDrmGlobalData *)global_info.data);
- if (err != OK) {
- return MessageStatus::ERROR;
- }
- } else if (global_info.interface == &wl_shm_interface) {
- WaylandShmGlobalData *global_data = (WaylandShmGlobalData *)global_info.data;
- ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);
- for (uint32_t format : global_data->formats) {
- send_wayland_message(client->socket, new_local_id, WL_SHM_FORMAT, { format });
- }
- }
- }
- ERR_FAIL_NULL_V(instance, MessageStatus::UNHANDLED);
- if (global_info.interface == &wl_seat_interface) {
- WaylandSeatInstanceData *new_data = memnew(WaylandSeatInstanceData);
- instance->data = new_data;
- }
- return MessageStatus::HANDLED;
- }
- }
- if (object->interface == &wl_compositor_interface && p_opcode == WL_COMPOSITOR_CREATE_SURFACE) {
- uint32_t new_local_id = body[0];
- WaylandSurfaceData *data = memnew(WaylandSurfaceData);
- data->client = client;
- uint32_t new_global_id = client->new_object(new_local_id, &wl_surface_interface, object->version, data);
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- DEBUG_LOG_WAYLAND_EMBED(vformat("Keeping track of surface l0x%x g0x%x.", new_local_id, new_global_id));
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });
- return MessageStatus::HANDLED;
- }
- if (object->interface == &wl_surface_interface) {
- WaylandSurfaceData *surface_data = (WaylandSurfaceData *)object->data;
- ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);
- if (p_opcode == WL_SURFACE_DESTROY) {
- for (uint32_t wl_seat_name : wl_seat_names) {
- WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;
- ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);
- if (global_seat_data->pointed_surface_id == global_id) {
- global_seat_data->pointed_surface_id = INVALID_ID;
- }
- if (global_seat_data->focused_surface_id == global_id) {
- global_seat_data->focused_surface_id = INVALID_ID;
- }
- }
- } else if (p_opcode == WL_SURFACE_COMMIT) {
- if (surface_data->role_object_handle.is_valid()) {
- WaylandObject *role_object = surface_data->role_object_handle.get();
- if (role_object && role_object->interface) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("!!!!! Committed surface g0x%x with role object %s id l0x%x", global_id, role_object->interface->name, surface_data->role_object_handle.get_local_id()));
- }
- if (role_object && role_object->interface == &xdg_toplevel_interface) {
- XdgToplevelData *toplevel_data = (XdgToplevelData *)role_object->data;
- ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);
- // xdg shell spec requires clients to first send data and then commit the
- // surface.
- if (toplevel_data->is_embedded() && !toplevel_data->configured) {
- toplevel_data->configured = true;
- // xdg_surface::configure
- send_wayland_message(client->socket, toplevel_data->xdg_surface_handle.get_local_id(), 0, { serial_counter++ });
- }
- }
- }
- send_wayland_message(compositor_socket, global_id, p_opcode, {});
- return MessageStatus::HANDLED;
- }
- }
- if (object->interface == &wl_seat_interface) {
- uint32_t global_seat_name = registry_globals_names[global_id];
- RegistryGlobalInfo &seat_global_info = registry_globals[global_seat_name];
- WaylandSeatGlobalData *global_data = (WaylandSeatGlobalData *)seat_global_info.data;
- ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);
- WaylandSeatInstanceData *instance_data = (WaylandSeatInstanceData *)object->data;
- ERR_FAIL_NULL_V(instance_data, MessageStatus::ERROR);
- if (p_opcode == WL_SEAT_GET_POINTER) {
- ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::ERROR);
- // [Request] wl_seat::get_pointer(n);
- uint32_t new_local_id = body[0];
- WaylandPointerData *new_data = memnew(WaylandPointerData);
- new_data->wl_seat_id = global_id;
- uint32_t new_global_id = client->new_object(new_local_id, &wl_pointer_interface, object->version, new_data);
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- instance_data->wl_pointer_id = new_global_id;
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });
- return MessageStatus::HANDLED;
- }
- if (p_opcode == WL_SEAT_GET_KEYBOARD) {
- ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::ERROR);
- // [Request] wl_seat::get_pointer(n);
- uint32_t new_local_id = body[0];
- WaylandKeyboardData *new_data = memnew(WaylandKeyboardData);
- new_data->wl_seat_id = global_id;
- uint32_t new_global_id = client->new_object(new_local_id, &wl_keyboard_interface, object->version, new_data);
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- instance_data->wl_keyboard_id = new_global_id;
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });
- return MessageStatus::HANDLED;
- }
- }
- if (object->interface == &xdg_wm_base_interface) {
- if (p_opcode == XDG_WM_BASE_CREATE_POSITIONER) {
- uint32_t new_local_id = body[0];
- uint32_t new_global_id = client->new_object(new_local_id, &xdg_positioner_interface, object->version, memnew(XdgPositionerData));
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });
- return MessageStatus::HANDLED;
- }
- if (p_opcode == XDG_WM_BASE_GET_XDG_SURFACE) {
- // [Request] xdg_wm_base::get_xdg_surface(no).
- uint32_t new_local_id = body[0];
- uint32_t surface_id = body[1];
- uint32_t global_surface_id = client->get_global_id(surface_id);
- bool fake = (client != main_client);
- XdgSurfaceData *data = memnew(XdgSurfaceData);
- data->wl_surface_id = global_surface_id;
- if (fake) {
- client->new_fake_object(new_local_id, &xdg_surface_interface, object->version, data);
- DEBUG_LOG_WAYLAND_EMBED(vformat("Created fake xdg_surface l0x%x for surface l0x%x", new_local_id, surface_id));
- } else {
- uint32_t new_global_id = client->new_object(new_local_id, &xdg_surface_interface, object->version, data);
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- DEBUG_LOG_WAYLAND_EMBED(vformat("Created real xdg_surface l0x%x g0x%x for surface l0x%x", new_local_id, new_global_id, surface_id));
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_surface_id });
- }
- return MessageStatus::HANDLED;
- }
- }
- if (object->interface == &xdg_surface_interface) {
- XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)object->data;
- ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);
- WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(xdg_surf_data->wl_surface_id)->data;
- ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);
- bool is_embedded = client->fake_objects.has(local_id);
- if (p_opcode == XDG_SURFACE_GET_POPUP) {
- // [Request] xdg_surface::get_popup(no?o).
- uint32_t new_local_id = body[0];
- uint32_t local_parent_id = body[1];
- uint32_t local_positioner_id = body[2];
- surface_data->role_object_handle = LocalObjectHandle(client, new_local_id);
- XdgPopupData *popup_data = memnew(XdgPopupData);
- popup_data->parent_handle = LocalObjectHandle(client, local_parent_id);
- if (!is_embedded) {
- uint32_t new_global_id = client->new_object(new_local_id, &xdg_popup_interface, object->version, popup_data);
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- uint32_t global_parent_id = client->get_global_id(local_parent_id);
- uint32_t global_positioner_id = client->get_global_id(local_positioner_id);
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_parent_id, global_positioner_id });
- return MessageStatus::HANDLED;
- }
- {
- // Popups are real, time to actually instantiate an xdg_surface.
- WaylandObject copy = *object;
- client->fake_objects.erase(local_id);
- global_id = client->new_object(local_id, copy.interface, copy.version, copy.data);
- ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::HANDLED);
- object = get_object(global_id);
- // xdg_wm_base::get_xdg_surface(no);
- send_wayland_message(compositor_socket, xdg_wm_base_id, 2, { global_id, xdg_surf_data->wl_surface_id });
- }
- uint32_t new_global_id = client->new_object(new_local_id, &xdg_popup_interface, object->version, popup_data);
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- uint32_t global_parent_id = INVALID_ID;
- if (local_parent_id != INVALID_ID) {
- XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)client->get_object(local_parent_id)->data;
- ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);
- WaylandSurfaceData *parent_surface_data = (WaylandSurfaceData *)get_object(parent_xdg_surf_data->wl_surface_id)->data;
- ERR_FAIL_NULL_V(parent_surface_data, MessageStatus::ERROR);
- WaylandObject *parent_role_obj = parent_surface_data->role_object_handle.get();
- ERR_FAIL_NULL_V(parent_role_obj, MessageStatus::ERROR);
- XdgPositionerData *pos_data = (XdgPositionerData *)client->get_object(local_positioner_id)->data;
- ERR_FAIL_NULL_V(pos_data, MessageStatus::ERROR);
- if (parent_role_obj->interface == &xdg_toplevel_interface) {
- XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)parent_role_obj->data;
- ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);
- if (parent_toplevel_data->is_embedded()) {
- // Embedded windows are subsurfaces of a parent window. We need to
- // "redirect" the popup request on the parent window and adjust the
- // positioner properly if needed.
- XdgToplevelData *main_parent_toplevel_data = (XdgToplevelData *)parent_toplevel_data->parent_handle.get()->data;
- ERR_FAIL_NULL_V(main_parent_toplevel_data, MessageStatus::ERROR);
- global_parent_id = main_parent_toplevel_data->xdg_surface_handle.get_global_id();
- WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(parent_toplevel_data->wl_subsurface_id)->data;
- ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);
- Point2i adj_pos = subsurf_data->position + pos_data->anchor_rect.position;
- // xdg_positioner::set_anchor_rect
- send_wayland_message(compositor_socket, client->get_global_id(local_positioner_id), 2, { (uint32_t)adj_pos.x, (uint32_t)adj_pos.y, (uint32_t)pos_data->anchor_rect.size.width, (uint32_t)pos_data->anchor_rect.size.height });
- }
- } else {
- global_parent_id = client->get_global_id(local_parent_id);
- }
- }
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_parent_id, client->get_global_id(local_positioner_id) });
- return MessageStatus::HANDLED;
- }
- if (p_opcode == XDG_SURFACE_GET_TOPLEVEL) {
- // [Request] xdg_surface::get_toplevel(n).
- uint32_t new_local_id = body[0];
- surface_data->role_object_handle = LocalObjectHandle(client, new_local_id);
- XdgToplevelData *data = memnew(XdgToplevelData);
- data->xdg_surface_handle = LocalObjectHandle(client, local_id);
- if (is_embedded) {
- client->new_fake_object(new_local_id, &xdg_toplevel_interface, object->version, data);
- client->embedded_window_id = new_local_id;
- // godot_embedded_client::window_embedded()
- send_wayland_message(main_client->socket, client->embedded_client_id, 1, {});
- } else {
- uint32_t new_global_id = client->new_object(new_local_id, &xdg_toplevel_interface, object->version, data);
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- if (main_toplevel_id == 0) {
- main_toplevel_id = new_global_id;
- DEBUG_LOG_WAYLAND_EMBED(vformat("main toplevel set to gx0%x.", main_toplevel_id));
- }
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });
- }
- return MessageStatus::HANDLED;
- }
- }
- if (object->interface == &xdg_positioner_interface) {
- XdgPositionerData *pos_data = (XdgPositionerData *)object->data;
- ERR_FAIL_NULL_V(pos_data, MessageStatus::ERROR);
- if (p_opcode == XDG_POSITIONER_SET_ANCHOR_RECT) {
- // Args: int x, int y, int width, int height.
- pos_data->anchor_rect = Rect2i(body[0], body[1], body[2], body[3]);
- send_wayland_message(compositor_socket, global_id, p_opcode, { body[0], body[1], body[2], body[3] });
- return MessageStatus::HANDLED;
- }
- }
- if (object->interface == &xdg_toplevel_interface && p_opcode == XDG_TOPLEVEL_DESTROY) {
- if (client->fake_objects.has(local_id)) {
- XdgToplevelData *data = (XdgToplevelData *)object->data;
- ERR_FAIL_NULL_V(data, MessageStatus::ERROR);
- XdgSurfaceData *xdg_surf_data = nullptr;
- if (data->xdg_surface_handle.is_valid()) {
- xdg_surf_data = (XdgSurfaceData *)data->xdg_surface_handle.get()->data;
- ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);
- }
- ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);
- XdgSurfaceData *parent_xdg_surf_data = nullptr;
- {
- XdgToplevelData *parent_data = nullptr;
- if (data->parent_handle.get()) {
- parent_data = (XdgToplevelData *)data->parent_handle.get()->data;
- ERR_FAIL_NULL_V(parent_data, MessageStatus::ERROR);
- }
- if (parent_data && parent_data->xdg_surface_handle.get()) {
- parent_xdg_surf_data = (XdgSurfaceData *)parent_data->xdg_surface_handle.get()->data;
- ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);
- }
- }
- for (uint32_t wl_seat_name : wl_seat_names) {
- WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;
- ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);
- if (global_seat_data->focused_surface_id == xdg_surf_data->wl_surface_id) {
- if (xdg_surf_data) {
- seat_name_leave_surface(wl_seat_name, xdg_surf_data->wl_surface_id);
- }
- if (parent_xdg_surf_data) {
- seat_name_enter_surface(wl_seat_name, parent_xdg_surf_data->wl_surface_id);
- }
- }
- }
- // wl_display::delete_id
- send_wayland_message(client->socket, local_id, p_opcode, {});
- if (local_id == client->embedded_window_id) {
- client->embedded_window_id = 0;
- }
- if (data->wl_subsurface_id != INVALID_ID) {
- send_wayland_message(compositor_socket, data->wl_subsurface_id, WL_SUBSURFACE_DESTROY, {});
- }
- client->delete_object(local_id);
- return MessageStatus::HANDLED;
- }
- }
- if (interface == &zwp_pointer_constraints_v1_interface) {
- // FIXME: This implementation leaves no way of unlocking the pointer when
- // embedded into the main window. We might need to be a bit more invasive.
- if (p_opcode == ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER) {
- // [Request] zwp_pointer_constraints_v1::lock_pointer(nooou).
- uint32_t new_local_id = body[0];
- uint32_t local_surface_id = body[1];
- uint32_t local_pointer_id = body[2];
- uint32_t lifetime = body[4];
- WaylandSurfaceData *surf_data = (WaylandSurfaceData *)client->get_object(local_surface_id)->data;
- ERR_FAIL_NULL_V(surf_data, MessageStatus::ERROR);
- WaylandObject *role_obj = surf_data->role_object_handle.get();
- ERR_FAIL_NULL_V(role_obj, MessageStatus::ERROR);
- if (role_obj->interface == &xdg_toplevel_interface) {
- XdgToplevelData *toplevel_data = (XdgToplevelData *)role_obj->data;
- ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);
- if (!toplevel_data->is_embedded()) {
- // Passthrough.
- return MessageStatus::UNHANDLED;
- }
- // Subsurfaces don't normally work, at least on sway, as the locking
- // condition might rely on focus, which they don't get. We can remap them to
- // the parent surface and set a region though.
- XdgToplevelData *parent_data = (XdgToplevelData *)toplevel_data->parent_handle.get()->data;
- ERR_FAIL_NULL_V(parent_data, MessageStatus::ERROR);
- XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)parent_data->xdg_surface_handle.get()->data;
- ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);
- WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(toplevel_data->wl_subsurface_id)->data;
- ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);
- uint32_t new_global_id = client->new_object(new_local_id, &zwp_locked_pointer_v1_interface, object->version);
- ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);
- uint32_t x = subsurf_data->position.x;
- uint32_t y = subsurf_data->position.y;
- uint32_t width = toplevel_data->size.width;
- uint32_t height = toplevel_data->size.height;
- // NOTE: At least on sway I can't seem to be able to get this region
- // working but the calls check out.
- DEBUG_LOG_WAYLAND_EMBED(vformat("Creating custom region x%d y%d w%d h%d", x, y, width, height));
- uint32_t new_region_id = allocate_global_id();
- get_object(new_region_id)->interface = &wl_region_interface;
- get_object(new_region_id)->version = get_object(wl_compositor_id)->version;
- // wl_compostor::create_region(n).
- send_wayland_message(compositor_socket, wl_compositor_id, 1, { new_region_id });
- // wl_region::add(iiii).
- send_wayland_message(compositor_socket, new_region_id, 1, { x, y, width, height });
- send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, parent_xdg_surf_data->wl_surface_id, client->get_global_id(local_pointer_id), new_region_id, lifetime });
- // wl_region::destroy().
- send_wayland_message(compositor_socket, new_region_id, 0, {});
- return MessageStatus::HANDLED;
- }
- }
- }
- if (interface == &godot_embedded_client_interface) {
- EmbeddedClientData *eclient_data = (EmbeddedClientData *)object->data;
- ERR_FAIL_NULL_V(eclient_data, MessageStatus::ERROR);
- Client *eclient = eclient_data->client;
- ERR_FAIL_NULL_V(eclient, MessageStatus::ERROR);
- if (p_opcode == GODOT_EMBEDDED_CLIENT_DESTROY) {
- if (!eclient_data->disconnected) {
- close(eclient->socket);
- }
- client->delete_object(local_id);
- return MessageStatus::HANDLED;
- }
- if (eclient_data->disconnected) {
- // Object is inert.
- return MessageStatus::HANDLED;
- }
- ERR_FAIL_COND_V(eclient->embedded_window_id == 0, MessageStatus::ERROR);
- XdgToplevelData *toplevel_data = (XdgToplevelData *)eclient->get_object(eclient->embedded_window_id)->data;
- ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);
- if (p_opcode == GODOT_EMBEDDED_CLIENT_SET_EMBEDDED_WINDOW_RECT && toplevel_data->wl_subsurface_id != INVALID_ID) {
- uint32_t x = body[0];
- uint32_t y = body[1];
- uint32_t width = body[2];
- uint32_t height = body[3];
- WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(toplevel_data->wl_subsurface_id)->data;
- ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);
- toplevel_data->size.width = width;
- toplevel_data->size.height = height;
- subsurf_data->position.x = x;
- subsurf_data->position.y = y;
- // wl_subsurface::set_position
- send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 1, { x, y });
- // xdg_toplevel::configure
- send_wayland_message(eclient->socket, eclient->embedded_window_id, 0, { width, height, 0 });
- // xdg_surface::configure
- send_wayland_message(eclient->socket, toplevel_data->xdg_surface_handle.get_local_id(), 0, { configure_serial_counter++ });
- return MessageStatus::HANDLED;
- } else if (p_opcode == GODOT_EMBEDDED_CLIENT_SET_EMBEDDED_WINDOW_PARENT) {
- uint32_t main_client_parent_id = body[0];
- if (toplevel_data->parent_handle.get_local_id() == main_client_parent_id) {
- return MessageStatus::HANDLED;
- }
- if (main_client_parent_id == INVALID_ID && toplevel_data->wl_subsurface_id != INVALID_ID) {
- // Window hiding logic.
- // wl_subsurface::destroy()
- send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 0, {});
- toplevel_data->parent_handle.invalidate();
- toplevel_data->wl_subsurface_id = INVALID_ID;
- return MessageStatus::HANDLED;
- }
- XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)client->get_object(main_client_parent_id)->data;
- ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);
- XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)parent_toplevel_data->xdg_surface_handle.get()->data;
- ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);
- XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)toplevel_data->xdg_surface_handle.get()->data;
- ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);
- if (toplevel_data->wl_subsurface_id != INVALID_ID) {
- // wl_subsurface::destroy()
- send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 0, {});
- }
- uint32_t new_sub_id = allocate_global_id();
- WaylandObject *new_sub_object = get_object(new_sub_id);
- new_sub_object->interface = &wl_subsurface_interface;
- new_sub_object->data = memnew(WaylandSubsurfaceData);
- new_sub_object->version = get_object(wl_subcompositor_id)->version;
- toplevel_data->wl_subsurface_id = new_sub_id;
- toplevel_data->parent_handle = LocalObjectHandle(main_client, main_client_parent_id);
- DEBUG_LOG_WAYLAND_EMBED(vformat("Binding subsurface g0x%x.", new_sub_id));
- // wl_subcompositor::get_subsurface
- send_wayland_message(compositor_socket, wl_subcompositor_id, 1, { new_sub_id, xdg_surf_data->wl_surface_id, parent_xdg_surf_data->wl_surface_id });
- // wl_subsurface::set_desync
- send_wayland_message(compositor_socket, new_sub_id, 5, {});
- return MessageStatus::HANDLED;
- } else if (p_opcode == GODOT_EMBEDDED_CLIENT_FOCUS_WINDOW) {
- XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)toplevel_data->xdg_surface_handle.get()->data;
- ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);
- for (uint32_t wl_seat_name : wl_seat_names) {
- RegistryGlobalInfo &global_seat_info = registry_globals[wl_seat_name];
- WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;
- if (global_seat_data->focused_surface_id != INVALID_ID) {
- seat_name_leave_surface(wl_seat_name, global_seat_data->focused_surface_id);
- }
- global_seat_data->focused_surface_id = xdg_surf_data->wl_surface_id;
- seat_name_enter_surface(wl_seat_name, xdg_surf_data->wl_surface_id);
- }
- } else if (p_opcode == GODOT_EMBEDDED_CLIENT_EMBEDDED_WINDOW_REQUEST_CLOSE) {
- // xdg_toplevel::close
- send_wayland_message(eclient->socket, eclient->embedded_window_id, 1, {});
- return MessageStatus::HANDLED;
- }
- }
- // Server-allocated objects are a bit annoying to handle for us. Right now we
- // use a heuristic. See: https://ppaalanen.blogspot.com/2014/07/wayland-protocol-design-object-lifespan.html
- if (strcmp(message.name, "destroy") == 0 || strcmp(message.name, "release") == 0) {
- if (object->shared) {
- // We must not delete shared objects.
- client->delete_object(local_id);
- return MessageStatus::HANDLED;
- }
- if (global_id != INVALID_ID) {
- send_wayland_message(compositor_socket, global_id, p_opcode, {});
- object->destroyed = true;
- }
- if (local_id & 0xff000000) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("!!!!!! Deallocating server object l0x%x", local_id));
- client->delete_object(local_id);
- }
- return MessageStatus::HANDLED;
- }
- if (client->fake_objects.has(local_id)) {
- // Object is fake, we're done.
- DEBUG_LOG_WAYLAND_EMBED("Dropping unhandled request for fake object.");
- return MessageStatus::HANDLED;
- }
- if (global_id == INVALID_ID) {
- DEBUG_LOG_WAYLAND_EMBED("Dropping request with invalid global object id");
- return MessageStatus::HANDLED;
- }
- return MessageStatus::UNHANDLED;
- }
- WaylandEmbedder::MessageStatus WaylandEmbedder::handle_event(uint32_t p_global_id, LocalObjectHandle p_local_handle, uint32_t p_opcode, const uint32_t *msg_data, size_t msg_len) {
- WaylandObject *global_object = get_object(p_global_id);
- ERR_FAIL_NULL_V_MSG(global_object, MessageStatus::ERROR, "Compositor messages must always have a global object.");
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- ERR_FAIL_NULL_V(global_object->interface, MessageStatus::ERROR);
- const struct wl_interface *interface = global_object->interface;
- ERR_FAIL_COND_V((int)p_opcode >= interface->event_count, MessageStatus::ERROR);
- const struct wl_message message = interface->events[p_opcode];
- if (p_local_handle.is_valid()) {
- int socket = p_local_handle.get_client()->socket;
- DEBUG_LOG_WAYLAND_EMBED(vformat("Client #%d <- %s::%s(%s) g0x%x", socket, interface->name, message.name, message.signature, p_global_id));
- } else {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Client N/A <- %s::%s(%s) g0x%x", interface->name, message.name, message.signature, p_global_id));
- }
- #endif //WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- const uint32_t *body = msg_data + 2;
- //size_t body_len = msg_len - (WL_WORD_SIZE * 2);
- // FIXME: Make sure that it makes sense to track this protocol. Not only is it
- // old and getting deprecated, but I can't even get this code branch to hit
- // probably because, at the time of writing, we only get the "main" display
- // through the proxy.
- if (global_object->interface == &wl_drm_interface) {
- // wl_drm can't ever be destroyed, so we need to track its state as it's going
- // to be instanced at least few times.
- uint32_t global_name = registry_globals_names[p_global_id];
- WaylandDrmGlobalData *global_data = (WaylandDrmGlobalData *)registry_globals[global_name].data;
- ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);
- if (p_opcode == WL_DRM_DEVICE) {
- // signature: s
- uint32_t name_len = body[0];
- uint8_t *name = (uint8_t *)(body + 1);
- global_data->device = String::utf8((const char *)name, name_len);
- return MessageStatus::UNHANDLED;
- }
- if (p_opcode == WL_DRM_FORMAT) {
- // signature: u
- uint32_t format = body[0];
- global_data->formats.push_back(format);
- return MessageStatus::UNHANDLED;
- }
- if (p_opcode == WL_DRM_AUTHENTICATED) {
- // signature: N/A
- global_data->authenticated = true;
- return MessageStatus::UNHANDLED;
- }
- if (p_opcode == WL_DRM_CAPABILITIES) {
- // signature: u
- uint32_t capabilities = body[0];
- global_data->capabilities = capabilities;
- }
- return MessageStatus::UNHANDLED;
- }
- if (global_object->interface == &wl_shm_interface) {
- uint32_t global_name = registry_globals_names[p_global_id];
- WaylandShmGlobalData *global_data = (WaylandShmGlobalData *)registry_globals[global_name].data;
- ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);
- if (p_opcode == WL_SHM_FORMAT) {
- // Signature: u
- uint32_t format = body[0];
- global_data->formats.push_back(format);
- }
- }
- if (!p_local_handle.is_valid()) {
- // Some requests might not have a valid local object handle for various
- // reasons, such as when certain events are directed to this proxy or when the
- // destination client of a message disconnected in the meantime.
- if (global_object->interface == &wl_display_interface) {
- if (p_opcode == WL_DISPLAY_DELETE_ID) {
- // [Event] wl_display::delete_id(u)
- uint32_t global_delete_id = body[0];
- DEBUG_LOG_WAYLAND_EMBED(vformat("Compositor requested deletion of g0x%x (no client)", global_delete_id));
- delete_object(global_delete_id);
- return MessageStatus::HANDLED;
- } else if (p_opcode == WL_DISPLAY_ERROR) {
- // [Event] wl_display::error(ous)
- uint32_t obj_id = body[0];
- uint32_t err_code = body[1];
- CRASH_NOW_MSG(vformat("Error obj g0x%x code %d: %s", obj_id, err_code, (const char *)(body + 3)));
- }
- }
- if (global_object->interface == &wl_callback_interface && p_opcode == WL_CALLBACK_DONE) {
- if (sync_callback_id != INVALID_ID && p_global_id == sync_callback_id) {
- sync_callback_id = 0;
- DEBUG_LOG_WAYLAND_EMBED("Sync response received");
- return MessageStatus::HANDLED;
- }
- }
- if (global_object->interface == &wl_registry_interface) {
- if (p_opcode == WL_REGISTRY_GLOBAL) {
- // [Event] wl_registry::global(usu).
- uint32_t global_name = body[0];
- uint32_t interface_name_len = body[1];
- const char *interface_name = (const char *)(body + 2);
- uint32_t global_version = body[2 + wl_array_word_offset(interface_name_len)];
- DEBUG_LOG_WAYLAND_EMBED("Global c#%d %s %d", global_name, interface_name, global_version);
- const struct wl_interface *global_interface = wl_interface_from_string(interface_name, interface_name_len);
- if (global_interface) {
- RegistryGlobalInfo global_info = {};
- global_info.interface = global_interface;
- global_info.version = MIN(global_version, (uint32_t)global_interface->version);
- DEBUG_LOG_WAYLAND_EMBED("Clamped global %s to version %d.", interface_name, global_info.version);
- global_info.compositor_name = global_name;
- int new_global_name = registry_globals_counter++;
- if (global_info.interface == &wl_shm_interface) {
- DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_shm data.");
- global_info.data = memnew(WaylandShmGlobalData);
- }
- if (global_info.interface == &wl_seat_interface) {
- DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_seat data.");
- global_info.data = memnew(WaylandSeatGlobalData);
- wl_seat_names.push_back(new_global_name);
- }
- if (global_info.interface == &wl_drm_interface) {
- DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_drm data.");
- global_info.data = memnew(WaylandDrmGlobalData);
- }
- registry_globals[new_global_name] = global_info;
- // We need some interfaces directly. It's better to bind a "copy" ourselves
- // than to wait for the client to ask one.
- if (global_interface == &xdg_wm_base_interface && xdg_wm_base_id == 0) {
- xdg_wm_base_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);
- ERR_FAIL_COND_V(xdg_wm_base_id == INVALID_ID, MessageStatus::ERROR);
- } else if (global_interface == &wl_compositor_interface && wl_compositor_id == 0) {
- wl_compositor_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);
- ERR_FAIL_COND_V(wl_compositor_id == INVALID_ID, MessageStatus::ERROR);
- } else if (global_interface == &wl_subcompositor_interface && wl_subcompositor_id == 0) {
- wl_subcompositor_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);
- ERR_FAIL_COND_V(wl_subcompositor_id == INVALID_ID, MessageStatus::ERROR);
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("Local registry object name: l#%d", new_global_name));
- if (clients.is_empty()) {
- // Let's not waste time.
- return MessageStatus::HANDLED;
- }
- // Notify all clients.
- LocalVector<wl_argument> args;
- args.push_back(wl_arg_uint(new_global_name));
- args.push_back(wl_arg_string(interface_name));
- args.push_back(wl_arg_uint(global_info.version));
- for (KeyValue<int, Client> &pair : clients) {
- Client &client = pair.value;
- for (uint32_t local_registry_id : client.wl_registry_instances) {
- send_wayland_event(client.socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL, args);
- }
- }
- return MessageStatus::HANDLED;
- } else {
- DEBUG_LOG_WAYLAND_EMBED("Skipping unknown global %s version %d.", interface_name, global_version);
- return MessageStatus::HANDLED;
- }
- } else if (p_opcode == WL_REGISTRY_GLOBAL_REMOVE) {
- uint32_t compositor_name = body[0];
- uint32_t local_name = 0;
- RegistryGlobalInfo *global_info = nullptr;
- // FIXME: Use a map or something.
- for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {
- uint32_t name = pair.key;
- RegistryGlobalInfo &info = pair.value;
- if (info.compositor_name == compositor_name) {
- local_name = name;
- global_info = &info;
- break;
- }
- }
- ERR_FAIL_NULL_V(global_info, MessageStatus::ERROR);
- if (global_info->instance_counter == 0) {
- memdelete(global_info->data);
- registry_globals.erase(local_name);
- } else {
- global_info->destroyed = true;
- }
- // Notify all clients.
- LocalVector<wl_argument> args;
- args.push_back(wl_arg_uint(local_name));
- for (KeyValue<int, Client> &pair : clients) {
- Client &client = pair.value;
- for (uint32_t local_registry_id : client.wl_registry_instances) {
- send_wayland_event(client.socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL_REMOVE, args);
- }
- }
- return MessageStatus::HANDLED;
- }
- }
- DEBUG_LOG_WAYLAND_EMBED("No valid local object handle, falling back to generic handler.");
- return MessageStatus::UNHANDLED;
- }
- Client *client = p_local_handle.get_client();
- ERR_FAIL_NULL_V(client, MessageStatus::ERROR);
- WaylandObject *object = p_local_handle.get();
- uint32_t local_id = p_local_handle.get_local_id();
- if (global_object->interface == &wl_display_interface) {
- if (p_opcode == WL_DISPLAY_DELETE_ID) {
- // [Event] wl_display::delete_id(u)
- uint32_t global_delete_id = body[0];
- uint32_t local_delete_id = client->get_local_id(global_delete_id);
- DEBUG_LOG_WAYLAND_EMBED(vformat("Compositor requested delete of g0x%x l0x%x", global_delete_id, local_delete_id));
- if (local_delete_id == INVALID_ID) {
- // No idea what this object is, might be of the other client. This
- // definitely does not make sense to us, so we're done.
- return MessageStatus::INVALID;
- }
- client->delete_object(local_delete_id);
- send_wayland_message(client->socket, DISPLAY_ID, WL_DISPLAY_DELETE_ID, { local_delete_id });
- return MessageStatus::HANDLED;
- }
- return MessageStatus::UNHANDLED;
- }
- if (object->interface == &wl_keyboard_interface) {
- WaylandKeyboardData *data = (WaylandKeyboardData *)object->data;
- ERR_FAIL_NULL_V(data, MessageStatus::ERROR);
- uint32_t global_seat_name = registry_globals_names[data->wl_seat_id];
- RegistryGlobalInfo &global_seat_info = registry_globals[global_seat_name];
- WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;
- ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);
- if (p_opcode == WL_KEYBOARD_ENTER) {
- // [Event] wl_keyboard::enter(uoa)
- uint32_t surface = body[1];
- if (global_seat_data->focused_surface_id != surface) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Focused g0x%x", surface));
- global_seat_data->focused_surface_id = surface;
- }
- } else if (p_opcode == WL_KEYBOARD_LEAVE) {
- // [Event] wl_keyboard::leave(uo)
- uint32_t surface = body[1];
- if (global_seat_data->focused_surface_id == surface) {
- global_seat_data->focused_surface_id = INVALID_ID;
- }
- } else if (p_opcode == WL_KEYBOARD_KEY) {
- // NOTE: modifiers event can be sent even without focus, according to the
- // spec, so there's no need to skip it.
- if (global_seat_data->focused_surface_id != INVALID_ID && !client->local_ids.has(global_seat_data->focused_surface_id)) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Skipped wl_keyboard event due to unfocused surface 0x%x", global_seat_data->focused_surface_id));
- return MessageStatus::HANDLED;
- }
- }
- return MessageStatus::UNHANDLED;
- }
- if (object->interface == &wl_pointer_interface) {
- WaylandPointerData *data = (WaylandPointerData *)object->data;
- ERR_FAIL_NULL_V(data, MessageStatus::ERROR);
- uint32_t global_seat_name = registry_globals_names[data->wl_seat_id];
- RegistryGlobalInfo &global_seat_info = registry_globals[global_seat_name];
- WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;
- ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);
- WaylandSeatInstanceData *seat_data = (WaylandSeatInstanceData *)object->data;
- ERR_FAIL_NULL_V(seat_data, MessageStatus::ERROR);
- if (p_opcode == WL_POINTER_BUTTON && global_seat_data->pointed_surface_id != INVALID_ID) {
- // [Event] wl_pointer::button(uuuu);
- uint32_t button = body[2];
- uint32_t state = body[3];
- DEBUG_LOG_WAYLAND_EMBED(vformat("Button %d state %d on surface g0x%x (focused g0x%x)", button, state, global_seat_data->pointed_surface_id, global_seat_data->focused_surface_id));
- bool client_pointed = client->local_ids.has(global_seat_data->pointed_surface_id);
- if (button != BTN_LEFT || state != WL_POINTER_BUTTON_STATE_RELEASED) {
- return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;
- }
- if (global_seat_data->focused_surface_id == global_seat_data->pointed_surface_id) {
- return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;
- }
- if (!global_surface_is_window(global_seat_data->pointed_surface_id)) {
- return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;
- }
- if (global_seat_data->focused_surface_id != INVALID_ID) {
- seat_name_leave_surface(global_seat_name, global_seat_data->focused_surface_id);
- }
- global_seat_data->focused_surface_id = global_seat_data->pointed_surface_id;
- seat_name_enter_surface(global_seat_name, global_seat_data->focused_surface_id);
- } else if (p_opcode == WL_POINTER_ENTER) {
- // [Event] wl_pointer::enter(uoff).
- uint32_t surface = body[1];
- WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(surface)->data;
- ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);
- if (global_seat_data->pointed_surface_id != surface) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Pointer (g0x%x seat g0x%x): pointed surface old g0x%x new g0x%x", p_global_id, data->wl_seat_id, global_seat_data->pointed_surface_id, surface));
- global_seat_data->pointed_surface_id = surface;
- }
- } else if (p_opcode == WL_POINTER_LEAVE) {
- // [Event] wl_pointer::leave(uo).
- uint32_t surface = body[1];
- WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(surface)->data;
- ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);
- if (global_seat_data->pointed_surface_id == surface) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Pointer (g0x%x seat g0x%x): g0x%x -> g0x%x", p_global_id, data->wl_seat_id, global_seat_data->pointed_surface_id, INVALID_ID));
- global_seat_data->pointed_surface_id = INVALID_ID;
- }
- }
- return MessageStatus::UNHANDLED;
- }
- if (object->interface == &xdg_popup_interface) {
- if (p_opcode == XDG_POPUP_CONFIGURE) {
- // [Event] xdg_popup::configure(iiii);
- int32_t x = body[0];
- int32_t y = body[1];
- int32_t width = body[2];
- int32_t height = body[3];
- XdgPopupData *data = (XdgPopupData *)object->data;
- ERR_FAIL_NULL_V(data, MessageStatus::ERROR);
- XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)data->parent_handle.get()->data;
- ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);
- WaylandSurfaceData *parent_surface_data = (WaylandSurfaceData *)get_object(parent_xdg_surf_data->wl_surface_id)->data;
- ERR_FAIL_NULL_V(parent_surface_data, MessageStatus::ERROR);
- WaylandObject *parent_role_obj = parent_surface_data->role_object_handle.get();
- ERR_FAIL_NULL_V(parent_role_obj, MessageStatus::ERROR);
- if (parent_role_obj->interface == &xdg_toplevel_interface) {
- XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)parent_role_obj->data;
- ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);
- if (parent_toplevel_data->is_embedded()) {
- WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(parent_toplevel_data->wl_subsurface_id)->data;
- ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);
- // The coordinates passed will be shifted by the embedded window position,
- // so we need to fix them back.
- Point2i fixed_position = Point2i(x, y) - subsurf_data->position;
- DEBUG_LOG_WAYLAND_EMBED(vformat("Correcting popup configure position to %s", fixed_position));
- send_wayland_message(client->socket, local_id, p_opcode, { (uint32_t)fixed_position.x, (uint32_t)fixed_position.y, (uint32_t)width, (uint32_t)height });
- return MessageStatus::HANDLED;
- }
- }
- }
- }
- return MessageStatus::UNHANDLED;
- }
- void WaylandEmbedder::shutdown() {
- thread_done.set();
- {
- // First making a list of all clients so that we can iteratively delete them.
- LocalVector<int> sockets;
- for (KeyValue<int, Client> &pair : clients) {
- sockets.push_back(pair.key);
- }
- for (int socket : sockets) {
- cleanup_socket(socket);
- }
- }
- close(compositor_socket);
- compositor_socket = -1;
- for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {
- RegistryGlobalInfo &info = pair.value;
- if (info.data) {
- memdelete(info.data);
- info.data = nullptr;
- }
- }
- }
- Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *info, uint32_t *buf, int *fds_requested) {
- ERR_FAIL_NULL_V(info, ERR_BUG);
- ERR_FAIL_NULL_V(fds_requested, ERR_BUG);
- ERR_FAIL_NULL_V_MSG(info->direction == ProxyDirection::COMPOSITOR && client, ERR_BUG, "Wait, where did this message come from?");
- *fds_requested = 0;
- WaylandObject *object = nullptr;
- uint32_t global_id = INVALID_ID;
- if (info->direction == ProxyDirection::CLIENT) {
- global_id = info->raw_id;
- } else if (info->direction == ProxyDirection::COMPOSITOR) {
- global_id = client->get_global_id(info->raw_id);
- }
- if (global_id != INVALID_ID) {
- object = get_object(global_id);
- } else if (client) {
- object = client->get_object(info->raw_id);
- }
- if (object == nullptr) {
- if (info->direction == ProxyDirection::COMPOSITOR) {
- uint32_t local_id = info->raw_id;
- ERR_PRINT(vformat("Couldn't find requested object l0x%x for client %d, disconnecting.", local_id, client->socket));
- socket_error(client->socket, local_id, WL_DISPLAY_ERROR_INVALID_OBJECT, vformat("Object l0x%x not found.", local_id));
- return OK;
- } else {
- CRASH_NOW_MSG(vformat("No object found for r0x%x", info->raw_id));
- }
- }
- const struct wl_interface *interface = nullptr;
- interface = object->interface;
- if (interface == nullptr && info->raw_id & 0xff000000) {
- // Regular clients have no confirmation about deleted server objects (why
- // should they?) but since we share connections there's the risk of receiving
- // messages about deleted server objects. The simplest solution is to ignore
- // unknown server-side objects. Not the safest thing, I know, but it should do
- // the job.
- DEBUG_LOG_WAYLAND_EMBED(vformat("Ignoring unknown server-side object r0x%x", info->raw_id));
- return OK;
- }
- ERR_FAIL_NULL_V_MSG(interface, ERR_BUG, vformat("Object r0x%x has no interface", info->raw_id));
- const struct wl_message *message = nullptr;
- if (info->direction == ProxyDirection::CLIENT) {
- ERR_FAIL_COND_V(info->opcode >= interface->event_count, ERR_BUG);
- message = &interface->events[info->opcode];
- } else {
- ERR_FAIL_COND_V(info->opcode >= interface->method_count, ERR_BUG);
- message = &interface->methods[info->opcode];
- }
- ERR_FAIL_NULL_V(message, ERR_BUG);
- *fds_requested = String(message->signature).count("h");
- LocalVector<int> sent_fds;
- if (*fds_requested > 0) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Requested %d FDs.", *fds_requested));
- List<int> &fd_queue = info->direction == ProxyDirection::COMPOSITOR ? client->fds : compositor_fds;
- for (int i = 0; i < *fds_requested; ++i) {
- ERR_FAIL_COND_V_MSG(fd_queue.is_empty(), ERR_BUG, "Out of FDs.");
- DEBUG_LOG_WAYLAND_EMBED(vformat("Fetching FD %d.", fd_queue.front()->get()));
- sent_fds.push_back(fd_queue.front()->get());
- fd_queue.pop_front();
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("Remaining FDs: %d.", fd_queue.size()));
- }
- if (object->destroyed) {
- DEBUG_LOG_WAYLAND_EMBED("Ignoring message for inert object.");
- // Inert object.
- return OK;
- }
- if (info->direction == ProxyDirection::COMPOSITOR) {
- MessageStatus request_status = handle_request(LocalObjectHandle(client, info->raw_id), info->opcode, buf, info->size);
- if (request_status == MessageStatus::ERROR) {
- return ERR_BUG;
- }
- if (request_status == MessageStatus::HANDLED) {
- DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");
- return OK;
- }
- if (global_id != INVALID_ID) {
- buf[0] = global_id;
- }
- DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");
- if (handle_generic_msg(client, object, message, info, buf)) {
- send_raw_message(compositor_socket, { { buf, info->size } }, sent_fds);
- }
- } else {
- uint32_t global_name = 0;
- bool is_global = false;
- if (registry_globals_names.has(global_id)) {
- global_name = registry_globals_names[global_id];
- is_global = true;
- }
- // FIXME: For compatibility, mirror events with instanced registry globals as
- // object arguments. For example, `wl_surface.enter` returns a `wl_output`. If
- // said `wl_output` has been instanced multiple times, we need to resend the
- // same event with each instance as the argument, or the client might miss the
- // event by looking for the "wrong" instance.
- //
- // Note that this missing behavior is exclusively a compatibility mechanism
- // for old compositors which only implement undestroyable globals. We
- // otherwise passthrough every bind request and then the compositor takes care
- // of everything.
- // See: https://lore.freedesktop.org/wayland-devel/[email protected]/
- if (object->shared) {
- bool handled = false;
- for (KeyValue<int, Client> &pair : clients) {
- Client &c = pair.value;
- if (c.socket < 0) {
- continue;
- }
- if (!c.local_ids.has(global_id)) {
- DEBUG_LOG_WAYLAND_EMBED("!!!!!!!!!!! Instance missing?");
- continue;
- }
- if (is_global) {
- if (!c.registry_globals_instances.has(global_name)) {
- continue;
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("Broadcasting to all global instances for client %d (socket %d)", c.pid, c.socket));
- for (uint32_t instance_id : c.registry_globals_instances[global_name]) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Global instance l0x%x", instance_id));
- LocalObjectHandle local_obj = LocalObjectHandle(&c, instance_id);
- if (!local_obj.is_valid()) {
- continue;
- }
- MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);
- if (event_status == MessageStatus::ERROR) {
- return ERR_BUG;
- }
- if (event_status == MessageStatus::HANDLED) {
- DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");
- handled = true;
- continue;
- }
- if (event_status == MessageStatus::INVALID) {
- continue;
- }
- DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");
- buf[0] = instance_id;
- if (handle_generic_msg(&c, local_obj.get(), message, info, buf, instance_id)) {
- send_raw_message(c.socket, { { buf, info->size } }, sent_fds);
- }
- handled = true;
- }
- } else if (interface == &wl_display_interface) {
- // NOTE: The only shared non-global objects are `wl_display` and
- // `wl_registry`, both of which require custom handlers. Additionally, of
- // those only `wl_display` has client-specific handlers, which is what this
- // branch manages.
- LocalObjectHandle local_obj = LocalObjectHandle(&c, c.get_local_id(global_id));
- if (!local_obj.is_valid()) {
- continue;
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("Shared non-global l0x%x g0x%x", c.get_local_id(global_id), global_id));
- MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);
- if (event_status == MessageStatus::ERROR) {
- return ERR_BUG;
- }
- if (event_status == MessageStatus::HANDLED) {
- DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");
- handled = true;
- continue;
- }
- if (event_status == MessageStatus::INVALID) {
- continue;
- }
- DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");
- if (handle_generic_msg(&c, local_obj.get(), message, info, buf)) {
- send_raw_message(c.socket, { { buf, info->size } }, sent_fds);
- }
- handled = true;
- }
- }
- if (!handled) {
- // No client handled this, it's going to be handled as a client-less event.
- // We do this only at the end to avoid handling certain events (e.g.
- // deletion) twice.
- handle_event(global_id, LocalObjectHandle(nullptr, INVALID_ID), info->opcode, buf, info->size);
- }
- } else {
- LocalObjectHandle local_obj = LocalObjectHandle(client, client ? client->get_local_id(global_id) : INVALID_ID);
- MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);
- if (event_status == MessageStatus::ERROR) {
- return ERR_BUG;
- }
- if (event_status == MessageStatus::HANDLED || event_status == MessageStatus::INVALID) {
- // We're done.
- return OK;
- }
- // Generic passthrough.
- if (client) {
- uint32_t local_id = client->get_local_id(global_id);
- ERR_FAIL_COND_V(local_id == INVALID_ID, OK);
- DEBUG_LOG_WAYLAND_EMBED(vformat("%s::%s(%s) g0x%x -> l0x%x", interface->name, message->name, message->signature, global_id, local_id));
- buf[0] = local_id;
- if (handle_generic_msg(client, local_obj.get(), message, info, buf)) {
- send_raw_message(client->socket, { { buf, info->size } }, sent_fds);
- }
- } else {
- WARN_PRINT_ONCE(vformat("[Wayland Embedder] Unexpected client-less event from %s#g0x%x. Object has probably leaked.", object->interface->name, global_id));
- handle_generic_msg(nullptr, object, message, info, buf);
- }
- }
- }
- for (int fd : sent_fds) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Closing fd %d.", fd));
- close(fd);
- }
- return OK;
- }
- Error WaylandEmbedder::handle_sock(int p_fd) {
- ERR_FAIL_COND_V(p_fd < 0, ERR_INVALID_PARAMETER);
- struct msg_info info = {};
- {
- struct msghdr head_msg = {};
- uint32_t header[2];
- struct iovec vec = { header, sizeof header };
- head_msg.msg_iov = &vec;
- head_msg.msg_iovlen = 1;
- ssize_t head_rec = recvmsg(p_fd, &head_msg, MSG_PEEK);
- if (head_rec == 0) {
- // Client disconnected.
- return ERR_CONNECTION_ERROR;
- }
- if (head_rec == -1) {
- if (errno == ECONNRESET) {
- // No need to print the error, the client forcefully disconnected, that's
- // fine.
- return ERR_CONNECTION_ERROR;
- }
- ERR_FAIL_V_MSG(FAILED, vformat("Can't read message header: %s", strerror(errno)));
- }
- ERR_FAIL_COND_V_MSG(((size_t)head_rec) != vec.iov_len, ERR_CONNECTION_ERROR, vformat("Should've received %d bytes, instead got %d bytes", vec.iov_len, head_rec));
- // Header is two 32-bit words: first is ID, second has size in most significant
- // half and opcode in the other half.
- info.raw_id = header[0];
- info.size = header[1] >> 16;
- info.opcode = header[1] & 0xFFFF;
- info.direction = p_fd != compositor_socket ? ProxyDirection::COMPOSITOR : ProxyDirection::CLIENT;
- }
- if (msg_buf.size() < info.words()) {
- msg_buf.resize(info.words());
- }
- ERR_FAIL_COND_V_MSG(info.size % WL_WORD_SIZE != 0, ERR_CONNECTION_ERROR, "Invalid message length.");
- struct msghdr full_msg = {};
- struct iovec vec = { msg_buf.ptr(), info.size };
- {
- full_msg.msg_iov = &vec;
- full_msg.msg_iovlen = 1;
- full_msg.msg_control = ancillary_buf.ptr();
- full_msg.msg_controllen = ancillary_buf.size();
- ssize_t full_rec = recvmsg(p_fd, &full_msg, 0);
- if (full_rec == -1) {
- if (errno == ECONNRESET) {
- // No need to print the error, the client forcefully disconnected, that's
- // fine.
- return ERR_CONNECTION_ERROR;
- }
- ERR_FAIL_V_MSG(FAILED, vformat("Can't read message: %s", strerror(errno)));
- }
- ERR_FAIL_COND_V_MSG(((size_t)full_rec) != info.size, ERR_CONNECTION_ERROR, "Invalid message length.");
- DEBUG_LOG_WAYLAND_EMBED(" === START PACKET === ");
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- printf("[PROXY] Received bytes: ");
- for (ssize_t i = 0; i < full_rec; ++i) {
- printf("%.2x", ((const uint8_t *)msg_buf.ptr())[i]);
- }
- printf("\n");
- #endif
- }
- if (full_msg.msg_controllen > 0) {
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&full_msg);
- while (cmsg) {
- // TODO: Check for validity of message fields.
- size_t data_len = cmsg->cmsg_len - sizeof *cmsg;
- if (cmsg->cmsg_type == SCM_RIGHTS) {
- // NOTE: Linux docs say that we can't just cast data to pointer type because
- // of alignment concerns. So we have to memcpy into a new buffer.
- int *cmsg_fds = (int *)malloc(data_len);
- memcpy(cmsg_fds, CMSG_DATA(cmsg), data_len);
- size_t cmsg_fds_count = data_len / sizeof *cmsg_fds;
- for (size_t i = 0; i < cmsg_fds_count; ++i) {
- int fd = cmsg_fds[i];
- if (info.direction == ProxyDirection::COMPOSITOR) {
- clients[p_fd].fds.push_back(fd);
- } else {
- compositor_fds.push_back(fd);
- }
- }
- #ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED
- printf("[PROXY] Received %ld file descriptors: ", cmsg_fds_count);
- for (size_t i = 0; i < cmsg_fds_count; ++i) {
- printf("%d ", cmsg_fds[i]);
- }
- printf("\n");
- #endif
- free(cmsg_fds);
- }
- cmsg = CMSG_NXTHDR(&full_msg, cmsg);
- }
- }
- full_msg.msg_control = nullptr;
- full_msg.msg_controllen = 0;
- int fds_requested = 0;
- Client *client = nullptr;
- if (p_fd == compositor_socket) {
- // Let's figure out the recipient of the message.
- for (KeyValue<int, Client> &pair : clients) {
- Client &c = pair.value;
- if (c.local_ids.has(info.raw_id)) {
- client = &c;
- }
- }
- } else {
- CRASH_COND(!clients.has(p_fd));
- client = &clients[p_fd];
- }
- if (handle_msg_info(client, &info, msg_buf.ptr(), &fds_requested) != OK) {
- return ERR_BUG;
- }
- DEBUG_LOG_WAYLAND_EMBED(" === END PACKET === ");
- return OK;
- }
- void WaylandEmbedder::_thread_loop(void *p_data) {
- Thread::set_name("Wayland Embed");
- ERR_FAIL_NULL(p_data);
- WaylandEmbedder *proxy = (WaylandEmbedder *)p_data;
- DEBUG_LOG_WAYLAND_EMBED("Proxy thread started");
- while (!proxy->thread_done.is_set()) {
- proxy->poll_sockets();
- }
- }
- Error WaylandEmbedder::init() {
- ancillary_buf.resize(EMBED_ANCILLARY_BUF_SIZE);
- proxy_socket = socket(AF_UNIX, SOCK_STREAM, 0);
- struct sockaddr_un addr = {};
- addr.sun_family = AF_UNIX;
- String runtime_dir_path = OS::get_singleton()->get_environment("XDG_RUNTIME_DIR");
- ERR_FAIL_COND_V_MSG(runtime_dir_path.is_empty(), ERR_DOES_NOT_EXIST, "XDG_RUNTIME_DIR is not set or empty.");
- runtime_dir = DirAccess::create_for_path(runtime_dir_path);
- ERR_FAIL_COND_V(!runtime_dir.is_valid(), ERR_BUG);
- ERR_FAIL_COND_V_MSG(!runtime_dir->is_writable(runtime_dir_path), ERR_FILE_CANT_WRITE, "XDG_RUNTIME_DIR points to an invalid directory.");
- int socket_id = 0;
- while (socket_path.is_empty()) {
- String test_socket_path = runtime_dir_path + "/godot-wayland-" + itos(socket_id);
- String test_socket_lock_path = test_socket_path + ".lock";
- print_verbose(vformat("Trying to get socket %s", test_socket_path));
- print_verbose(vformat("Opening lock %s", test_socket_lock_path));
- int test_lock_fd = open(test_socket_lock_path.utf8().get_data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
- if (flock(test_lock_fd, LOCK_EX | LOCK_NB) == -1) {
- print_verbose(vformat("Can't lock %s", test_socket_lock_path));
- close(test_lock_fd);
- ++socket_id;
- continue;
- } else {
- lock_fd = test_lock_fd;
- socket_path = test_socket_path;
- socket_lock_path = test_socket_lock_path;
- break;
- }
- }
- DirAccess::remove_absolute(socket_path);
- strncpy(addr.sun_path, socket_path.utf8().get_data(), sizeof(addr.sun_path) - 1);
- if (bind(proxy_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't bind embedding socket.");
- }
- if (listen(proxy_socket, 1) == -1) {
- ERR_FAIL_V_MSG(ERR_CANT_OPEN, "Can't listen embedding socket.");
- }
- struct wl_display *display = wl_display_connect(nullptr);
- ERR_FAIL_NULL_V(display, ERR_CANT_OPEN);
- compositor_socket = wl_display_get_fd(display);
- pollfds.push_back({ proxy_socket, POLLIN, 0 });
- pollfds.push_back({ compositor_socket, POLLIN, 0 });
- RegistryGlobalInfo control_global_info = {};
- control_global_info.interface = &godot_embedding_compositor_interface;
- control_global_info.version = godot_embedding_compositor_interface.version;
- godot_embedding_compositor_name = registry_globals_counter++;
- registry_globals[godot_embedding_compositor_name] = control_global_info;
- {
- uint32_t invalid_id = INVALID_ID;
- objects.request(invalid_id);
- CRASH_COND(invalid_id != INVALID_ID);
- }
- {
- uint32_t display_id = new_object(&wl_display_interface);
- CRASH_COND(display_id != DISPLAY_ID);
- get_object(DISPLAY_ID)->shared = true;
- }
- {
- uint32_t registry_id = new_object(&wl_registry_interface);
- CRASH_COND(registry_id != REGISTRY_ID);
- get_object(REGISTRY_ID)->shared = true;
- }
- // wl_display::get_registry(n)
- send_wayland_message(compositor_socket, DISPLAY_ID, 1, { REGISTRY_ID });
- sync();
- proxy_thread.start(_thread_loop, this);
- return OK;
- }
- void WaylandEmbedder::handle_fd(int p_fd, int p_revents) {
- if (p_fd == proxy_socket && p_revents & POLLIN) {
- // Client init.
- int new_fd = accept(proxy_socket, nullptr, nullptr);
- ERR_FAIL_COND_MSG(new_fd == -1, "Failed to accept client.");
- struct ucred cred = {};
- socklen_t cred_size = sizeof cred;
- getsockopt(new_fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_size);
- Client &client = clients.insert_new(new_fd, {})->value;
- client.embedder = this;
- client.socket = new_fd;
- client.pid = cred.pid;
- client.global_ids[DISPLAY_ID] = Client::GlobalIdInfo(DISPLAY_ID, nullptr);
- client.local_ids[DISPLAY_ID] = DISPLAY_ID;
- pollfds.push_back({ new_fd, POLLIN, 0 });
- if (main_client == nullptr) {
- main_client = &client;
- }
- if (new_fd != main_client->socket && main_client->registry_globals_instances.has(godot_embedding_compositor_name)) {
- uint32_t new_local_id = main_client->allocate_server_id();
- client.embedded_client_id = new_local_id;
- for (uint32_t local_id : main_client->registry_globals_instances[godot_embedding_compositor_name]) {
- EmbeddedClientData *eclient_data = memnew(EmbeddedClientData);
- eclient_data->client = &client;
- main_client->new_fake_object(new_local_id, &godot_embedded_client_interface, 1, eclient_data);
- // godot_embedding_compositor::client(nu)
- send_wayland_message(main_client->socket, local_id, 0, { new_local_id, (uint32_t)cred.pid });
- }
- }
- DEBUG_LOG_WAYLAND_EMBED(vformat("New client %d (pid %d) initialized.", client.socket, cred.pid));
- return;
- }
- if (p_fd == compositor_socket && p_revents & POLLIN) {
- Error err = handle_sock(p_fd);
- if (err == ERR_BUG) {
- ERR_PRINT("Unexpected error while handling socket, shutting down.");
- shutdown();
- return;
- }
- return;
- }
- const Client *client = clients.getptr(p_fd);
- if (client) {
- if (main_client && client == main_client && p_revents & (POLLHUP | POLLERR)) {
- DEBUG_LOG_WAYLAND_EMBED("Main client disconnected, shutting down.");
- shutdown();
- return;
- }
- if (p_revents & POLLIN) {
- Error err = handle_sock(p_fd);
- if (err == ERR_BUG) {
- ERR_PRINT("Unexpected error while handling socket, shutting down.");
- shutdown();
- return;
- }
- if (err != OK) {
- DEBUG_LOG_WAYLAND_EMBED("disconnecting");
- cleanup_socket(p_fd);
- return;
- }
- return;
- } else if (p_revents & (POLLHUP | POLLERR | POLLNVAL)) {
- if (p_revents & POLLHUP) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d hangup.", p_fd));
- }
- if (p_revents & POLLERR) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d error.", p_fd));
- }
- if (p_revents & POLLNVAL) {
- DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d invalid FD.", p_fd));
- }
- cleanup_socket(p_fd);
- return;
- }
- }
- }
- WaylandEmbedder::~WaylandEmbedder() {
- shutdown();
- if (proxy_thread.is_started()) {
- proxy_thread.wait_to_finish();
- }
- }
- #endif // TOOLS_ENABLED
- #endif // WAYLAND_ENABLED
|