Path: blob/master/platform/linuxbsd/wayland/wayland_embedder.cpp
14772 views
/**************************************************************************/1/* wayland_embedder.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "wayland_embedder.h"3132#ifdef WAYLAND_ENABLED3334#ifdef TOOLS_ENABLED3536#include <sys/stat.h>3738#ifdef __FreeBSD__39#include <dev/evdev/input-event-codes.h>40#else41// Assume Linux.42#include <linux/input-event-codes.h>43#endif4445#include "core/os/os.h"4647#include <fcntl.h>48#include <sys/file.h>49#include <unistd.h>5051#define WAYLAND_EMBED_ID_MAX 10005253//#define WAYLAND_EMBED_DEBUG_LOGS_ENABLED54#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED5556// Gotta flush as we're doing this mess from a thread without any57// synchronization. It's awful, I know, but the `print_*` utilities hang for58// some reason during editor startup and I need some quick and dirty debugging.59#define DEBUG_LOG_WAYLAND_EMBED(...) \60if (1) { \61printf("[PROXY] %s\n", vformat(__VA_ARGS__).utf8().ptr()); \62fflush(stdout); \63} else \64((void)0)6566#else67#define DEBUG_LOG_WAYLAND_EMBED(...)68#endif6970// Wayland messages are structured with 32-bit words.71#define WL_WORD_SIZE (sizeof(uint32_t))7273// Event opcodes. Request opcodes are defined in the generated client headers.74// We could generate server headers but they would clash (without modifications)75// and we use just a few constants anyways.7677#define WL_DISPLAY_ERROR 078#define WL_DISPLAY_DELETE_ID 17980#define WL_REGISTRY_GLOBAL 081#define WL_REGISTRY_GLOBAL_REMOVE 18283#define WL_CALLBACK_DONE 08485#define WL_KEYBOARD_ENTER 186#define WL_KEYBOARD_LEAVE 287#define WL_KEYBOARD_KEY 38889#define WL_POINTER_ENTER 090#define WL_POINTER_LEAVE 191#define WL_POINTER_BUTTON 39293#define WL_SHM_FORMAT 09495#define WL_DRM_DEVICE 096#define WL_DRM_FORMAT 197#define WL_DRM_AUTHENTICATED 298#define WL_DRM_CAPABILITIES 399100#define XDG_POPUP_CONFIGURE 0101102size_t WaylandEmbedder::wl_array_word_offset(uint32_t p_size) {103uint32_t pad = (WL_WORD_SIZE - (p_size % WL_WORD_SIZE)) % WL_WORD_SIZE;104return (p_size + pad) / WL_WORD_SIZE;105}106107const struct wl_interface *WaylandEmbedder::wl_interface_from_string(const char *name, size_t size) {108for (size_t i = 0; i < (sizeof interfaces / sizeof *interfaces); ++i) {109if (strncmp(name, interfaces[i]->name, size) == 0) {110return interfaces[i];111}112}113114return nullptr;115}116117int WaylandEmbedder::wl_interface_get_destructor_opcode(const struct wl_interface *p_iface, uint32_t version) {118ERR_FAIL_NULL_V(p_iface, -1);119120// FIXME: Figure out how to extract the destructor from the XML files. This121// value is not currently exposed by wayland-scanner.122for (int i = 0; i < p_iface->method_count; ++i) {123const struct wl_message &m = p_iface->methods[i];124uint32_t destructor_version = String::to_int(m.signature);125if (destructor_version <= version && (strcmp(m.name, "destroy") == 0 || strcmp(m.name, "release") == 0)) {126return i;127}128}129130return -1;131}132133struct WaylandEmbedder::WaylandObject *WaylandEmbedder::get_object(uint32_t p_global_id) {134if (p_global_id == 0) {135return nullptr;136}137138// Server-allocated stuff starts at 0xff000000.139bool is_server = p_global_id & 0xff000000;140if (is_server) {141p_global_id &= ~(0xff000000);142}143144#ifdef DEV_ENABLED145if (p_global_id >= WAYLAND_EMBED_ID_MAX) {146// Oh no. Time for debug info!147148#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED149for (uint32_t id = 1; id < objects.reserved_size(); ++id) {150WaylandObject &object = objects[id];151DEBUG_LOG_WAYLAND_EMBED(vformat(" - g0x%x (#%d): %s version %d, data 0x%x", id, id, object.interface->name, object.version, (uintptr_t)object.data));152}153#endif // WAYLAND_EMBED_DEBUG_LOGS_ENABLED154155CRASH_NOW_MSG(vformat("Tried to access ID bigger than debug cap (%d > %d).", p_global_id, WAYLAND_EMBED_ID_MAX));156}157#endif // DEV_ENABLED158159if (is_server) {160if (server_objects.size() <= p_global_id) {161return nullptr;162}163164return &server_objects[p_global_id];165} else {166if (objects.reserved_size() <= p_global_id) {167return nullptr;168}169170return &objects[p_global_id];171}172}173174Error WaylandEmbedder::delete_object(uint32_t p_global_id) {175WaylandObject *object = get_object(p_global_id);176ERR_FAIL_NULL_V(object, ERR_DOES_NOT_EXIST);177178if (object->shared) {179ERR_FAIL_V_MSG(FAILED, vformat("Tried to delete shared object g0x%x.", p_global_id));180}181182DEBUG_LOG_WAYLAND_EMBED(vformat("Deleting object %s g0x%x", object->interface ? object->interface->name : "UNKNOWN", p_global_id));183184if (object->data) {185memdelete(object->data);186object->data = nullptr;187}188189bool is_server = p_global_id & 0xff000000;190if (is_server) {191server_objects[p_global_id & ~(0xff000000)] = WaylandObject();192} else {193objects.free(p_global_id);194}195196registry_globals_names.erase(p_global_id);197198return OK;199}200201uint32_t WaylandEmbedder::Client::allocate_server_id() {202uint32_t new_id = INVALID_ID;203204if (free_server_ids.size() > 0) {205int new_size = free_server_ids.size() - 1;206new_id = free_server_ids[new_size] | 0xff000000;207free_server_ids.resize_uninitialized(new_size);208} else {209new_id = allocated_server_ids | 0xff000000;210211++allocated_server_ids;212#ifdef DEV_ENABLED213CRASH_COND_MSG(allocated_server_ids > WAYLAND_EMBED_ID_MAX, "Max server ID reached. This might indicate a leak.");214#endif // DEV_ENABLED215}216217DEBUG_LOG_WAYLAND_EMBED(vformat("Allocated server-side id 0x%x.", new_id));218219return new_id;220}221222struct WaylandEmbedder::WaylandObject *WaylandEmbedder::Client::get_object(uint32_t p_local_id) {223if (p_local_id == INVALID_ID) {224return nullptr;225}226227if (global_instances.has(p_local_id)) {228return &global_instances[p_local_id];229}230231if (fake_objects.has(p_local_id)) {232return &fake_objects[p_local_id];233}234235if (!global_ids.has(p_local_id)) {236return nullptr;237}238239ERR_FAIL_NULL_V(embedder, nullptr);240return embedder->get_object(get_global_id(p_local_id));241}242243Error WaylandEmbedder::Client::bind_global_id(uint32_t p_global_id, uint32_t p_local_id) {244ERR_FAIL_COND_V(local_ids.has(p_global_id), ERR_ALREADY_EXISTS);245ERR_FAIL_COND_V(global_ids.has(p_local_id), ERR_ALREADY_EXISTS);246247GlobalIdInfo gid_info;248gid_info.id = p_global_id;249DEBUG_LOG_WAYLAND_EMBED(vformat("Pushing g0x%x in the global id history", p_global_id));250gid_info.history_elem = global_id_history.push_back(p_global_id);251global_ids[p_local_id] = gid_info;252253local_ids[p_global_id] = p_local_id;254255return OK;256}257258Error WaylandEmbedder::Client::delete_object(uint32_t p_local_id) {259if (fake_objects.has(p_local_id)) {260#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED261WaylandObject *object = &fake_objects[p_local_id];262DEBUG_LOG_WAYLAND_EMBED(vformat("Deleting fake object %s l0x%x", object->interface ? object->interface->name : "UNKNOWN", p_local_id));263#endif264265if (!(p_local_id & 0xff000000)) {266// wl_display::delete_id267send_wayland_message(socket, DISPLAY_ID, 1, { p_local_id });268}269270fake_objects.erase(p_local_id);271272// We can skip everything else below, as fake objects don't have a global id.273return OK;274}275276ERR_FAIL_COND_V(!global_ids.has(p_local_id), ERR_DOES_NOT_EXIST);277GlobalIdInfo gid_info = global_ids[p_local_id];278uint32_t global_id = gid_info.id;279280DEBUG_LOG_WAYLAND_EMBED(vformat("Erasing g0x%x from the global id history", global_id));281global_id_history.erase(gid_info.history_elem);282283if (global_instances.has(p_local_id)) {284#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED285WaylandObject *object = &global_instances[p_local_id];286DEBUG_LOG_WAYLAND_EMBED(vformat("Deleting global instance %s l0x%x", object->interface ? object->interface->name : "UNKNOWN", p_local_id));287#endif288289// wl_display::delete_id290send_wayland_message(socket, DISPLAY_ID, 1, { p_local_id });291292// We don't want to delete the global object tied to this instance, so we'll only get rid of the local stuff.293global_instances.erase(p_local_id);294global_ids.erase(p_local_id);295296if (global_id != INVALID_ID) {297local_ids.erase(global_id);298}299300// We're done here.301return OK;302}303304if (wl_registry_instances.has(p_local_id)) {305wl_registry_instances.erase(p_local_id);306}307308WaylandObject *object = embedder->get_object(global_id);309ERR_FAIL_NULL_V(object, ERR_DOES_NOT_EXIST);310311ERR_FAIL_COND_V_MSG(object->shared, ERR_INVALID_PARAMETER, vformat("Tried to delete shared object g0x%x.", global_id));312313global_ids.erase(p_local_id);314local_ids.erase(global_id);315316if (p_local_id & 0xff000000) {317free_server_ids.push_back(p_local_id & ~(0xff000000));318}319320uint32_t *global_name = embedder->registry_globals_names.getptr(global_id);321if (global_name) {322{323RegistryGlobalInfo &info = embedder->registry_globals[*global_name];324ERR_FAIL_COND_V_MSG(info.instance_counter == 0, ERR_BUG, "Instance counter inconsistency.");325--info.instance_counter;326327if (info.destroyed && info.instance_counter == 0) {328embedder->registry_globals.erase(*global_name);329}330}331332registry_globals_instances[*global_name].erase(p_local_id);333}334335return embedder->delete_object(global_id);336}337338// Returns INVALID_ID if the creation fails. In that case, the user can assume339// that the client got kicked out.340uint32_t WaylandEmbedder::Client::new_object(uint32_t p_local_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {341if (embedder == nullptr) {342socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "No embedder set.");343ERR_FAIL_V(INVALID_ID);344}345346if (get_object(p_local_id) != nullptr) {347socket_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));348ERR_FAIL_V(INVALID_ID);349}350351uint32_t new_global_id = embedder->new_object(p_interface, p_version, p_data);352353bind_global_id(new_global_id, p_local_id);354355return new_global_id;356}357358uint32_t WaylandEmbedder::Client::new_server_object(uint32_t p_global_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {359if (embedder == nullptr) {360socket_error(socket, get_local_id(p_global_id), WL_DISPLAY_ERROR_IMPLEMENTATION, "No embedder set.");361ERR_FAIL_V(INVALID_ID);362}363364uint32_t new_local_id = allocate_server_id();365366embedder->new_server_object(p_global_id, p_interface, p_version, p_data);367368bind_global_id(p_global_id, new_local_id);369370return new_local_id;371}372373WaylandEmbedder::WaylandObject *WaylandEmbedder::Client::new_fake_object(uint32_t p_local_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {374if (embedder == nullptr) {375socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "No embedder set.");376ERR_FAIL_V(nullptr);377}378379if (get_object(p_local_id) != nullptr) {380socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, vformat("Object l0x%x already exists", p_local_id));381ERR_FAIL_V(nullptr);382}383384WaylandObject &new_object = fake_objects[p_local_id];385new_object.interface = p_interface;386new_object.version = p_version;387new_object.data = p_data;388389return &new_object;390}391392WaylandEmbedder::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) {393if (embedder == nullptr) {394socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "No embedder set.");395ERR_FAIL_V(nullptr);396}397398if (get_object(p_local_id) != nullptr) {399socket_error(socket, p_local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, vformat("Object l0x%x already exists", p_local_id));400ERR_FAIL_V(nullptr);401}402403WaylandObject &new_object = global_instances[p_local_id];404new_object.interface = p_interface;405new_object.version = p_version;406new_object.data = p_data;407408// FIXME: Track each instance properly. Global instances (the compatibility409// mechanism) are particular as they're the only case where a global ID might410// map to multiple local objects. In that case we need to mirror each event411// which passes a registry object as an argument for each instance.412GlobalIdInfo gid_info;413gid_info.id = p_global_id;414gid_info.history_elem = global_id_history.push_back(p_global_id);415global_ids[p_local_id] = gid_info;416417// NOTE: Normally, for each client, there's a single local object per global418// object, but global instances break this expectation. This is technically419// wrong but should work fine, as we have special logic whenever needed.420//421// TODO: it might be nice to enforce that this table is never looked up for422// global instances or even just log attempts.423local_ids[p_global_id] = p_local_id;424425return &new_object;426}427428Error WaylandEmbedder::Client::send_wl_drm_state(uint32_t p_id, WaylandDrmGlobalData *p_state) {429ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);430431if (p_state->device.is_empty()) {432// Not yet initialized.433return OK;434}435436LocalVector<union wl_argument> args;437args.push_back(wl_arg_string(p_state->device.utf8().get_data()));438send_wayland_event(socket, p_id, wl_drm_interface, WL_DRM_DEVICE, args);439440for (uint32_t format : p_state->formats) {441Error err = send_wayland_message(socket, p_id, WL_DRM_FORMAT, { format });442ERR_FAIL_COND_V(err != OK, err);443}444445if (p_state->authenticated) {446Error err = send_wayland_message(socket, p_id, WL_DRM_AUTHENTICATED, {});447ERR_FAIL_COND_V(err != OK, err);448}449450Error err = send_wayland_message(socket, p_id, WL_DRM_CAPABILITIES, { p_state->capabilities });451ERR_FAIL_COND_V(err != OK, err);452453return OK;454}455456void WaylandEmbedder::cleanup_socket(int p_socket) {457DEBUG_LOG_WAYLAND_EMBED(vformat("Cleaning up socket %d.", p_socket));458459close(p_socket);460461for (size_t i = 0; i < pollfds.size(); ++i) {462if (pollfds[i].fd == p_socket) {463pollfds.remove_at_unordered(i);464break;465}466}467468ERR_FAIL_COND(!clients.has(p_socket));469470Client &client = clients[p_socket];471472for (KeyValue<uint32_t, WaylandObject> &pair : client.fake_objects) {473WaylandObject &object = pair.value;474475if (object.interface == &xdg_toplevel_interface) {476XdgToplevelData *data = (XdgToplevelData *)object.data;477CRASH_COND(data == nullptr);478479if (data->wl_subsurface_id != INVALID_ID) {480// wl_subsurface::destroy() - xdg_toplevels are mapped to subsurfaces.481send_wayland_message(compositor_socket, data->wl_subsurface_id, 0, {});482}483484if (!data->xdg_surface_handle.get()) {485continue;486}487488XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)data->xdg_surface_handle.get()->data;489if (xdg_surf_data == nullptr) {490continue;491}492493if (!data->parent_handle.get()) {494continue;495}496497XdgToplevelData *parent_data = (XdgToplevelData *)data->parent_handle.get()->data;498if (parent_data == nullptr) {499continue;500}501502if (!parent_data->xdg_surface_handle.get()) {503continue;504}505506XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)parent_data->xdg_surface_handle.get()->data;507if (parent_xdg_surf_data == nullptr) {508continue;509}510511for (uint32_t wl_seat_name : wl_seat_names) {512WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;513if (global_seat_data == nullptr) {514continue;515}516517if (global_seat_data->focused_surface_id == xdg_surf_data->wl_surface_id) {518seat_name_leave_surface(wl_seat_name, xdg_surf_data->wl_surface_id);519seat_name_enter_surface(wl_seat_name, parent_xdg_surf_data->wl_surface_id);520}521}522}523}524525for (List<uint32_t>::Element *E = client.global_id_history.back(); E;) {526uint32_t global_id = E->get();527E = E->prev();528529WaylandObject *object = get_object(global_id);530if (object == nullptr) {531DEBUG_LOG_WAYLAND_EMBED(vformat("Skipping deletability check of object g0x%x as it's null.", global_id));532continue;533}534535if (object->interface == nullptr) {536DEBUG_LOG_WAYLAND_EMBED(vformat("Skipping deletability check of object g0x%x as it's invalid.", global_id));537continue;538}539540DEBUG_LOG_WAYLAND_EMBED(vformat("Checking deletability of %s#g0x%x version %s", object->interface->name, global_id, object->version));541542if (object->shared) {543DEBUG_LOG_WAYLAND_EMBED("Shared, skipping.");544continue;545}546547if (object->interface == &wl_callback_interface) {548// Those things self-destruct.549DEBUG_LOG_WAYLAND_EMBED("wl_callback self destructs.");550continue;551}552553if (object->destroyed) {554DEBUG_LOG_WAYLAND_EMBED("Already destroyed, skipping.");555continue;556}557558int destructor = wl_interface_get_destructor_opcode(object->interface, object->version);559if (destructor >= 0) {560DEBUG_LOG_WAYLAND_EMBED(vformat("Destroying %s#g0x%x", object->interface->name, global_id));561562if (object->interface == &wl_surface_interface) {563for (uint32_t wl_seat_name : wl_seat_names) {564WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;565if (global_seat_data) {566if (global_seat_data->pointed_surface_id == global_id) {567global_seat_data->pointed_surface_id = INVALID_ID;568}569570if (global_seat_data->focused_surface_id == global_id) {571global_seat_data->focused_surface_id = INVALID_ID;572}573}574}575}576577send_wayland_message(compositor_socket, global_id, destructor, {});578object->destroyed = true;579580if (global_id & 0xff000000) {581delete_object(global_id);582object = nullptr;583}584}585586if (object && !object->destroyed) {587ERR_PRINT(vformat("Unreferenced object %s g0x%x (leak!)", object->interface->name, global_id));588}589}590591uint32_t eclient_id = client.embedded_client_id;592593clients.erase(client.socket);594595WaylandObject *eclient = main_client->get_object(eclient_id);596597if (eclient) {598EmbeddedClientData *eclient_data = (EmbeddedClientData *)eclient->data;599ERR_FAIL_NULL(eclient_data);600601if (!eclient_data->disconnected) {602// godot_embedded_client::disconnected603send_wayland_message(main_client->socket, eclient_id, 0, {});604}605606eclient_data->disconnected = true;607}608}609610void WaylandEmbedder::socket_error(int p_socket, uint32_t p_object_id, uint32_t p_code, const String &p_message) {611const char *err_name = "unknown";612switch (p_code) {613case WL_DISPLAY_ERROR_INVALID_OBJECT: {614err_name = "invalid_object";615} break;616617case WL_DISPLAY_ERROR_INVALID_METHOD: {618err_name = "invalid_method";619} break;620621case WL_DISPLAY_ERROR_NO_MEMORY: {622err_name = "no_memory";623} break;624625case WL_DISPLAY_ERROR_IMPLEMENTATION: {626err_name = "implementation";627} break;628}629630ERR_PRINT(vformat("Socket %d %s error: %s", p_socket, err_name, p_message));631632LocalVector<union wl_argument> args;633args.push_back(wl_arg_object(p_object_id));634args.push_back(wl_arg_uint(p_code));635args.push_back(wl_arg_string(vformat("[Godot Embedder] %s", p_message).utf8().get_data()));636637send_wayland_event(p_socket, DISPLAY_ID, wl_display_interface, WL_DISPLAY_ERROR, args);638639// So, here's the deal: from some extensive research I did, there are640// absolutely zero safeguards for ensuring that the error message ends to the641// client. It's absolutely tiny and takes _nothing_ to get there (less than642// 4µs with a debug build on my machine), but still enough to get truncated in643// the distance between `send_wayland_event` and `close`.644//645// Because of this we're going to give the client some slack: we're going to646// wait for its socket to close (or whatever) or 1s, whichever happens first.647//648// Hopefully it's good enough for <1000 bytes :P649struct pollfd pollfd = {};650pollfd.fd = p_socket;651652int ret = poll(&pollfd, 1, 1'000);653if (ret == 0) {654ERR_PRINT("Client timeout while disconnecting.");655}656if (ret < 0) {657ERR_PRINT(vformat("Client error while disconnecting: %s", strerror(errno)));658}659660close(p_socket);661}662663void WaylandEmbedder::poll_sockets() {664if (poll(pollfds.ptr(), pollfds.size(), -1) == -1) {665CRASH_NOW_MSG(vformat("poll() failed, errno %d.", errno));666}667668// First handle everything but the listening socket (which is always the first669// element), so that we can cleanup closed sockets before accidentally reusing670// them (and breaking everything).671for (size_t i = 1; i < pollfds.size(); ++i) {672handle_fd(pollfds[i].fd, pollfds[i].revents);673}674675handle_fd(pollfds[0].fd, pollfds[0].revents);676}677678Error WaylandEmbedder::send_raw_message(int p_socket, std::initializer_list<struct iovec> p_vecs, const LocalVector<int> &p_fds) {679struct msghdr msg = {};680msg.msg_iov = (struct iovec *)p_vecs.begin();681msg.msg_iovlen = p_vecs.size();682683if (!p_fds.is_empty()) {684size_t data_size = p_fds.size() * sizeof(int);685686msg.msg_control = Memory::alloc_aligned_static(CMSG_SPACE(data_size), CMSG_ALIGN(1));687msg.msg_controllen = CMSG_SPACE(data_size);688689struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);690cmsg->cmsg_level = SOL_SOCKET;691cmsg->cmsg_type = SCM_RIGHTS;692cmsg->cmsg_len = CMSG_LEN(data_size);693694// NOTE: According to the linux man page cmsg(5), we shall not access the695// pointer returned CMSG_DATA directly, due to alignment concerns. We should696// copy data from a suitably aligned object instead.697memcpy(CMSG_DATA(cmsg), p_fds.ptr(), data_size);698}699700#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED701printf("[PROXY] Sending: ");702703for (const struct iovec &vec : p_vecs) {704for (size_t i = 0; i < vec.iov_len; ++i) {705printf("%.2x", ((const uint8_t *)vec.iov_base)[i]);706}707}708printf("\n");709#endif710711sendmsg(p_socket, &msg, MSG_NOSIGNAL);712713if (msg.msg_control) {714Memory::free_aligned_static(msg.msg_control);715}716717return OK;718}719720Error 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) {721ERR_FAIL_COND_V(p_socket < 0, ERR_INVALID_PARAMETER);722ERR_FAIL_COND_V(p_id == INVALID_ID, ERR_INVALID_PARAMETER);723724uint32_t args_size = p_args_words * sizeof *p_args;725726// Header is always 8 bytes long.727uint32_t total_size = 8 + (args_size);728729uint32_t header[2] = { p_id, (total_size << 16) + p_opcode };730731struct iovec vecs[2] = {732{ header, 8 },733// According to the sendmsg manual, these buffers should never be written to,734// so this cast should be safe.735{ (void *)p_args, args_size },736};737738struct msghdr msg = {};739msg.msg_iov = vecs;740msg.msg_iovlen = std_size(vecs);741742#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED743printf("[PROXY] Sending: ");744745for (struct iovec &vec : vecs) {746for (size_t i = 0; i < vec.iov_len; ++i) {747printf("%.2x", ((const uint8_t *)vec.iov_base)[i]);748}749}750printf("\n");751#endif752753if (sendmsg(p_socket, &msg, MSG_NOSIGNAL) < 0) {754return FAILED;755}756757return OK;758}759760Error 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) {761ERR_FAIL_COND_V(p_direction == ProxyDirection::CLIENT && p_opcode >= (uint32_t)p_interface.event_count, ERR_INVALID_PARAMETER);762ERR_FAIL_COND_V(p_direction == ProxyDirection::COMPOSITOR && p_opcode >= (uint32_t)p_interface.method_count, ERR_INVALID_PARAMETER);763764const struct wl_message &msg = p_direction == ProxyDirection::CLIENT ? p_interface.events[p_opcode] : p_interface.methods[p_opcode];765766LocalVector<uint32_t> arg_buf;767768size_t arg_idx = 0;769for (size_t sig_idx = 0; sig_idx < strlen(msg.signature); ++sig_idx) {770if (arg_idx >= p_args.size()) {771String 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());772ERR_FAIL_COND_V_MSG(arg_idx >= p_args.size(), ERR_INVALID_PARAMETER, err_msg);773}774775char sym = msg.signature[sig_idx];776if (sym >= '0' && sym <= '?') {777// We don't care about version notices and nullability symbols. We can skip778// those.779continue;780}781782const union wl_argument &arg = p_args[arg_idx];783784switch (sym) {785case 'i': {786arg_buf.push_back((uint32_t)arg.i);787} break;788789case 'u': {790arg_buf.push_back(arg.u);791} break;792793case 'f': {794arg_buf.push_back((uint32_t)arg.f);795} break;796797case 'o': {798// We're encoding object arguments as uints because I don't think we can799// reuse the whole opaque struct thing.800arg_buf.push_back(arg.u);801} break;802803case 'n': {804arg_buf.push_back(arg.n);805} break;806807case 's': {808const char *str = p_args[arg_idx].s;809// Wayland requires the string length to include the null terminator.810uint32_t str_len = strlen(str) + 1;811812arg_buf.push_back(str_len);813814size_t data_begin_idx = arg_buf.size();815816uint32_t str_words = wl_array_word_offset(str_len);817818arg_buf.resize(arg_buf.size() + str_words);819strcpy((char *)(arg_buf.ptr() + data_begin_idx), str);820} break;821822case 'a': {823const wl_array *arr = p_args[arg_idx].a;824825arg_buf.push_back(arr->size);826827size_t data_begin_idx = arg_buf.size();828829uint32_t words = wl_array_word_offset(arr->size);830831arg_buf.resize(arg_buf.size() + words);832memcpy(arg_buf.ptr() + data_begin_idx, arr->data, arr->size);833} break;834835// FDs (h) are encoded out-of-band.836}837838++arg_idx;839}840841send_wayland_message(p_socket, p_id, p_opcode, arg_buf.ptr(), arg_buf.size());842843return OK;844}845846uint32_t WaylandEmbedder::new_object(const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {847uint32_t new_global_id = allocate_global_id();848849DEBUG_LOG_WAYLAND_EMBED(vformat("New object g0x%x %s", new_global_id, p_interface->name));850851WaylandObject *new_object = get_object(new_global_id);852new_object->interface = p_interface;853new_object->version = p_version;854new_object->data = p_data;855856return new_global_id;857}858859WaylandEmbedder::WaylandObject *WaylandEmbedder::new_server_object(uint32_t p_global_id, const struct wl_interface *p_interface, int p_version, WaylandObjectData *p_data) {860// The max ID will never increment more than one at a time, due to the861// packed nature of IDs. libwayland already does similar assertions so it862// just makes sense to double-check to avoid messing memory up or863// allocating a huge buffer for nothing.864uint32_t stripped_id = p_global_id & ~(0xff000000);865866ERR_FAIL_COND_V_MSG(stripped_id > server_objects.size(), nullptr, "Invalid new server id requested.");867ERR_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));868869if (stripped_id == server_objects.size()) {870server_objects.resize(server_objects.size() + 1);871}872873DEBUG_LOG_WAYLAND_EMBED(vformat("New server object %s g0x%x", p_interface->name, p_global_id));874875WaylandObject *new_object = get_object(p_global_id);876new_object->interface = p_interface;877new_object->version = p_version;878new_object->data = p_data;879880return new_object;881}882883void WaylandEmbedder::sync() {884CRASH_COND_MSG(sync_callback_id, "Sync already in progress.");885886sync_callback_id = allocate_global_id();887get_object(sync_callback_id)->interface = &wl_callback_interface;888get_object(sync_callback_id)->version = 1;889send_wayland_message(compositor_socket, DISPLAY_ID, 0, { sync_callback_id });890891DEBUG_LOG_WAYLAND_EMBED("Synchronizing");892893while (true) {894poll_sockets();895896if (!sync_callback_id) {897// Obj got deleted - sync is done.898return;899}900}901}902903// Returns the gid for the newly bound object, or an existing shared object if904// necessary.905uint32_t WaylandEmbedder::wl_registry_bind(uint32_t p_registry_id, uint32_t p_name, int p_version) {906RegistryGlobalInfo &info = registry_globals[p_name];907908uint32_t id = INVALID_ID;909910if (wl_interface_get_destructor_opcode(info.interface, p_version) < 0) {911DEBUG_LOG_WAYLAND_EMBED(vformat("Binding instanced global %s %d", info.interface->name, p_version));912913// Reusable object.914if (info.reusable_objects.has(p_version) && info.reusable_objects[p_version] != INVALID_ID) {915DEBUG_LOG_WAYLAND_EMBED("Already bound.");916return info.reusable_objects[p_version];917}918919id = new_object(info.interface, p_version);920ERR_FAIL_COND_V(id == INVALID_ID, INVALID_ID);921922info.reusable_objects[p_version] = id;923get_object(id)->shared = true;924} else {925DEBUG_LOG_WAYLAND_EMBED(vformat("Binding global %s as g0x%x version %d", info.interface->name, id, p_version));926id = new_object(info.interface, p_version);927}928929ERR_FAIL_COND_V(id == INVALID_ID, INVALID_ID);930931registry_globals_names[id] = p_name;932933LocalVector<union wl_argument> args;934args.push_back(wl_arg_uint(info.compositor_name));935args.push_back(wl_arg_string(info.interface->name));936args.push_back(wl_arg_int(p_version));937args.push_back(wl_arg_new_id(id));938939Error err = send_wayland_method(compositor_socket, p_registry_id, wl_registry_interface, WL_REGISTRY_BIND, args);940ERR_FAIL_COND_V_MSG(err != OK, INVALID_ID, "Error while sending bind request.");941942return id;943}944945void WaylandEmbedder::seat_name_enter_surface(uint32_t p_seat_name, uint32_t p_wl_surface_id) {946WaylandSurfaceData *surf_data = (WaylandSurfaceData *)get_object(p_wl_surface_id)->data;947CRASH_COND(surf_data == nullptr);948949Client *client = surf_data->client;950CRASH_COND(client == nullptr);951952if (!client->local_ids.has(p_wl_surface_id)) {953DEBUG_LOG_WAYLAND_EMBED("Called seat_name_enter_surface with an unknown surface");954return;955}956957uint32_t local_surface_id = client->get_local_id(p_wl_surface_id);958959DEBUG_LOG_WAYLAND_EMBED(vformat("KB: Entering surface g0x%x", p_wl_surface_id));960961for (uint32_t local_seat_id : client->registry_globals_instances[p_seat_name]) {962WaylandSeatInstanceData *seat_data = (WaylandSeatInstanceData *)client->get_object(local_seat_id)->data;963CRASH_COND(seat_data == nullptr);964965uint32_t local_keyboard_id = client->get_local_id(seat_data->wl_keyboard_id);966967if (local_keyboard_id != INVALID_ID) {968// TODO: track keys. Not super important at the time of writing, since we969// don't use that in the engine, although we should.970971// wl_keyboard::enter(serial, surface, keys) - keys will be empty for now972send_wayland_message(client->socket, local_keyboard_id, 1, { serial_counter++, local_surface_id, 0 });973}974}975976if (client->socket != main_client->socket) {977// godot_embedded_client::window_focus_in978send_wayland_message(main_client->socket, client->embedded_client_id, 2, {});979}980}981982void WaylandEmbedder::seat_name_leave_surface(uint32_t p_seat_name, uint32_t p_wl_surface_id) {983WaylandSurfaceData *surf_data = (WaylandSurfaceData *)get_object(p_wl_surface_id)->data;984CRASH_COND(surf_data == nullptr);985986Client *client = surf_data->client;987CRASH_COND(client == nullptr);988989if (!client->local_ids.has(p_wl_surface_id)) {990DEBUG_LOG_WAYLAND_EMBED("Called seat_name_leave_surface with an unknown surface!");991return;992}993994uint32_t local_surface_id = client->get_local_id(p_wl_surface_id);995996DEBUG_LOG_WAYLAND_EMBED(vformat("KB: Leaving surface g0x%x", p_wl_surface_id));997998for (uint32_t local_seat_id : client->registry_globals_instances[p_seat_name]) {999WaylandSeatInstanceData *seat_data = (WaylandSeatInstanceData *)client->get_object(local_seat_id)->data;1000CRASH_COND(seat_data == nullptr);10011002uint32_t local_keyboard_id = client->get_local_id(seat_data->wl_keyboard_id);10031004if (local_keyboard_id != INVALID_ID) {1005// wl_keyboard::enter(serial, surface, keys) - keys will be empty for now1006send_wayland_message(client->socket, local_keyboard_id, 2, { serial_counter++, local_surface_id });1007}1008}10091010if (client != main_client) {1011// godot_embedded_client::window_focus_out1012send_wayland_message(main_client->socket, client->embedded_client_id, 3, {});1013}1014}10151016int WaylandEmbedder::allocate_global_id() {1017uint32_t id = INVALID_ID;1018objects.request(id);1019objects[id] = WaylandObject();10201021DEBUG_LOG_WAYLAND_EMBED(vformat("Allocated new global id g0x%x", id));10221023#ifdef DEV_ENABLED1024if (id > WAYLAND_EMBED_ID_MAX) {1025// Oh no. Time for debug info!10261027#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED1028for (uint32_t i = 1; i < objects.reserved_size(); ++i) {1029WaylandObject &object = objects[id];1030DEBUG_LOG_WAYLAND_EMBED(vformat(" - g0x%x (#%d): %s version %d, data 0x%x", i, i, object.interface->name, object.version, (uintptr_t)object.data));1031}1032#endif // WAYLAND_EMBED_DEBUG_LOGS_ENABLED10331034CRASH_NOW_MSG("Max ID reached. This might indicate a leak.");1035}1036#endif // DEV_ENABLED10371038return id;1039}10401041bool WaylandEmbedder::global_surface_is_window(uint32_t p_wl_surface_id) {1042WaylandObject *surface_object = get_object(p_wl_surface_id);1043ERR_FAIL_NULL_V(surface_object, false);1044if (surface_object->interface != &wl_surface_interface || surface_object->data == nullptr) {1045return false;1046}10471048WaylandSurfaceData *surface_data = (WaylandSurfaceData *)surface_object->data;1049if (!surface_data->role_object_handle.get()) {1050return false;1051}10521053WaylandObject *role_object = surface_data->role_object_handle.get();10541055return (role_object && role_object->interface == &xdg_toplevel_interface);1056}10571058bool 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) {1059// We allow client-less events.1060CRASH_COND(client == nullptr && info->direction == ProxyDirection::COMPOSITOR);10611062ERR_FAIL_NULL_V(p_object, false);10631064bool valid = true;10651066// Let's strip the header.1067uint32_t *body = buf + 2;10681069size_t arg_idx = 0;1070size_t buf_idx = 0;1071size_t last_str_buf_idx = -1;1072uint32_t last_str_len = 0;1073for (size_t i = 0; i < strlen(message->signature); ++i) {1074ERR_FAIL_COND_V(buf_idx > (info->size / sizeof *body), false);10751076char sym = message->signature[i];1077if (sym >= '0' && sym <= '?') {1078// We don't care about version notices and nullability symbols. We can skip1079// those.1080continue;1081}10821083switch (sym) {1084case 'a': {1085uint32_t array_len = body[buf_idx];10861087// We can't obviously go forward by just one byte. Let's skip to the end of1088// the array.1089buf_idx += wl_array_word_offset(array_len);1090} break;10911092case 's': {1093uint32_t string_len = body[buf_idx];10941095last_str_buf_idx = buf_idx;1096last_str_len = string_len;10971098// Same as the array.1099buf_idx += wl_array_word_offset(string_len);1100} break;11011102case 'n': {1103uint32_t arg = body[buf_idx];11041105const struct wl_interface *new_interface = message->types[arg_idx];1106uint32_t new_version = p_object->version;11071108if (!new_interface && last_str_len != 0) {1109// When the protocol definition does not define an interface it reports a1110// string and an unsigned integer representing the interface and the1111// version requested.1112new_interface = wl_interface_from_string((char *)(body + last_str_buf_idx + 1), last_str_len);1113new_version = body[arg_idx - 1];1114}11151116if (new_interface == nullptr) {1117if (last_str_len > 0) {1118DEBUG_LOG_WAYLAND_EMBED(vformat("Unknown interface %s, marking packet as invalid.", (char *)(body + last_str_buf_idx + 1)));1119} else {1120DEBUG_LOG_WAYLAND_EMBED("Unknown interface, marking packet as invalid.");1121}1122valid = false;1123break;1124}11251126if (info->direction == ProxyDirection::COMPOSITOR) {1127// FIXME: Create objects only if the packet is valid.1128uint32_t new_local_id = arg;1129body[buf_idx] = client->new_object(new_local_id, new_interface, new_version);11301131if (body[buf_idx] == INVALID_ID) {1132valid = false;1133break;1134}11351136} else if (info->direction == ProxyDirection::CLIENT) {1137uint32_t new_global_id = arg;11381139if (client) {1140body[buf_idx] = client->new_server_object(new_global_id, new_interface, new_version);1141} else {1142new_server_object(new_global_id, new_interface, new_version);1143}11441145if (body[buf_idx] == INVALID_ID) {1146valid = false;1147break;1148}1149}1150} break;11511152case 'o': {1153if (!client) {1154break;1155}11561157uint32_t obj_id = body[buf_idx];1158if (obj_id == 0) {1159// Object arguments can be nil.1160break;1161}11621163if (info->direction == ProxyDirection::CLIENT) {1164if (!client->local_ids.has(obj_id)) {1165DEBUG_LOG_WAYLAND_EMBED(vformat("Object argument g0x%x not found, marking packet as invalid.", obj_id));1166valid = false;1167break;1168}1169body[buf_idx] = instance_id != INVALID_ID ? instance_id : client->get_local_id(obj_id);1170} else if (info->direction == ProxyDirection::COMPOSITOR) {1171if (!client->global_ids.has(obj_id)) {1172DEBUG_LOG_WAYLAND_EMBED(vformat("Object argument l0x%x not found, marking packet as invalid.", obj_id));1173valid = false;1174break;1175}1176body[buf_idx] = client->get_global_id(obj_id);1177}1178} break;1179}11801181++arg_idx;1182++buf_idx;1183}11841185return valid;1186}11871188WaylandEmbedder::MessageStatus WaylandEmbedder::handle_request(LocalObjectHandle p_object, uint32_t p_opcode, const uint32_t *msg_data, size_t msg_len) {1189ERR_FAIL_COND_V(!p_object.is_valid(), MessageStatus::HANDLED);11901191WaylandObject *object = p_object.get();1192Client *client = p_object.get_client();11931194ERR_FAIL_NULL_V(object, MessageStatus::HANDLED);11951196// NOTE: Global ID may be null.1197uint32_t global_id = p_object.get_global_id();1198uint32_t local_id = p_object.get_local_id();11991200ERR_FAIL_NULL_V(object->interface, MessageStatus::ERROR);1201const struct wl_interface *interface = object->interface;12021203ERR_FAIL_COND_V((int)p_opcode >= interface->method_count, MessageStatus::ERROR);1204const struct wl_message message = interface->methods[p_opcode];12051206DEBUG_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));12071208const uint32_t *body = msg_data + 2;12091210if (registry_globals_names.has(global_id)) {1211int global_name = registry_globals_names[global_id];1212ERR_FAIL_COND_V(!registry_globals.has(global_name), MessageStatus::ERROR);1213RegistryGlobalInfo &global_info = registry_globals[global_name];12141215if (global_info.destroyed) {1216DEBUG_LOG_WAYLAND_EMBED("Skipping request for destroyed global object");1217return MessageStatus::HANDLED;1218}1219}12201221if (object->interface == &wl_display_interface && p_opcode == WL_DISPLAY_GET_REGISTRY) {1222// The gist of this is that the registry is a global and the compositor can1223// quite simply take for granted that a single client can access any global1224// bound from any registry. Let's remove all doubts by using a single1225// registry (also for efficiency) and doing fancy remaps.1226uint32_t local_registry_id = body[0];12271228// Note that the registry has already been allocated in the initialization1229// routine.12301231for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {1232uint32_t global_name = pair.key;1233RegistryGlobalInfo &global_info = pair.value;12341235if (global_info.destroyed) {1236continue;1237}12381239const struct wl_interface *global_interface = global_info.interface;12401241if (client != main_client && embedded_interface_deny_list.has(global_interface)) {1242DEBUG_LOG_WAYLAND_EMBED(vformat("Skipped global announcement %s for embedded client.", global_interface->name));1243continue;1244}12451246LocalVector<union wl_argument> args;1247args.push_back(wl_arg_uint(global_name));1248args.push_back(wl_arg_string(global_interface->name));1249args.push_back(wl_arg_uint(global_info.version));12501251send_wayland_event(client->socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL, args);1252}12531254client->wl_registry_instances.insert(local_registry_id);1255client->new_global_instance(local_registry_id, REGISTRY_ID, &wl_registry_interface, 1);12561257return MessageStatus::HANDLED;1258}12591260if (object->interface == &wl_registry_interface) {1261if (p_opcode == WL_REGISTRY_BIND) {1262// [Request] wl_registry::bind(usun)1263uint32_t global_name = body[0];1264uint32_t interface_name_len = body[1];1265//const char *interface_name = (const char *)(body + 2);1266uint32_t version = body[2 + wl_array_word_offset(interface_name_len)];1267uint32_t new_local_id_idx = 2 + wl_array_word_offset(interface_name_len) + 1;1268uint32_t new_local_id = body[new_local_id_idx];12691270if (!registry_globals.has(global_name)) {1271socket_error(client->socket, local_id, WL_DISPLAY_ERROR_INVALID_METHOD, vformat("Invalid global object #%d", global_name));1272return MessageStatus::HANDLED;1273}12741275RegistryGlobalInfo &global_info = registry_globals[global_name];1276ERR_FAIL_NULL_V(global_info.interface, MessageStatus::ERROR);12771278version = MIN(global_info.version, version);12791280if (global_info.interface == &godot_embedding_compositor_interface) {1281if (!client->registry_globals_instances.has(global_name)) {1282client->registry_globals_instances[global_name] = {};1283}12841285client->registry_globals_instances[global_name].insert(new_local_id);1286++global_info.instance_counter;1287DEBUG_LOG_WAYLAND_EMBED("Bound embedded compositor interface.");1288client->new_fake_object(new_local_id, &godot_embedding_compositor_interface, 1);1289return MessageStatus::HANDLED;1290}12911292WaylandObject *instance = nullptr;12931294client->registry_globals_instances[global_name].insert(new_local_id);1295++global_info.instance_counter;12961297if (!client->registry_globals_instances.has(global_name)) {1298client->registry_globals_instances[global_name] = {};1299}13001301uint32_t bind_gid = wl_registry_bind(REGISTRY_ID, global_name, version);1302if (bind_gid == INVALID_ID) {1303socket_error(client->socket, local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "Bind failed.");1304return MessageStatus::HANDLED;1305}13061307WaylandObject *bind_obj = get_object(bind_gid);1308if (bind_obj == nullptr) {1309socket_error(client->socket, local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "Bind failed.");1310return MessageStatus::HANDLED;1311}13121313if (!bind_obj->shared) {1314client->bind_global_id(bind_gid, new_local_id);1315instance = bind_obj;1316} else {1317instance = client->new_global_instance(new_local_id, global_info.reusable_objects[version], global_info.interface, version);1318DEBUG_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]));13191320// Some interfaces report their state as soon as they're bound. Since1321// instances are handled by us, we need to track and report the relevant1322// data ourselves.1323if (global_info.interface == &wl_drm_interface) {1324Error err = client->send_wl_drm_state(new_local_id, (WaylandDrmGlobalData *)global_info.data);1325if (err != OK) {1326return MessageStatus::ERROR;1327}1328} else if (global_info.interface == &wl_shm_interface) {1329WaylandShmGlobalData *global_data = (WaylandShmGlobalData *)global_info.data;1330ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);13311332for (uint32_t format : global_data->formats) {1333send_wayland_message(client->socket, new_local_id, WL_SHM_FORMAT, { format });1334}1335}1336}13371338ERR_FAIL_NULL_V(instance, MessageStatus::UNHANDLED);13391340if (global_info.interface == &wl_seat_interface) {1341WaylandSeatInstanceData *new_data = memnew(WaylandSeatInstanceData);1342instance->data = new_data;1343}13441345return MessageStatus::HANDLED;1346}1347}13481349if (object->interface == &wl_compositor_interface && p_opcode == WL_COMPOSITOR_CREATE_SURFACE) {1350uint32_t new_local_id = body[0];13511352WaylandSurfaceData *data = memnew(WaylandSurfaceData);1353data->client = client;13541355uint32_t new_global_id = client->new_object(new_local_id, &wl_surface_interface, object->version, data);1356ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);13571358DEBUG_LOG_WAYLAND_EMBED(vformat("Keeping track of surface l0x%x g0x%x.", new_local_id, new_global_id));13591360send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });1361return MessageStatus::HANDLED;1362}13631364if (object->interface == &wl_surface_interface) {1365WaylandSurfaceData *surface_data = (WaylandSurfaceData *)object->data;1366ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);13671368if (p_opcode == WL_SURFACE_DESTROY) {1369for (uint32_t wl_seat_name : wl_seat_names) {1370WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;1371ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);13721373if (global_seat_data->pointed_surface_id == global_id) {1374global_seat_data->pointed_surface_id = INVALID_ID;1375}13761377if (global_seat_data->focused_surface_id == global_id) {1378global_seat_data->focused_surface_id = INVALID_ID;1379}1380}1381} else if (p_opcode == WL_SURFACE_COMMIT) {1382if (surface_data->role_object_handle.is_valid()) {1383WaylandObject *role_object = surface_data->role_object_handle.get();1384if (role_object && role_object->interface) {1385DEBUG_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()));1386}13871388if (role_object && role_object->interface == &xdg_toplevel_interface) {1389XdgToplevelData *toplevel_data = (XdgToplevelData *)role_object->data;1390ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);1391// xdg shell spec requires clients to first send data and then commit the1392// surface.13931394if (toplevel_data->is_embedded() && !toplevel_data->configured) {1395toplevel_data->configured = true;1396// xdg_surface::configure1397send_wayland_message(client->socket, toplevel_data->xdg_surface_handle.get_local_id(), 0, { serial_counter++ });1398}1399}1400}14011402send_wayland_message(compositor_socket, global_id, p_opcode, {});1403return MessageStatus::HANDLED;1404}1405}14061407if (object->interface == &wl_seat_interface) {1408uint32_t global_seat_name = registry_globals_names[global_id];14091410RegistryGlobalInfo &seat_global_info = registry_globals[global_seat_name];1411WaylandSeatGlobalData *global_data = (WaylandSeatGlobalData *)seat_global_info.data;1412ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);14131414WaylandSeatInstanceData *instance_data = (WaylandSeatInstanceData *)object->data;1415ERR_FAIL_NULL_V(instance_data, MessageStatus::ERROR);14161417if (p_opcode == WL_SEAT_GET_POINTER) {1418ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::ERROR);1419// [Request] wl_seat::get_pointer(n);1420uint32_t new_local_id = body[0];14211422WaylandPointerData *new_data = memnew(WaylandPointerData);1423new_data->wl_seat_id = global_id;14241425uint32_t new_global_id = client->new_object(new_local_id, &wl_pointer_interface, object->version, new_data);1426ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);14271428instance_data->wl_pointer_id = new_global_id;14291430send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });14311432return MessageStatus::HANDLED;1433}14341435if (p_opcode == WL_SEAT_GET_KEYBOARD) {1436ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::ERROR);1437// [Request] wl_seat::get_pointer(n);1438uint32_t new_local_id = body[0];14391440WaylandKeyboardData *new_data = memnew(WaylandKeyboardData);1441new_data->wl_seat_id = global_id;14421443uint32_t new_global_id = client->new_object(new_local_id, &wl_keyboard_interface, object->version, new_data);1444ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);14451446instance_data->wl_keyboard_id = new_global_id;14471448send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });14491450return MessageStatus::HANDLED;1451}1452}14531454if (object->interface == &xdg_wm_base_interface) {1455if (p_opcode == XDG_WM_BASE_CREATE_POSITIONER) {1456uint32_t new_local_id = body[0];1457uint32_t new_global_id = client->new_object(new_local_id, &xdg_positioner_interface, object->version, memnew(XdgPositionerData));1458ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);14591460send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });1461return MessageStatus::HANDLED;1462}14631464if (p_opcode == XDG_WM_BASE_GET_XDG_SURFACE) {1465// [Request] xdg_wm_base::get_xdg_surface(no).1466uint32_t new_local_id = body[0];1467uint32_t surface_id = body[1];14681469uint32_t global_surface_id = client->get_global_id(surface_id);14701471bool fake = (client != main_client);14721473XdgSurfaceData *data = memnew(XdgSurfaceData);1474data->wl_surface_id = global_surface_id;14751476if (fake) {1477client->new_fake_object(new_local_id, &xdg_surface_interface, object->version, data);1478DEBUG_LOG_WAYLAND_EMBED(vformat("Created fake xdg_surface l0x%x for surface l0x%x", new_local_id, surface_id));1479} else {1480uint32_t new_global_id = client->new_object(new_local_id, &xdg_surface_interface, object->version, data);1481ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);14821483DEBUG_LOG_WAYLAND_EMBED(vformat("Created real xdg_surface l0x%x g0x%x for surface l0x%x", new_local_id, new_global_id, surface_id));14841485send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_surface_id });1486}14871488return MessageStatus::HANDLED;1489}1490}14911492if (object->interface == &xdg_surface_interface) {1493XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)object->data;1494ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);14951496WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(xdg_surf_data->wl_surface_id)->data;1497ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);14981499bool is_embedded = client->fake_objects.has(local_id);15001501if (p_opcode == XDG_SURFACE_GET_POPUP) {1502// [Request] xdg_surface::get_popup(no?o).15031504uint32_t new_local_id = body[0];1505uint32_t local_parent_id = body[1];1506uint32_t local_positioner_id = body[2];15071508surface_data->role_object_handle = LocalObjectHandle(client, new_local_id);15091510XdgPopupData *popup_data = memnew(XdgPopupData);1511popup_data->parent_handle = LocalObjectHandle(client, local_parent_id);15121513if (!is_embedded) {1514uint32_t new_global_id = client->new_object(new_local_id, &xdg_popup_interface, object->version, popup_data);1515ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);15161517uint32_t global_parent_id = client->get_global_id(local_parent_id);1518uint32_t global_positioner_id = client->get_global_id(local_positioner_id);1519send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_parent_id, global_positioner_id });15201521return MessageStatus::HANDLED;1522}15231524{1525// Popups are real, time to actually instantiate an xdg_surface.1526WaylandObject copy = *object;1527client->fake_objects.erase(local_id);15281529global_id = client->new_object(local_id, copy.interface, copy.version, copy.data);1530ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::HANDLED);1531object = get_object(global_id);15321533// xdg_wm_base::get_xdg_surface(no);1534send_wayland_message(compositor_socket, xdg_wm_base_id, 2, { global_id, xdg_surf_data->wl_surface_id });1535}15361537uint32_t new_global_id = client->new_object(new_local_id, &xdg_popup_interface, object->version, popup_data);1538ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);15391540uint32_t global_parent_id = INVALID_ID;1541if (local_parent_id != INVALID_ID) {1542XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)client->get_object(local_parent_id)->data;1543ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);15441545WaylandSurfaceData *parent_surface_data = (WaylandSurfaceData *)get_object(parent_xdg_surf_data->wl_surface_id)->data;1546ERR_FAIL_NULL_V(parent_surface_data, MessageStatus::ERROR);15471548WaylandObject *parent_role_obj = parent_surface_data->role_object_handle.get();1549ERR_FAIL_NULL_V(parent_role_obj, MessageStatus::ERROR);15501551XdgPositionerData *pos_data = (XdgPositionerData *)client->get_object(local_positioner_id)->data;1552ERR_FAIL_NULL_V(pos_data, MessageStatus::ERROR);15531554if (parent_role_obj->interface == &xdg_toplevel_interface) {1555XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)parent_role_obj->data;1556ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);15571558if (parent_toplevel_data->is_embedded()) {1559// Embedded windows are subsurfaces of a parent window. We need to1560// "redirect" the popup request on the parent window and adjust the1561// positioner properly if needed.15621563XdgToplevelData *main_parent_toplevel_data = (XdgToplevelData *)parent_toplevel_data->parent_handle.get()->data;1564ERR_FAIL_NULL_V(main_parent_toplevel_data, MessageStatus::ERROR);15651566global_parent_id = main_parent_toplevel_data->xdg_surface_handle.get_global_id();15671568WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(parent_toplevel_data->wl_subsurface_id)->data;1569ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);15701571Point2i adj_pos = subsurf_data->position + pos_data->anchor_rect.position;15721573// xdg_positioner::set_anchor_rect1574send_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 });1575}1576} else {1577global_parent_id = client->get_global_id(local_parent_id);1578}1579}15801581send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_parent_id, client->get_global_id(local_positioner_id) });1582return MessageStatus::HANDLED;1583}15841585if (p_opcode == XDG_SURFACE_GET_TOPLEVEL) {1586// [Request] xdg_surface::get_toplevel(n).1587uint32_t new_local_id = body[0];15881589surface_data->role_object_handle = LocalObjectHandle(client, new_local_id);15901591XdgToplevelData *data = memnew(XdgToplevelData);1592data->xdg_surface_handle = LocalObjectHandle(client, local_id);15931594if (is_embedded) {1595client->new_fake_object(new_local_id, &xdg_toplevel_interface, object->version, data);1596client->embedded_window_id = new_local_id;15971598// godot_embedded_client::window_embedded()1599send_wayland_message(main_client->socket, client->embedded_client_id, 1, {});1600} else {1601uint32_t new_global_id = client->new_object(new_local_id, &xdg_toplevel_interface, object->version, data);1602ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);16031604if (main_toplevel_id == 0) {1605main_toplevel_id = new_global_id;1606DEBUG_LOG_WAYLAND_EMBED(vformat("main toplevel set to gx0%x.", main_toplevel_id));1607}16081609send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });1610}16111612return MessageStatus::HANDLED;1613}1614}16151616if (object->interface == &xdg_positioner_interface) {1617XdgPositionerData *pos_data = (XdgPositionerData *)object->data;1618ERR_FAIL_NULL_V(pos_data, MessageStatus::ERROR);16191620if (p_opcode == XDG_POSITIONER_SET_ANCHOR_RECT) {1621// Args: int x, int y, int width, int height.1622pos_data->anchor_rect = Rect2i(body[0], body[1], body[2], body[3]);16231624send_wayland_message(compositor_socket, global_id, p_opcode, { body[0], body[1], body[2], body[3] });1625return MessageStatus::HANDLED;1626}1627}16281629if (object->interface == &xdg_toplevel_interface && p_opcode == XDG_TOPLEVEL_DESTROY) {1630if (client->fake_objects.has(local_id)) {1631XdgToplevelData *data = (XdgToplevelData *)object->data;1632ERR_FAIL_NULL_V(data, MessageStatus::ERROR);16331634XdgSurfaceData *xdg_surf_data = nullptr;1635if (data->xdg_surface_handle.is_valid()) {1636xdg_surf_data = (XdgSurfaceData *)data->xdg_surface_handle.get()->data;1637ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);1638}1639ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);16401641XdgSurfaceData *parent_xdg_surf_data = nullptr;1642{1643XdgToplevelData *parent_data = nullptr;1644if (data->parent_handle.get()) {1645parent_data = (XdgToplevelData *)data->parent_handle.get()->data;1646ERR_FAIL_NULL_V(parent_data, MessageStatus::ERROR);1647}16481649if (parent_data && parent_data->xdg_surface_handle.get()) {1650parent_xdg_surf_data = (XdgSurfaceData *)parent_data->xdg_surface_handle.get()->data;1651ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);1652}1653}16541655for (uint32_t wl_seat_name : wl_seat_names) {1656WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;1657ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);16581659if (global_seat_data->focused_surface_id == xdg_surf_data->wl_surface_id) {1660if (xdg_surf_data) {1661seat_name_leave_surface(wl_seat_name, xdg_surf_data->wl_surface_id);1662}16631664if (parent_xdg_surf_data) {1665seat_name_enter_surface(wl_seat_name, parent_xdg_surf_data->wl_surface_id);1666}1667}1668}16691670// wl_display::delete_id1671send_wayland_message(client->socket, local_id, p_opcode, {});16721673if (local_id == client->embedded_window_id) {1674client->embedded_window_id = 0;1675}16761677if (data->wl_subsurface_id != INVALID_ID) {1678send_wayland_message(compositor_socket, data->wl_subsurface_id, WL_SUBSURFACE_DESTROY, {});1679}16801681client->delete_object(local_id);16821683return MessageStatus::HANDLED;1684}1685}16861687if (interface == &zwp_pointer_constraints_v1_interface) {1688// FIXME: This implementation leaves no way of unlocking the pointer when1689// embedded into the main window. We might need to be a bit more invasive.1690if (p_opcode == ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER) {1691// [Request] zwp_pointer_constraints_v1::lock_pointer(nooou).16921693uint32_t new_local_id = body[0];1694uint32_t local_surface_id = body[1];1695uint32_t local_pointer_id = body[2];1696uint32_t lifetime = body[4];16971698WaylandSurfaceData *surf_data = (WaylandSurfaceData *)client->get_object(local_surface_id)->data;1699ERR_FAIL_NULL_V(surf_data, MessageStatus::ERROR);17001701WaylandObject *role_obj = surf_data->role_object_handle.get();1702ERR_FAIL_NULL_V(role_obj, MessageStatus::ERROR);17031704if (role_obj->interface == &xdg_toplevel_interface) {1705XdgToplevelData *toplevel_data = (XdgToplevelData *)role_obj->data;1706ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);17071708if (!toplevel_data->is_embedded()) {1709// Passthrough.1710return MessageStatus::UNHANDLED;1711}17121713// Subsurfaces don't normally work, at least on sway, as the locking1714// condition might rely on focus, which they don't get. We can remap them to1715// the parent surface and set a region though.17161717XdgToplevelData *parent_data = (XdgToplevelData *)toplevel_data->parent_handle.get()->data;1718ERR_FAIL_NULL_V(parent_data, MessageStatus::ERROR);17191720XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)parent_data->xdg_surface_handle.get()->data;1721ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);17221723WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(toplevel_data->wl_subsurface_id)->data;1724ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);17251726uint32_t new_global_id = client->new_object(new_local_id, &zwp_locked_pointer_v1_interface, object->version);1727ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);17281729uint32_t x = subsurf_data->position.x;1730uint32_t y = subsurf_data->position.y;1731uint32_t width = toplevel_data->size.width;1732uint32_t height = toplevel_data->size.height;17331734// NOTE: At least on sway I can't seem to be able to get this region1735// working but the calls check out.1736DEBUG_LOG_WAYLAND_EMBED(vformat("Creating custom region x%d y%d w%d h%d", x, y, width, height));17371738uint32_t new_region_id = allocate_global_id();1739get_object(new_region_id)->interface = &wl_region_interface;1740get_object(new_region_id)->version = get_object(wl_compositor_id)->version;17411742// wl_compostor::create_region(n).1743send_wayland_message(compositor_socket, wl_compositor_id, 1, { new_region_id });17441745// wl_region::add(iiii).1746send_wayland_message(compositor_socket, new_region_id, 1, { x, y, width, height });17471748send_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 });17491750// wl_region::destroy().1751send_wayland_message(compositor_socket, new_region_id, 0, {});17521753return MessageStatus::HANDLED;1754}1755}1756}17571758if (interface == &godot_embedded_client_interface) {1759EmbeddedClientData *eclient_data = (EmbeddedClientData *)object->data;1760ERR_FAIL_NULL_V(eclient_data, MessageStatus::ERROR);17611762Client *eclient = eclient_data->client;1763ERR_FAIL_NULL_V(eclient, MessageStatus::ERROR);17641765if (p_opcode == GODOT_EMBEDDED_CLIENT_DESTROY) {1766if (!eclient_data->disconnected) {1767close(eclient->socket);1768}17691770client->delete_object(local_id);17711772return MessageStatus::HANDLED;1773}17741775if (eclient_data->disconnected) {1776// Object is inert.1777return MessageStatus::HANDLED;1778}17791780ERR_FAIL_COND_V(eclient->embedded_window_id == 0, MessageStatus::ERROR);17811782XdgToplevelData *toplevel_data = (XdgToplevelData *)eclient->get_object(eclient->embedded_window_id)->data;1783ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);17841785if (p_opcode == GODOT_EMBEDDED_CLIENT_SET_EMBEDDED_WINDOW_RECT && toplevel_data->wl_subsurface_id != INVALID_ID) {1786uint32_t x = body[0];1787uint32_t y = body[1];1788uint32_t width = body[2];1789uint32_t height = body[3];17901791WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(toplevel_data->wl_subsurface_id)->data;1792ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);17931794toplevel_data->size.width = width;1795toplevel_data->size.height = height;17961797subsurf_data->position.x = x;1798subsurf_data->position.y = y;17991800// wl_subsurface::set_position1801send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 1, { x, y });18021803// xdg_toplevel::configure1804send_wayland_message(eclient->socket, eclient->embedded_window_id, 0, { width, height, 0 });18051806// xdg_surface::configure1807send_wayland_message(eclient->socket, toplevel_data->xdg_surface_handle.get_local_id(), 0, { configure_serial_counter++ });18081809return MessageStatus::HANDLED;1810} else if (p_opcode == GODOT_EMBEDDED_CLIENT_SET_EMBEDDED_WINDOW_PARENT) {1811uint32_t main_client_parent_id = body[0];18121813if (toplevel_data->parent_handle.get_local_id() == main_client_parent_id) {1814return MessageStatus::HANDLED;1815}18161817if (main_client_parent_id == INVALID_ID && toplevel_data->wl_subsurface_id != INVALID_ID) {1818// Window hiding logic.18191820// wl_subsurface::destroy()1821send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 0, {});18221823toplevel_data->parent_handle.invalidate();1824toplevel_data->wl_subsurface_id = INVALID_ID;18251826return MessageStatus::HANDLED;1827}18281829XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)client->get_object(main_client_parent_id)->data;1830ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);1831XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)parent_toplevel_data->xdg_surface_handle.get()->data;1832ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);18331834XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)toplevel_data->xdg_surface_handle.get()->data;1835ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);18361837if (toplevel_data->wl_subsurface_id != INVALID_ID) {1838// wl_subsurface::destroy()1839send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 0, {});1840}18411842uint32_t new_sub_id = allocate_global_id();1843WaylandObject *new_sub_object = get_object(new_sub_id);1844new_sub_object->interface = &wl_subsurface_interface;1845new_sub_object->data = memnew(WaylandSubsurfaceData);1846new_sub_object->version = get_object(wl_subcompositor_id)->version;18471848toplevel_data->wl_subsurface_id = new_sub_id;1849toplevel_data->parent_handle = LocalObjectHandle(main_client, main_client_parent_id);18501851DEBUG_LOG_WAYLAND_EMBED(vformat("Binding subsurface g0x%x.", new_sub_id));18521853// wl_subcompositor::get_subsurface1854send_wayland_message(compositor_socket, wl_subcompositor_id, 1, { new_sub_id, xdg_surf_data->wl_surface_id, parent_xdg_surf_data->wl_surface_id });18551856// wl_subsurface::set_desync1857send_wayland_message(compositor_socket, new_sub_id, 5, {});18581859return MessageStatus::HANDLED;1860} else if (p_opcode == GODOT_EMBEDDED_CLIENT_FOCUS_WINDOW) {1861XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)toplevel_data->xdg_surface_handle.get()->data;1862ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);18631864for (uint32_t wl_seat_name : wl_seat_names) {1865RegistryGlobalInfo &global_seat_info = registry_globals[wl_seat_name];1866WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;18671868if (global_seat_data->focused_surface_id != INVALID_ID) {1869seat_name_leave_surface(wl_seat_name, global_seat_data->focused_surface_id);1870}1871global_seat_data->focused_surface_id = xdg_surf_data->wl_surface_id;18721873seat_name_enter_surface(wl_seat_name, xdg_surf_data->wl_surface_id);1874}1875} else if (p_opcode == GODOT_EMBEDDED_CLIENT_EMBEDDED_WINDOW_REQUEST_CLOSE) {1876// xdg_toplevel::close1877send_wayland_message(eclient->socket, eclient->embedded_window_id, 1, {});18781879return MessageStatus::HANDLED;1880}1881}18821883// Server-allocated objects are a bit annoying to handle for us. Right now we1884// use a heuristic. See: https://ppaalanen.blogspot.com/2014/07/wayland-protocol-design-object-lifespan.html1885if (strcmp(message.name, "destroy") == 0 || strcmp(message.name, "release") == 0) {1886if (object->shared) {1887// We must not delete shared objects.1888client->delete_object(local_id);1889return MessageStatus::HANDLED;1890}18911892if (global_id != INVALID_ID) {1893send_wayland_message(compositor_socket, global_id, p_opcode, {});1894object->destroyed = true;1895}18961897if (local_id & 0xff000000) {1898DEBUG_LOG_WAYLAND_EMBED(vformat("!!!!!! Deallocating server object l0x%x", local_id));1899client->delete_object(local_id);1900}19011902return MessageStatus::HANDLED;1903}19041905if (client->fake_objects.has(local_id)) {1906// Object is fake, we're done.1907DEBUG_LOG_WAYLAND_EMBED("Dropping unhandled request for fake object.");1908return MessageStatus::HANDLED;1909}19101911if (global_id == INVALID_ID) {1912DEBUG_LOG_WAYLAND_EMBED("Dropping request with invalid global object id");1913return MessageStatus::HANDLED;1914}19151916return MessageStatus::UNHANDLED;1917}19181919WaylandEmbedder::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) {1920WaylandObject *global_object = get_object(p_global_id);1921ERR_FAIL_NULL_V_MSG(global_object, MessageStatus::ERROR, "Compositor messages must always have a global object.");19221923#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED1924ERR_FAIL_NULL_V(global_object->interface, MessageStatus::ERROR);1925const struct wl_interface *interface = global_object->interface;19261927ERR_FAIL_COND_V((int)p_opcode >= interface->event_count, MessageStatus::ERROR);1928const struct wl_message message = interface->events[p_opcode];19291930if (p_local_handle.is_valid()) {1931int socket = p_local_handle.get_client()->socket;1932DEBUG_LOG_WAYLAND_EMBED(vformat("Client #%d <- %s::%s(%s) g0x%x", socket, interface->name, message.name, message.signature, p_global_id));1933} else {1934DEBUG_LOG_WAYLAND_EMBED(vformat("Client N/A <- %s::%s(%s) g0x%x", interface->name, message.name, message.signature, p_global_id));1935}1936#endif //WAYLAND_EMBED_DEBUG_LOGS_ENABLED19371938const uint32_t *body = msg_data + 2;1939//size_t body_len = msg_len - (WL_WORD_SIZE * 2);19401941// FIXME: Make sure that it makes sense to track this protocol. Not only is it1942// old and getting deprecated, but I can't even get this code branch to hit1943// probably because, at the time of writing, we only get the "main" display1944// through the proxy.1945if (global_object->interface == &wl_drm_interface) {1946// wl_drm can't ever be destroyed, so we need to track its state as it's going1947// to be instanced at least few times.1948uint32_t global_name = registry_globals_names[p_global_id];1949WaylandDrmGlobalData *global_data = (WaylandDrmGlobalData *)registry_globals[global_name].data;1950ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);19511952if (p_opcode == WL_DRM_DEVICE) {1953// signature: s1954uint32_t name_len = body[0];1955uint8_t *name = (uint8_t *)(body + 1);1956global_data->device = String::utf8((const char *)name, name_len);19571958return MessageStatus::UNHANDLED;1959}19601961if (p_opcode == WL_DRM_FORMAT) {1962// signature: u1963uint32_t format = body[0];1964global_data->formats.push_back(format);19651966return MessageStatus::UNHANDLED;1967}19681969if (p_opcode == WL_DRM_AUTHENTICATED) {1970// signature: N/A1971global_data->authenticated = true;19721973return MessageStatus::UNHANDLED;1974}19751976if (p_opcode == WL_DRM_CAPABILITIES) {1977// signature: u1978uint32_t capabilities = body[0];1979global_data->capabilities = capabilities;1980}19811982return MessageStatus::UNHANDLED;1983}19841985if (global_object->interface == &wl_shm_interface) {1986uint32_t global_name = registry_globals_names[p_global_id];1987WaylandShmGlobalData *global_data = (WaylandShmGlobalData *)registry_globals[global_name].data;1988ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);19891990if (p_opcode == WL_SHM_FORMAT) {1991// Signature: u1992uint32_t format = body[0];1993global_data->formats.push_back(format);1994}1995}19961997if (!p_local_handle.is_valid()) {1998// Some requests might not have a valid local object handle for various1999// reasons, such as when certain events are directed to this proxy or when the2000// destination client of a message disconnected in the meantime.20012002if (global_object->interface == &wl_display_interface) {2003if (p_opcode == WL_DISPLAY_DELETE_ID) {2004// [Event] wl_display::delete_id(u)2005uint32_t global_delete_id = body[0];2006DEBUG_LOG_WAYLAND_EMBED(vformat("Compositor requested deletion of g0x%x (no client)", global_delete_id));20072008delete_object(global_delete_id);20092010return MessageStatus::HANDLED;2011} else if (p_opcode == WL_DISPLAY_ERROR) {2012// [Event] wl_display::error(ous)2013uint32_t obj_id = body[0];2014uint32_t err_code = body[1];20152016CRASH_NOW_MSG(vformat("Error obj g0x%x code %d: %s", obj_id, err_code, (const char *)(body + 3)));2017}2018}20192020if (global_object->interface == &wl_callback_interface && p_opcode == WL_CALLBACK_DONE) {2021if (sync_callback_id != INVALID_ID && p_global_id == sync_callback_id) {2022sync_callback_id = 0;2023DEBUG_LOG_WAYLAND_EMBED("Sync response received");2024return MessageStatus::HANDLED;2025}2026}20272028if (global_object->interface == &wl_registry_interface) {2029if (p_opcode == WL_REGISTRY_GLOBAL) {2030// [Event] wl_registry::global(usu).20312032uint32_t global_name = body[0];2033uint32_t interface_name_len = body[1];2034const char *interface_name = (const char *)(body + 2);2035uint32_t global_version = body[2 + wl_array_word_offset(interface_name_len)];20362037DEBUG_LOG_WAYLAND_EMBED("Global c#%d %s %d", global_name, interface_name, global_version);20382039const struct wl_interface *global_interface = wl_interface_from_string(interface_name, interface_name_len);2040if (global_interface) {2041RegistryGlobalInfo global_info = {};2042global_info.interface = global_interface;2043global_info.version = MIN(global_version, (uint32_t)global_interface->version);2044DEBUG_LOG_WAYLAND_EMBED("Clamped global %s to version %d.", interface_name, global_info.version);2045global_info.compositor_name = global_name;20462047int new_global_name = registry_globals_counter++;20482049if (global_info.interface == &wl_shm_interface) {2050DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_shm data.");2051global_info.data = memnew(WaylandShmGlobalData);2052}20532054if (global_info.interface == &wl_seat_interface) {2055DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_seat data.");2056global_info.data = memnew(WaylandSeatGlobalData);2057wl_seat_names.push_back(new_global_name);2058}20592060if (global_info.interface == &wl_drm_interface) {2061DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_drm data.");2062global_info.data = memnew(WaylandDrmGlobalData);2063}20642065registry_globals[new_global_name] = global_info;20662067// We need some interfaces directly. It's better to bind a "copy" ourselves2068// than to wait for the client to ask one.2069if (global_interface == &xdg_wm_base_interface && xdg_wm_base_id == 0) {2070xdg_wm_base_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);2071ERR_FAIL_COND_V(xdg_wm_base_id == INVALID_ID, MessageStatus::ERROR);2072} else if (global_interface == &wl_compositor_interface && wl_compositor_id == 0) {2073wl_compositor_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);2074ERR_FAIL_COND_V(wl_compositor_id == INVALID_ID, MessageStatus::ERROR);2075} else if (global_interface == &wl_subcompositor_interface && wl_subcompositor_id == 0) {2076wl_subcompositor_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);2077ERR_FAIL_COND_V(wl_subcompositor_id == INVALID_ID, MessageStatus::ERROR);2078}20792080DEBUG_LOG_WAYLAND_EMBED(vformat("Local registry object name: l#%d", new_global_name));20812082if (clients.is_empty()) {2083// Let's not waste time.2084return MessageStatus::HANDLED;2085}20862087// Notify all clients.2088LocalVector<wl_argument> args;2089args.push_back(wl_arg_uint(new_global_name));2090args.push_back(wl_arg_string(interface_name));2091args.push_back(wl_arg_uint(global_info.version));2092for (KeyValue<int, Client> &pair : clients) {2093Client &client = pair.value;2094for (uint32_t local_registry_id : client.wl_registry_instances) {2095send_wayland_event(client.socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL, args);2096}2097}20982099return MessageStatus::HANDLED;2100} else {2101DEBUG_LOG_WAYLAND_EMBED("Skipping unknown global %s version %d.", interface_name, global_version);21022103return MessageStatus::HANDLED;2104}2105} else if (p_opcode == WL_REGISTRY_GLOBAL_REMOVE) {2106uint32_t compositor_name = body[0];2107uint32_t local_name = 0;2108RegistryGlobalInfo *global_info = nullptr;21092110// FIXME: Use a map or something.2111for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {2112uint32_t name = pair.key;2113RegistryGlobalInfo &info = pair.value;21142115if (info.compositor_name == compositor_name) {2116local_name = name;2117global_info = &info;2118break;2119}2120}21212122ERR_FAIL_NULL_V(global_info, MessageStatus::ERROR);21232124if (global_info->instance_counter == 0) {2125memdelete(global_info->data);2126registry_globals.erase(local_name);2127} else {2128global_info->destroyed = true;2129}21302131// Notify all clients.2132LocalVector<wl_argument> args;2133args.push_back(wl_arg_uint(local_name));2134for (KeyValue<int, Client> &pair : clients) {2135Client &client = pair.value;2136for (uint32_t local_registry_id : client.wl_registry_instances) {2137send_wayland_event(client.socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL_REMOVE, args);2138}2139}21402141return MessageStatus::HANDLED;2142}2143}21442145DEBUG_LOG_WAYLAND_EMBED("No valid local object handle, falling back to generic handler.");2146return MessageStatus::UNHANDLED;2147}21482149Client *client = p_local_handle.get_client();21502151ERR_FAIL_NULL_V(client, MessageStatus::ERROR);21522153WaylandObject *object = p_local_handle.get();2154uint32_t local_id = p_local_handle.get_local_id();21552156if (global_object->interface == &wl_display_interface) {2157if (p_opcode == WL_DISPLAY_DELETE_ID) {2158// [Event] wl_display::delete_id(u)2159uint32_t global_delete_id = body[0];2160uint32_t local_delete_id = client->get_local_id(global_delete_id);2161DEBUG_LOG_WAYLAND_EMBED(vformat("Compositor requested delete of g0x%x l0x%x", global_delete_id, local_delete_id));2162if (local_delete_id == INVALID_ID) {2163// No idea what this object is, might be of the other client. This2164// definitely does not make sense to us, so we're done.2165return MessageStatus::INVALID;2166}21672168client->delete_object(local_delete_id);21692170send_wayland_message(client->socket, DISPLAY_ID, WL_DISPLAY_DELETE_ID, { local_delete_id });21712172return MessageStatus::HANDLED;2173}21742175return MessageStatus::UNHANDLED;2176}21772178if (object->interface == &wl_keyboard_interface) {2179WaylandKeyboardData *data = (WaylandKeyboardData *)object->data;2180ERR_FAIL_NULL_V(data, MessageStatus::ERROR);21812182uint32_t global_seat_name = registry_globals_names[data->wl_seat_id];2183RegistryGlobalInfo &global_seat_info = registry_globals[global_seat_name];2184WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;2185ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);21862187if (p_opcode == WL_KEYBOARD_ENTER) {2188// [Event] wl_keyboard::enter(uoa)2189uint32_t surface = body[1];21902191if (global_seat_data->focused_surface_id != surface) {2192DEBUG_LOG_WAYLAND_EMBED(vformat("Focused g0x%x", surface));2193global_seat_data->focused_surface_id = surface;2194}2195} else if (p_opcode == WL_KEYBOARD_LEAVE) {2196// [Event] wl_keyboard::leave(uo)2197uint32_t surface = body[1];21982199if (global_seat_data->focused_surface_id == surface) {2200global_seat_data->focused_surface_id = INVALID_ID;2201}2202} else if (p_opcode == WL_KEYBOARD_KEY) {2203// NOTE: modifiers event can be sent even without focus, according to the2204// spec, so there's no need to skip it.2205if (global_seat_data->focused_surface_id != INVALID_ID && !client->local_ids.has(global_seat_data->focused_surface_id)) {2206DEBUG_LOG_WAYLAND_EMBED(vformat("Skipped wl_keyboard event due to unfocused surface 0x%x", global_seat_data->focused_surface_id));2207return MessageStatus::HANDLED;2208}2209}22102211return MessageStatus::UNHANDLED;2212}22132214if (object->interface == &wl_pointer_interface) {2215WaylandPointerData *data = (WaylandPointerData *)object->data;2216ERR_FAIL_NULL_V(data, MessageStatus::ERROR);22172218uint32_t global_seat_name = registry_globals_names[data->wl_seat_id];2219RegistryGlobalInfo &global_seat_info = registry_globals[global_seat_name];2220WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;2221ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);22222223WaylandSeatInstanceData *seat_data = (WaylandSeatInstanceData *)object->data;2224ERR_FAIL_NULL_V(seat_data, MessageStatus::ERROR);22252226if (p_opcode == WL_POINTER_BUTTON && global_seat_data->pointed_surface_id != INVALID_ID) {2227// [Event] wl_pointer::button(uuuu);2228uint32_t button = body[2];2229uint32_t state = body[3];22302231DEBUG_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));22322233bool client_pointed = client->local_ids.has(global_seat_data->pointed_surface_id);22342235if (button != BTN_LEFT || state != WL_POINTER_BUTTON_STATE_RELEASED) {2236return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;2237}22382239if (global_seat_data->focused_surface_id == global_seat_data->pointed_surface_id) {2240return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;2241}22422243if (!global_surface_is_window(global_seat_data->pointed_surface_id)) {2244return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;2245}22462247if (global_seat_data->focused_surface_id != INVALID_ID) {2248seat_name_leave_surface(global_seat_name, global_seat_data->focused_surface_id);2249}22502251global_seat_data->focused_surface_id = global_seat_data->pointed_surface_id;2252seat_name_enter_surface(global_seat_name, global_seat_data->focused_surface_id);2253} else if (p_opcode == WL_POINTER_ENTER) {2254// [Event] wl_pointer::enter(uoff).2255uint32_t surface = body[1];2256WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(surface)->data;2257ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);22582259if (global_seat_data->pointed_surface_id != surface) {2260DEBUG_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));22612262global_seat_data->pointed_surface_id = surface;2263}2264} else if (p_opcode == WL_POINTER_LEAVE) {2265// [Event] wl_pointer::leave(uo).2266uint32_t surface = body[1];2267WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(surface)->data;2268ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);22692270if (global_seat_data->pointed_surface_id == surface) {2271DEBUG_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));2272global_seat_data->pointed_surface_id = INVALID_ID;2273}2274}22752276return MessageStatus::UNHANDLED;2277}22782279if (object->interface == &xdg_popup_interface) {2280if (p_opcode == XDG_POPUP_CONFIGURE) {2281// [Event] xdg_popup::configure(iiii);2282int32_t x = body[0];2283int32_t y = body[1];2284int32_t width = body[2];2285int32_t height = body[3];22862287XdgPopupData *data = (XdgPopupData *)object->data;2288ERR_FAIL_NULL_V(data, MessageStatus::ERROR);22892290XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)data->parent_handle.get()->data;2291ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);22922293WaylandSurfaceData *parent_surface_data = (WaylandSurfaceData *)get_object(parent_xdg_surf_data->wl_surface_id)->data;2294ERR_FAIL_NULL_V(parent_surface_data, MessageStatus::ERROR);22952296WaylandObject *parent_role_obj = parent_surface_data->role_object_handle.get();2297ERR_FAIL_NULL_V(parent_role_obj, MessageStatus::ERROR);22982299if (parent_role_obj->interface == &xdg_toplevel_interface) {2300XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)parent_role_obj->data;2301ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);23022303if (parent_toplevel_data->is_embedded()) {2304WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(parent_toplevel_data->wl_subsurface_id)->data;2305ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);23062307// The coordinates passed will be shifted by the embedded window position,2308// so we need to fix them back.2309Point2i fixed_position = Point2i(x, y) - subsurf_data->position;23102311DEBUG_LOG_WAYLAND_EMBED(vformat("Correcting popup configure position to %s", fixed_position));23122313send_wayland_message(client->socket, local_id, p_opcode, { (uint32_t)fixed_position.x, (uint32_t)fixed_position.y, (uint32_t)width, (uint32_t)height });23142315return MessageStatus::HANDLED;2316}2317}2318}2319}23202321return MessageStatus::UNHANDLED;2322}23232324void WaylandEmbedder::shutdown() {2325thread_done.set();23262327{2328// First making a list of all clients so that we can iteratively delete them.2329LocalVector<int> sockets;2330for (KeyValue<int, Client> &pair : clients) {2331sockets.push_back(pair.key);2332}23332334for (int socket : sockets) {2335cleanup_socket(socket);2336}2337}23382339close(compositor_socket);2340compositor_socket = -1;23412342for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {2343RegistryGlobalInfo &info = pair.value;2344if (info.data) {2345memdelete(info.data);2346info.data = nullptr;2347}2348}2349}23502351Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *info, uint32_t *buf, int *fds_requested) {2352ERR_FAIL_NULL_V(info, ERR_BUG);2353ERR_FAIL_NULL_V(fds_requested, ERR_BUG);2354ERR_FAIL_NULL_V_MSG(info->direction == ProxyDirection::COMPOSITOR && client, ERR_BUG, "Wait, where did this message come from?");23552356*fds_requested = 0;23572358WaylandObject *object = nullptr;23592360uint32_t global_id = INVALID_ID;2361if (info->direction == ProxyDirection::CLIENT) {2362global_id = info->raw_id;2363} else if (info->direction == ProxyDirection::COMPOSITOR) {2364global_id = client->get_global_id(info->raw_id);2365}23662367if (global_id != INVALID_ID) {2368object = get_object(global_id);2369} else if (client) {2370object = client->get_object(info->raw_id);2371}23722373if (object == nullptr) {2374if (info->direction == ProxyDirection::COMPOSITOR) {2375uint32_t local_id = info->raw_id;2376ERR_PRINT(vformat("Couldn't find requested object l0x%x for client %d, disconnecting.", local_id, client->socket));23772378socket_error(client->socket, local_id, WL_DISPLAY_ERROR_INVALID_OBJECT, vformat("Object l0x%x not found.", local_id));2379return OK;2380} else {2381CRASH_NOW_MSG(vformat("No object found for r0x%x", info->raw_id));2382}2383}23842385const struct wl_interface *interface = nullptr;2386interface = object->interface;23872388if (interface == nullptr && info->raw_id & 0xff000000) {2389// Regular clients have no confirmation about deleted server objects (why2390// should they?) but since we share connections there's the risk of receiving2391// messages about deleted server objects. The simplest solution is to ignore2392// unknown server-side objects. Not the safest thing, I know, but it should do2393// the job.2394DEBUG_LOG_WAYLAND_EMBED(vformat("Ignoring unknown server-side object r0x%x", info->raw_id));2395return OK;2396}23972398ERR_FAIL_NULL_V_MSG(interface, ERR_BUG, vformat("Object r0x%x has no interface", info->raw_id));23992400const struct wl_message *message = nullptr;2401if (info->direction == ProxyDirection::CLIENT) {2402ERR_FAIL_COND_V(info->opcode >= interface->event_count, ERR_BUG);2403message = &interface->events[info->opcode];2404} else {2405ERR_FAIL_COND_V(info->opcode >= interface->method_count, ERR_BUG);2406message = &interface->methods[info->opcode];2407}2408ERR_FAIL_NULL_V(message, ERR_BUG);24092410*fds_requested = String(message->signature).count("h");2411LocalVector<int> sent_fds;24122413if (*fds_requested > 0) {2414DEBUG_LOG_WAYLAND_EMBED(vformat("Requested %d FDs.", *fds_requested));24152416List<int> &fd_queue = info->direction == ProxyDirection::COMPOSITOR ? client->fds : compositor_fds;2417for (int i = 0; i < *fds_requested; ++i) {2418ERR_FAIL_COND_V_MSG(fd_queue.is_empty(), ERR_BUG, "Out of FDs.");2419DEBUG_LOG_WAYLAND_EMBED(vformat("Fetching FD %d.", fd_queue.front()->get()));2420sent_fds.push_back(fd_queue.front()->get());2421fd_queue.pop_front();2422}24232424DEBUG_LOG_WAYLAND_EMBED(vformat("Remaining FDs: %d.", fd_queue.size()));2425}24262427if (object->destroyed) {2428DEBUG_LOG_WAYLAND_EMBED("Ignoring message for inert object.");2429// Inert object.2430return OK;2431}24322433if (info->direction == ProxyDirection::COMPOSITOR) {2434MessageStatus request_status = handle_request(LocalObjectHandle(client, info->raw_id), info->opcode, buf, info->size);2435if (request_status == MessageStatus::ERROR) {2436return ERR_BUG;2437}24382439if (request_status == MessageStatus::HANDLED) {2440DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");2441return OK;2442}24432444if (global_id != INVALID_ID) {2445buf[0] = global_id;2446}24472448DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");24492450if (handle_generic_msg(client, object, message, info, buf)) {2451send_raw_message(compositor_socket, { { buf, info->size } }, sent_fds);2452}2453} else {2454uint32_t global_name = 0;24552456bool is_global = false;2457if (registry_globals_names.has(global_id)) {2458global_name = registry_globals_names[global_id];2459is_global = true;2460}24612462// FIXME: For compatibility, mirror events with instanced registry globals as2463// object arguments. For example, `wl_surface.enter` returns a `wl_output`. If2464// said `wl_output` has been instanced multiple times, we need to resend the2465// same event with each instance as the argument, or the client might miss the2466// event by looking for the "wrong" instance.2467//2468// Note that this missing behavior is exclusively a compatibility mechanism2469// for old compositors which only implement undestroyable globals. We2470// otherwise passthrough every bind request and then the compositor takes care2471// of everything.2472// See: https://lore.freedesktop.org/wayland-devel/[email protected]/2473if (object->shared) {2474bool handled = false;24752476for (KeyValue<int, Client> &pair : clients) {2477Client &c = pair.value;2478if (c.socket < 0) {2479continue;2480}24812482if (!c.local_ids.has(global_id)) {2483DEBUG_LOG_WAYLAND_EMBED("!!!!!!!!!!! Instance missing?");2484continue;2485}24862487if (is_global) {2488if (!c.registry_globals_instances.has(global_name)) {2489continue;2490}24912492DEBUG_LOG_WAYLAND_EMBED(vformat("Broadcasting to all global instances for client %d (socket %d)", c.pid, c.socket));2493for (uint32_t instance_id : c.registry_globals_instances[global_name]) {2494DEBUG_LOG_WAYLAND_EMBED(vformat("Global instance l0x%x", instance_id));24952496LocalObjectHandle local_obj = LocalObjectHandle(&c, instance_id);2497if (!local_obj.is_valid()) {2498continue;2499}25002501MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);25022503if (event_status == MessageStatus::ERROR) {2504return ERR_BUG;2505}25062507if (event_status == MessageStatus::HANDLED) {2508DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");2509handled = true;2510continue;2511}25122513if (event_status == MessageStatus::INVALID) {2514continue;2515}25162517DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");25182519buf[0] = instance_id;25202521if (handle_generic_msg(&c, local_obj.get(), message, info, buf, instance_id)) {2522send_raw_message(c.socket, { { buf, info->size } }, sent_fds);2523}25242525handled = true;2526}2527} else if (interface == &wl_display_interface) {2528// NOTE: The only shared non-global objects are `wl_display` and2529// `wl_registry`, both of which require custom handlers. Additionally, of2530// those only `wl_display` has client-specific handlers, which is what this2531// branch manages.25322533LocalObjectHandle local_obj = LocalObjectHandle(&c, c.get_local_id(global_id));2534if (!local_obj.is_valid()) {2535continue;2536}25372538DEBUG_LOG_WAYLAND_EMBED(vformat("Shared non-global l0x%x g0x%x", c.get_local_id(global_id), global_id));25392540MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);2541if (event_status == MessageStatus::ERROR) {2542return ERR_BUG;2543}25442545if (event_status == MessageStatus::HANDLED) {2546DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");2547handled = true;2548continue;2549}25502551if (event_status == MessageStatus::INVALID) {2552continue;2553}25542555DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");25562557if (handle_generic_msg(&c, local_obj.get(), message, info, buf)) {2558send_raw_message(c.socket, { { buf, info->size } }, sent_fds);2559}25602561handled = true;2562}2563}25642565if (!handled) {2566// No client handled this, it's going to be handled as a client-less event.2567// We do this only at the end to avoid handling certain events (e.g.2568// deletion) twice.2569handle_event(global_id, LocalObjectHandle(nullptr, INVALID_ID), info->opcode, buf, info->size);2570}2571} else {2572LocalObjectHandle local_obj = LocalObjectHandle(client, client ? client->get_local_id(global_id) : INVALID_ID);25732574MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);2575if (event_status == MessageStatus::ERROR) {2576return ERR_BUG;2577}25782579if (event_status == MessageStatus::HANDLED || event_status == MessageStatus::INVALID) {2580// We're done.2581return OK;2582}25832584// Generic passthrough.25852586if (client) {2587uint32_t local_id = client->get_local_id(global_id);2588ERR_FAIL_COND_V(local_id == INVALID_ID, OK);25892590DEBUG_LOG_WAYLAND_EMBED(vformat("%s::%s(%s) g0x%x -> l0x%x", interface->name, message->name, message->signature, global_id, local_id));2591buf[0] = local_id;25922593if (handle_generic_msg(client, local_obj.get(), message, info, buf)) {2594send_raw_message(client->socket, { { buf, info->size } }, sent_fds);2595}2596} else {2597WARN_PRINT_ONCE(vformat("[Wayland Embedder] Unexpected client-less event from %s#g0x%x. Object has probably leaked.", object->interface->name, global_id));2598handle_generic_msg(nullptr, object, message, info, buf);2599}2600}2601}26022603for (int fd : sent_fds) {2604DEBUG_LOG_WAYLAND_EMBED(vformat("Closing fd %d.", fd));2605close(fd);2606}26072608return OK;2609}26102611Error WaylandEmbedder::handle_sock(int p_fd) {2612ERR_FAIL_COND_V(p_fd < 0, ERR_INVALID_PARAMETER);26132614struct msg_info info = {};26152616{2617struct msghdr head_msg = {};2618uint32_t header[2];2619struct iovec vec = { header, sizeof header };26202621head_msg.msg_iov = &vec;2622head_msg.msg_iovlen = 1;26232624ssize_t head_rec = recvmsg(p_fd, &head_msg, MSG_PEEK);26252626if (head_rec == 0) {2627// Client disconnected.2628return ERR_CONNECTION_ERROR;2629}26302631if (head_rec == -1) {2632if (errno == ECONNRESET) {2633// No need to print the error, the client forcefully disconnected, that's2634// fine.2635return ERR_CONNECTION_ERROR;2636}26372638ERR_FAIL_V_MSG(FAILED, vformat("Can't read message header: %s", strerror(errno)));2639}26402641ERR_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));26422643// Header is two 32-bit words: first is ID, second has size in most significant2644// half and opcode in the other half.2645info.raw_id = header[0];2646info.size = header[1] >> 16;2647info.opcode = header[1] & 0xFFFF;2648info.direction = p_fd != compositor_socket ? ProxyDirection::COMPOSITOR : ProxyDirection::CLIENT;2649}26502651if (msg_buf.size() < info.words()) {2652msg_buf.resize(info.words());2653}26542655ERR_FAIL_COND_V_MSG(info.size % WL_WORD_SIZE != 0, ERR_CONNECTION_ERROR, "Invalid message length.");26562657struct msghdr full_msg = {};2658struct iovec vec = { msg_buf.ptr(), info.size };2659{2660full_msg.msg_iov = &vec;2661full_msg.msg_iovlen = 1;2662full_msg.msg_control = ancillary_buf.ptr();2663full_msg.msg_controllen = ancillary_buf.size();26642665ssize_t full_rec = recvmsg(p_fd, &full_msg, 0);26662667if (full_rec == -1) {2668if (errno == ECONNRESET) {2669// No need to print the error, the client forcefully disconnected, that's2670// fine.2671return ERR_CONNECTION_ERROR;2672}26732674ERR_FAIL_V_MSG(FAILED, vformat("Can't read message: %s", strerror(errno)));2675}26762677ERR_FAIL_COND_V_MSG(((size_t)full_rec) != info.size, ERR_CONNECTION_ERROR, "Invalid message length.");26782679DEBUG_LOG_WAYLAND_EMBED(" === START PACKET === ");26802681#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED2682printf("[PROXY] Received bytes: ");2683for (ssize_t i = 0; i < full_rec; ++i) {2684printf("%.2x", ((const uint8_t *)msg_buf.ptr())[i]);2685}2686printf("\n");2687#endif2688}26892690if (full_msg.msg_controllen > 0) {2691struct cmsghdr *cmsg = CMSG_FIRSTHDR(&full_msg);2692while (cmsg) {2693// TODO: Check for validity of message fields.2694size_t data_len = cmsg->cmsg_len - sizeof *cmsg;26952696if (cmsg->cmsg_type == SCM_RIGHTS) {2697// NOTE: Linux docs say that we can't just cast data to pointer type because2698// of alignment concerns. So we have to memcpy into a new buffer.2699int *cmsg_fds = (int *)malloc(data_len);2700memcpy(cmsg_fds, CMSG_DATA(cmsg), data_len);27012702size_t cmsg_fds_count = data_len / sizeof *cmsg_fds;2703for (size_t i = 0; i < cmsg_fds_count; ++i) {2704int fd = cmsg_fds[i];27052706if (info.direction == ProxyDirection::COMPOSITOR) {2707clients[p_fd].fds.push_back(fd);2708} else {2709compositor_fds.push_back(fd);2710}2711}27122713#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED2714printf("[PROXY] Received %ld file descriptors: ", cmsg_fds_count);2715for (size_t i = 0; i < cmsg_fds_count; ++i) {2716printf("%d ", cmsg_fds[i]);2717}2718printf("\n");2719#endif27202721free(cmsg_fds);2722}27232724cmsg = CMSG_NXTHDR(&full_msg, cmsg);2725}2726}2727full_msg.msg_control = nullptr;2728full_msg.msg_controllen = 0;27292730int fds_requested = 0;27312732Client *client = nullptr;2733if (p_fd == compositor_socket) {2734// Let's figure out the recipient of the message.2735for (KeyValue<int, Client> &pair : clients) {2736Client &c = pair.value;27372738if (c.local_ids.has(info.raw_id)) {2739client = &c;2740}2741}2742} else {2743CRASH_COND(!clients.has(p_fd));2744client = &clients[p_fd];2745}27462747if (handle_msg_info(client, &info, msg_buf.ptr(), &fds_requested) != OK) {2748return ERR_BUG;2749}27502751DEBUG_LOG_WAYLAND_EMBED(" === END PACKET === ");27522753return OK;2754}27552756void WaylandEmbedder::_thread_loop(void *p_data) {2757Thread::set_name("Wayland Embed");27582759ERR_FAIL_NULL(p_data);2760WaylandEmbedder *proxy = (WaylandEmbedder *)p_data;27612762DEBUG_LOG_WAYLAND_EMBED("Proxy thread started");27632764while (!proxy->thread_done.is_set()) {2765proxy->poll_sockets();2766}2767}27682769Error WaylandEmbedder::init() {2770ancillary_buf.resize(EMBED_ANCILLARY_BUF_SIZE);27712772proxy_socket = socket(AF_UNIX, SOCK_STREAM, 0);27732774struct sockaddr_un addr = {};2775addr.sun_family = AF_UNIX;27762777String runtime_dir_path = OS::get_singleton()->get_environment("XDG_RUNTIME_DIR");2778ERR_FAIL_COND_V_MSG(runtime_dir_path.is_empty(), ERR_DOES_NOT_EXIST, "XDG_RUNTIME_DIR is not set or empty.");27792780runtime_dir = DirAccess::create_for_path(runtime_dir_path);2781ERR_FAIL_COND_V(!runtime_dir.is_valid(), ERR_BUG);2782ERR_FAIL_COND_V_MSG(!runtime_dir->is_writable(runtime_dir_path), ERR_FILE_CANT_WRITE, "XDG_RUNTIME_DIR points to an invalid directory.");27832784int socket_id = 0;2785while (socket_path.is_empty()) {2786String test_socket_path = runtime_dir_path + "/godot-wayland-" + itos(socket_id);2787String test_socket_lock_path = test_socket_path + ".lock";27882789print_verbose(vformat("Trying to get socket %s", test_socket_path));2790print_verbose(vformat("Opening lock %s", test_socket_lock_path));2791int test_lock_fd = open(test_socket_lock_path.utf8().get_data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);27922793if (flock(test_lock_fd, LOCK_EX | LOCK_NB) == -1) {2794print_verbose(vformat("Can't lock %s", test_socket_lock_path));2795close(test_lock_fd);2796++socket_id;2797continue;2798} else {2799lock_fd = test_lock_fd;2800socket_path = test_socket_path;2801socket_lock_path = test_socket_lock_path;28022803break;2804}2805}28062807DirAccess::remove_absolute(socket_path);2808strncpy(addr.sun_path, socket_path.utf8().get_data(), sizeof(addr.sun_path) - 1);28092810if (bind(proxy_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {2811ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't bind embedding socket.");2812}28132814if (listen(proxy_socket, 1) == -1) {2815ERR_FAIL_V_MSG(ERR_CANT_OPEN, "Can't listen embedding socket.");2816}28172818struct wl_display *display = wl_display_connect(nullptr);2819ERR_FAIL_NULL_V(display, ERR_CANT_OPEN);2820compositor_socket = wl_display_get_fd(display);28212822pollfds.push_back({ proxy_socket, POLLIN, 0 });2823pollfds.push_back({ compositor_socket, POLLIN, 0 });28242825RegistryGlobalInfo control_global_info = {};2826control_global_info.interface = &godot_embedding_compositor_interface;2827control_global_info.version = godot_embedding_compositor_interface.version;28282829godot_embedding_compositor_name = registry_globals_counter++;2830registry_globals[godot_embedding_compositor_name] = control_global_info;28312832{2833uint32_t invalid_id = INVALID_ID;2834objects.request(invalid_id);28352836CRASH_COND(invalid_id != INVALID_ID);2837}28382839{2840uint32_t display_id = new_object(&wl_display_interface);2841CRASH_COND(display_id != DISPLAY_ID);28422843get_object(DISPLAY_ID)->shared = true;2844}28452846{2847uint32_t registry_id = new_object(&wl_registry_interface);2848CRASH_COND(registry_id != REGISTRY_ID);28492850get_object(REGISTRY_ID)->shared = true;2851}28522853// wl_display::get_registry(n)2854send_wayland_message(compositor_socket, DISPLAY_ID, 1, { REGISTRY_ID });28552856sync();28572858proxy_thread.start(_thread_loop, this);28592860return OK;2861}28622863void WaylandEmbedder::handle_fd(int p_fd, int p_revents) {2864if (p_fd == proxy_socket && p_revents & POLLIN) {2865// Client init.2866int new_fd = accept(proxy_socket, nullptr, nullptr);2867ERR_FAIL_COND_MSG(new_fd == -1, "Failed to accept client.");28682869struct ucred cred = {};2870socklen_t cred_size = sizeof cred;2871getsockopt(new_fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_size);28722873Client &client = clients.insert_new(new_fd, {})->value;28742875client.embedder = this;2876client.socket = new_fd;2877client.pid = cred.pid;28782879client.global_ids[DISPLAY_ID] = Client::GlobalIdInfo(DISPLAY_ID, nullptr);2880client.local_ids[DISPLAY_ID] = DISPLAY_ID;28812882pollfds.push_back({ new_fd, POLLIN, 0 });28832884if (main_client == nullptr) {2885main_client = &client;2886}28872888if (new_fd != main_client->socket && main_client->registry_globals_instances.has(godot_embedding_compositor_name)) {2889uint32_t new_local_id = main_client->allocate_server_id();28902891client.embedded_client_id = new_local_id;28922893for (uint32_t local_id : main_client->registry_globals_instances[godot_embedding_compositor_name]) {2894EmbeddedClientData *eclient_data = memnew(EmbeddedClientData);2895eclient_data->client = &client;28962897main_client->new_fake_object(new_local_id, &godot_embedded_client_interface, 1, eclient_data);28982899// godot_embedding_compositor::client(nu)2900send_wayland_message(main_client->socket, local_id, 0, { new_local_id, (uint32_t)cred.pid });2901}2902}29032904DEBUG_LOG_WAYLAND_EMBED(vformat("New client %d (pid %d) initialized.", client.socket, cred.pid));2905return;2906}29072908if (p_fd == compositor_socket && p_revents & POLLIN) {2909Error err = handle_sock(p_fd);29102911if (err == ERR_BUG) {2912ERR_PRINT("Unexpected error while handling socket, shutting down.");2913shutdown();2914return;2915}29162917return;2918}29192920const Client *client = clients.getptr(p_fd);2921if (client) {2922if (main_client && client == main_client && p_revents & (POLLHUP | POLLERR)) {2923DEBUG_LOG_WAYLAND_EMBED("Main client disconnected, shutting down.");2924shutdown();2925return;2926}29272928if (p_revents & POLLIN) {2929Error err = handle_sock(p_fd);2930if (err == ERR_BUG) {2931ERR_PRINT("Unexpected error while handling socket, shutting down.");2932shutdown();2933return;2934}29352936if (err != OK) {2937DEBUG_LOG_WAYLAND_EMBED("disconnecting");2938cleanup_socket(p_fd);2939return;2940}29412942return;2943} else if (p_revents & (POLLHUP | POLLERR | POLLNVAL)) {2944if (p_revents & POLLHUP) {2945DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d hangup.", p_fd));2946}2947if (p_revents & POLLERR) {2948DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d error.", p_fd));2949}2950if (p_revents & POLLNVAL) {2951DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d invalid FD.", p_fd));2952}29532954cleanup_socket(p_fd);29552956return;2957}2958}2959}29602961WaylandEmbedder::~WaylandEmbedder() {2962shutdown();2963if (proxy_thread.is_started()) {2964proxy_thread.wait_to_finish();2965}2966}29672968#endif // TOOLS_ENABLED29692970#endif // WAYLAND_ENABLED297129722973