Path: blob/master/platform/linuxbsd/wayland/wayland_embedder.cpp
20942 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) {1117#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED1118if (last_str_len > 0) {1119DEBUG_LOG_WAYLAND_EMBED(vformat("Unknown interface %s, marking packet as invalid.", (char *)(body + last_str_buf_idx + 1)));1120} else {1121DEBUG_LOG_WAYLAND_EMBED("Unknown interface, marking packet as invalid.");1122}1123#endif1124valid = false;1125break;1126}11271128if (info->direction == ProxyDirection::COMPOSITOR) {1129// FIXME: Create objects only if the packet is valid.1130uint32_t new_local_id = arg;1131body[buf_idx] = client->new_object(new_local_id, new_interface, new_version);11321133if (body[buf_idx] == INVALID_ID) {1134valid = false;1135break;1136}11371138} else if (info->direction == ProxyDirection::CLIENT) {1139uint32_t new_global_id = arg;11401141if (client) {1142body[buf_idx] = client->new_server_object(new_global_id, new_interface, new_version);1143} else {1144new_server_object(new_global_id, new_interface, new_version);1145}11461147if (body[buf_idx] == INVALID_ID) {1148valid = false;1149break;1150}1151}1152} break;11531154case 'o': {1155if (!client) {1156break;1157}11581159uint32_t obj_id = body[buf_idx];1160if (obj_id == 0) {1161// Object arguments can be nil.1162break;1163}11641165if (info->direction == ProxyDirection::CLIENT) {1166if (!client->local_ids.has(obj_id)) {1167DEBUG_LOG_WAYLAND_EMBED(vformat("Object argument g0x%x not found, marking packet as invalid.", obj_id));1168valid = false;1169break;1170}1171body[buf_idx] = instance_id != INVALID_ID ? instance_id : client->get_local_id(obj_id);1172} else if (info->direction == ProxyDirection::COMPOSITOR) {1173if (!client->global_ids.has(obj_id)) {1174DEBUG_LOG_WAYLAND_EMBED(vformat("Object argument l0x%x not found, marking packet as invalid.", obj_id));1175valid = false;1176break;1177}1178body[buf_idx] = client->get_global_id(obj_id);1179}1180} break;1181}11821183++arg_idx;1184++buf_idx;1185}11861187return valid;1188}11891190WaylandEmbedder::MessageStatus WaylandEmbedder::handle_request(LocalObjectHandle p_object, uint32_t p_opcode, const uint32_t *msg_data, size_t msg_len) {1191ERR_FAIL_COND_V(!p_object.is_valid(), MessageStatus::HANDLED);11921193WaylandObject *object = p_object.get();1194Client *client = p_object.get_client();11951196ERR_FAIL_NULL_V(object, MessageStatus::HANDLED);11971198// NOTE: Global ID may be null.1199uint32_t global_id = p_object.get_global_id();1200uint32_t local_id = p_object.get_local_id();12011202ERR_FAIL_NULL_V(object->interface, MessageStatus::ERROR);1203const struct wl_interface *interface = object->interface;12041205ERR_FAIL_COND_V((int)p_opcode >= interface->method_count, MessageStatus::ERROR);1206const struct wl_message message = interface->methods[p_opcode];12071208DEBUG_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));12091210const uint32_t *body = msg_data + 2;12111212if (registry_globals_names.has(global_id)) {1213int global_name = registry_globals_names[global_id];1214ERR_FAIL_COND_V(!registry_globals.has(global_name), MessageStatus::ERROR);1215RegistryGlobalInfo &global_info = registry_globals[global_name];12161217if (global_info.destroyed) {1218DEBUG_LOG_WAYLAND_EMBED("Skipping request for destroyed global object");1219return MessageStatus::HANDLED;1220}1221}12221223if (object->interface == &wl_display_interface && p_opcode == WL_DISPLAY_GET_REGISTRY) {1224// The gist of this is that the registry is a global and the compositor can1225// quite simply take for granted that a single client can access any global1226// bound from any registry. Let's remove all doubts by using a single1227// registry (also for efficiency) and doing fancy remaps.1228uint32_t local_registry_id = body[0];12291230// Note that the registry has already been allocated in the initialization1231// routine.12321233for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {1234uint32_t global_name = pair.key;1235RegistryGlobalInfo &global_info = pair.value;12361237if (global_info.destroyed) {1238continue;1239}12401241const struct wl_interface *global_interface = global_info.interface;12421243if (client != main_client && embedded_interface_deny_list.has(global_interface)) {1244DEBUG_LOG_WAYLAND_EMBED(vformat("Skipped global announcement %s for embedded client.", global_interface->name));1245continue;1246}12471248LocalVector<union wl_argument> args;1249args.push_back(wl_arg_uint(global_name));1250args.push_back(wl_arg_string(global_interface->name));1251args.push_back(wl_arg_uint(global_info.version));12521253send_wayland_event(client->socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL, args);1254}12551256client->wl_registry_instances.insert(local_registry_id);1257client->new_global_instance(local_registry_id, REGISTRY_ID, &wl_registry_interface, 1);12581259return MessageStatus::HANDLED;1260}12611262if (object->interface == &wl_registry_interface) {1263if (p_opcode == WL_REGISTRY_BIND) {1264// [Request] wl_registry::bind(usun)1265uint32_t global_name = body[0];1266uint32_t interface_name_len = body[1];1267//const char *interface_name = (const char *)(body + 2);1268uint32_t version = body[2 + wl_array_word_offset(interface_name_len)];1269uint32_t new_local_id_idx = 2 + wl_array_word_offset(interface_name_len) + 1;1270uint32_t new_local_id = body[new_local_id_idx];12711272if (!registry_globals.has(global_name)) {1273socket_error(client->socket, local_id, WL_DISPLAY_ERROR_INVALID_METHOD, vformat("Invalid global object #%d", global_name));1274return MessageStatus::HANDLED;1275}12761277RegistryGlobalInfo &global_info = registry_globals[global_name];1278ERR_FAIL_NULL_V(global_info.interface, MessageStatus::ERROR);12791280version = MIN(global_info.version, version);12811282if (global_info.interface == &godot_embedding_compositor_interface) {1283if (!client->registry_globals_instances.has(global_name)) {1284client->registry_globals_instances[global_name] = {};1285}12861287client->registry_globals_instances[global_name].insert(new_local_id);1288++global_info.instance_counter;1289DEBUG_LOG_WAYLAND_EMBED("Bound embedded compositor interface.");1290client->new_fake_object(new_local_id, &godot_embedding_compositor_interface, 1);1291return MessageStatus::HANDLED;1292}12931294WaylandObject *instance = nullptr;12951296client->registry_globals_instances[global_name].insert(new_local_id);1297++global_info.instance_counter;12981299if (!client->registry_globals_instances.has(global_name)) {1300client->registry_globals_instances[global_name] = {};1301}13021303uint32_t bind_gid = wl_registry_bind(REGISTRY_ID, global_name, version);1304if (bind_gid == INVALID_ID) {1305socket_error(client->socket, local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "Bind failed.");1306return MessageStatus::HANDLED;1307}13081309WaylandObject *bind_obj = get_object(bind_gid);1310if (bind_obj == nullptr) {1311socket_error(client->socket, local_id, WL_DISPLAY_ERROR_IMPLEMENTATION, "Bind failed.");1312return MessageStatus::HANDLED;1313}13141315if (!bind_obj->shared) {1316client->bind_global_id(bind_gid, new_local_id);1317instance = bind_obj;1318} else {1319instance = client->new_global_instance(new_local_id, global_info.reusable_objects[version], global_info.interface, version);1320DEBUG_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]));13211322// Some interfaces report their state as soon as they're bound. Since1323// instances are handled by us, we need to track and report the relevant1324// data ourselves.1325if (global_info.interface == &wl_drm_interface) {1326Error err = client->send_wl_drm_state(new_local_id, (WaylandDrmGlobalData *)global_info.data);1327if (err != OK) {1328return MessageStatus::ERROR;1329}1330} else if (global_info.interface == &wl_shm_interface) {1331WaylandShmGlobalData *global_data = (WaylandShmGlobalData *)global_info.data;1332ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);13331334for (uint32_t format : global_data->formats) {1335send_wayland_message(client->socket, new_local_id, WL_SHM_FORMAT, { format });1336}1337}1338}13391340ERR_FAIL_NULL_V(instance, MessageStatus::UNHANDLED);13411342if (global_info.interface == &wl_seat_interface) {1343WaylandSeatInstanceData *new_data = memnew(WaylandSeatInstanceData);1344instance->data = new_data;1345}13461347return MessageStatus::HANDLED;1348}1349}13501351if (object->interface == &wl_compositor_interface && p_opcode == WL_COMPOSITOR_CREATE_SURFACE) {1352uint32_t new_local_id = body[0];13531354WaylandSurfaceData *data = memnew(WaylandSurfaceData);1355data->client = client;13561357uint32_t new_global_id = client->new_object(new_local_id, &wl_surface_interface, object->version, data);1358ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);13591360DEBUG_LOG_WAYLAND_EMBED(vformat("Keeping track of surface l0x%x g0x%x.", new_local_id, new_global_id));13611362send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });1363return MessageStatus::HANDLED;1364}13651366if (object->interface == &wl_surface_interface) {1367WaylandSurfaceData *surface_data = (WaylandSurfaceData *)object->data;1368ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);13691370if (p_opcode == WL_SURFACE_DESTROY) {1371for (uint32_t wl_seat_name : wl_seat_names) {1372WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;1373ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);13741375if (global_seat_data->pointed_surface_id == global_id) {1376global_seat_data->pointed_surface_id = INVALID_ID;1377}13781379if (global_seat_data->focused_surface_id == global_id) {1380global_seat_data->focused_surface_id = INVALID_ID;1381}1382}1383} else if (p_opcode == WL_SURFACE_COMMIT) {1384if (surface_data->role_object_handle.is_valid()) {1385WaylandObject *role_object = surface_data->role_object_handle.get();1386if (role_object && role_object->interface) {1387DEBUG_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()));1388}13891390if (role_object && role_object->interface == &xdg_toplevel_interface) {1391XdgToplevelData *toplevel_data = (XdgToplevelData *)role_object->data;1392ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);1393// xdg shell spec requires clients to first send data and then commit the1394// surface.13951396if (toplevel_data->is_embedded() && !toplevel_data->configured) {1397toplevel_data->configured = true;1398// xdg_surface::configure1399send_wayland_message(client->socket, toplevel_data->xdg_surface_handle.get_local_id(), 0, { serial_counter++ });1400}1401}1402}14031404send_wayland_message(compositor_socket, global_id, p_opcode, {});1405return MessageStatus::HANDLED;1406}1407}14081409if (object->interface == &wl_seat_interface) {1410uint32_t global_seat_name = registry_globals_names[global_id];14111412RegistryGlobalInfo &seat_global_info = registry_globals[global_seat_name];1413WaylandSeatGlobalData *global_data = (WaylandSeatGlobalData *)seat_global_info.data;1414ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);14151416WaylandSeatInstanceData *instance_data = (WaylandSeatInstanceData *)object->data;1417ERR_FAIL_NULL_V(instance_data, MessageStatus::ERROR);14181419if (p_opcode == WL_SEAT_GET_POINTER) {1420ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::ERROR);1421// [Request] wl_seat::get_pointer(n);1422uint32_t new_local_id = body[0];14231424WaylandPointerData *new_data = memnew(WaylandPointerData);1425new_data->wl_seat_id = global_id;14261427uint32_t new_global_id = client->new_object(new_local_id, &wl_pointer_interface, object->version, new_data);1428ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);14291430instance_data->wl_pointer_id = new_global_id;14311432send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });14331434return MessageStatus::HANDLED;1435}14361437if (p_opcode == WL_SEAT_GET_KEYBOARD) {1438ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::ERROR);1439// [Request] wl_seat::get_pointer(n);1440uint32_t new_local_id = body[0];14411442WaylandKeyboardData *new_data = memnew(WaylandKeyboardData);1443new_data->wl_seat_id = global_id;14441445uint32_t new_global_id = client->new_object(new_local_id, &wl_keyboard_interface, object->version, new_data);1446ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);14471448instance_data->wl_keyboard_id = new_global_id;14491450send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });14511452return MessageStatus::HANDLED;1453}1454}14551456if (object->interface == &xdg_wm_base_interface) {1457if (p_opcode == XDG_WM_BASE_CREATE_POSITIONER) {1458uint32_t new_local_id = body[0];1459uint32_t new_global_id = client->new_object(new_local_id, &xdg_positioner_interface, object->version, memnew(XdgPositionerData));1460ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);14611462send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });1463return MessageStatus::HANDLED;1464}14651466if (p_opcode == XDG_WM_BASE_GET_XDG_SURFACE) {1467// [Request] xdg_wm_base::get_xdg_surface(no).1468uint32_t new_local_id = body[0];1469uint32_t surface_id = body[1];14701471uint32_t global_surface_id = client->get_global_id(surface_id);14721473bool fake = (client != main_client);14741475XdgSurfaceData *data = memnew(XdgSurfaceData);1476data->wl_surface_id = global_surface_id;14771478if (fake) {1479client->new_fake_object(new_local_id, &xdg_surface_interface, object->version, data);1480DEBUG_LOG_WAYLAND_EMBED(vformat("Created fake xdg_surface l0x%x for surface l0x%x", new_local_id, surface_id));1481} else {1482uint32_t new_global_id = client->new_object(new_local_id, &xdg_surface_interface, object->version, data);1483ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);14841485DEBUG_LOG_WAYLAND_EMBED(vformat("Created real xdg_surface l0x%x g0x%x for surface l0x%x", new_local_id, new_global_id, surface_id));14861487send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_surface_id });1488}14891490return MessageStatus::HANDLED;1491}1492}14931494if (object->interface == &xdg_surface_interface) {1495XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)object->data;1496ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);14971498WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(xdg_surf_data->wl_surface_id)->data;1499ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);15001501bool is_embedded = client->fake_objects.has(local_id);15021503if (p_opcode == XDG_SURFACE_GET_POPUP) {1504// [Request] xdg_surface::get_popup(no?o).15051506uint32_t new_local_id = body[0];1507uint32_t local_parent_id = body[1];1508uint32_t local_positioner_id = body[2];15091510surface_data->role_object_handle = LocalObjectHandle(client, new_local_id);15111512XdgPopupData *popup_data = memnew(XdgPopupData);1513popup_data->parent_handle = LocalObjectHandle(client, local_parent_id);15141515if (!is_embedded) {1516uint32_t new_global_id = client->new_object(new_local_id, &xdg_popup_interface, object->version, popup_data);1517ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);15181519uint32_t global_parent_id = client->get_global_id(local_parent_id);1520uint32_t global_positioner_id = client->get_global_id(local_positioner_id);1521send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_parent_id, global_positioner_id });15221523return MessageStatus::HANDLED;1524}15251526{1527// Popups are real, time to actually instantiate an xdg_surface.1528WaylandObject copy = *object;1529client->fake_objects.erase(local_id);15301531global_id = client->new_object(local_id, copy.interface, copy.version, copy.data);1532ERR_FAIL_COND_V(global_id == INVALID_ID, MessageStatus::HANDLED);1533object = get_object(global_id);15341535// xdg_wm_base::get_xdg_surface(no);1536send_wayland_message(compositor_socket, xdg_wm_base_id, 2, { global_id, xdg_surf_data->wl_surface_id });1537}15381539uint32_t new_global_id = client->new_object(new_local_id, &xdg_popup_interface, object->version, popup_data);1540ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);15411542uint32_t global_parent_id = INVALID_ID;1543if (local_parent_id != INVALID_ID) {1544XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)client->get_object(local_parent_id)->data;1545ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);15461547WaylandSurfaceData *parent_surface_data = (WaylandSurfaceData *)get_object(parent_xdg_surf_data->wl_surface_id)->data;1548ERR_FAIL_NULL_V(parent_surface_data, MessageStatus::ERROR);15491550WaylandObject *parent_role_obj = parent_surface_data->role_object_handle.get();1551ERR_FAIL_NULL_V(parent_role_obj, MessageStatus::ERROR);15521553XdgPositionerData *pos_data = (XdgPositionerData *)client->get_object(local_positioner_id)->data;1554ERR_FAIL_NULL_V(pos_data, MessageStatus::ERROR);15551556if (parent_role_obj->interface == &xdg_toplevel_interface) {1557XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)parent_role_obj->data;1558ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);15591560if (parent_toplevel_data->is_embedded()) {1561// Embedded windows are subsurfaces of a parent window. We need to1562// "redirect" the popup request on the parent window and adjust the1563// positioner properly if needed.15641565XdgToplevelData *main_parent_toplevel_data = (XdgToplevelData *)parent_toplevel_data->parent_handle.get()->data;1566ERR_FAIL_NULL_V(main_parent_toplevel_data, MessageStatus::ERROR);15671568global_parent_id = main_parent_toplevel_data->xdg_surface_handle.get_global_id();15691570WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(parent_toplevel_data->wl_subsurface_id)->data;1571ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);15721573Point2i adj_pos = subsurf_data->position + pos_data->anchor_rect.position;15741575// xdg_positioner::set_anchor_rect1576send_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 });1577}1578} else {1579global_parent_id = client->get_global_id(local_parent_id);1580}1581}15821583send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id, global_parent_id, client->get_global_id(local_positioner_id) });1584return MessageStatus::HANDLED;1585}15861587if (p_opcode == XDG_SURFACE_GET_TOPLEVEL) {1588// [Request] xdg_surface::get_toplevel(n).1589uint32_t new_local_id = body[0];15901591surface_data->role_object_handle = LocalObjectHandle(client, new_local_id);15921593XdgToplevelData *data = memnew(XdgToplevelData);1594data->xdg_surface_handle = LocalObjectHandle(client, local_id);15951596if (is_embedded) {1597client->new_fake_object(new_local_id, &xdg_toplevel_interface, object->version, data);1598client->embedded_window_id = new_local_id;15991600// godot_embedded_client::window_embedded()1601send_wayland_message(main_client->socket, client->embedded_client_id, 1, {});1602} else {1603uint32_t new_global_id = client->new_object(new_local_id, &xdg_toplevel_interface, object->version, data);1604ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);16051606if (main_toplevel_id == 0) {1607main_toplevel_id = new_global_id;1608DEBUG_LOG_WAYLAND_EMBED(vformat("main toplevel set to gx0%x.", main_toplevel_id));1609}16101611send_wayland_message(compositor_socket, global_id, p_opcode, { new_global_id });1612}16131614return MessageStatus::HANDLED;1615}1616}16171618if (object->interface == &xdg_positioner_interface) {1619XdgPositionerData *pos_data = (XdgPositionerData *)object->data;1620ERR_FAIL_NULL_V(pos_data, MessageStatus::ERROR);16211622if (p_opcode == XDG_POSITIONER_SET_ANCHOR_RECT) {1623// Args: int x, int y, int width, int height.1624pos_data->anchor_rect = Rect2i(body[0], body[1], body[2], body[3]);16251626send_wayland_message(compositor_socket, global_id, p_opcode, { body[0], body[1], body[2], body[3] });1627return MessageStatus::HANDLED;1628}1629}16301631if (object->interface == &xdg_toplevel_interface && p_opcode == XDG_TOPLEVEL_DESTROY) {1632if (client->fake_objects.has(local_id)) {1633XdgToplevelData *data = (XdgToplevelData *)object->data;1634ERR_FAIL_NULL_V(data, MessageStatus::ERROR);16351636XdgSurfaceData *xdg_surf_data = nullptr;1637if (data->xdg_surface_handle.is_valid()) {1638xdg_surf_data = (XdgSurfaceData *)data->xdg_surface_handle.get()->data;1639ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);1640}1641ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);16421643XdgSurfaceData *parent_xdg_surf_data = nullptr;1644{1645XdgToplevelData *parent_data = nullptr;1646if (data->parent_handle.get()) {1647parent_data = (XdgToplevelData *)data->parent_handle.get()->data;1648ERR_FAIL_NULL_V(parent_data, MessageStatus::ERROR);1649}16501651if (parent_data && parent_data->xdg_surface_handle.get()) {1652parent_xdg_surf_data = (XdgSurfaceData *)parent_data->xdg_surface_handle.get()->data;1653ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);1654}1655}16561657for (uint32_t wl_seat_name : wl_seat_names) {1658WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)registry_globals[wl_seat_name].data;1659ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);16601661if (global_seat_data->focused_surface_id == xdg_surf_data->wl_surface_id) {1662if (xdg_surf_data) {1663seat_name_leave_surface(wl_seat_name, xdg_surf_data->wl_surface_id);1664}16651666if (parent_xdg_surf_data) {1667seat_name_enter_surface(wl_seat_name, parent_xdg_surf_data->wl_surface_id);1668}1669}1670}16711672// wl_display::delete_id1673send_wayland_message(client->socket, local_id, p_opcode, {});16741675if (local_id == client->embedded_window_id) {1676client->embedded_window_id = 0;1677}16781679if (data->wl_subsurface_id != INVALID_ID) {1680send_wayland_message(compositor_socket, data->wl_subsurface_id, WL_SUBSURFACE_DESTROY, {});1681}16821683client->delete_object(local_id);16841685return MessageStatus::HANDLED;1686}1687}16881689if (interface == &zwp_pointer_constraints_v1_interface) {1690// FIXME: This implementation leaves no way of unlocking the pointer when1691// embedded into the main window. We might need to be a bit more invasive.1692if (p_opcode == ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER) {1693// [Request] zwp_pointer_constraints_v1::lock_pointer(nooou).16941695uint32_t new_local_id = body[0];1696uint32_t local_surface_id = body[1];1697uint32_t local_pointer_id = body[2];1698uint32_t lifetime = body[4];16991700WaylandSurfaceData *surf_data = (WaylandSurfaceData *)client->get_object(local_surface_id)->data;1701ERR_FAIL_NULL_V(surf_data, MessageStatus::ERROR);17021703WaylandObject *role_obj = surf_data->role_object_handle.get();1704ERR_FAIL_NULL_V(role_obj, MessageStatus::ERROR);17051706if (role_obj->interface == &xdg_toplevel_interface) {1707XdgToplevelData *toplevel_data = (XdgToplevelData *)role_obj->data;1708ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);17091710if (!toplevel_data->is_embedded()) {1711// Passthrough.1712return MessageStatus::UNHANDLED;1713}17141715// Subsurfaces don't normally work, at least on sway, as the locking1716// condition might rely on focus, which they don't get. We can remap them to1717// the parent surface and set a region though.17181719XdgToplevelData *parent_data = (XdgToplevelData *)toplevel_data->parent_handle.get()->data;1720ERR_FAIL_NULL_V(parent_data, MessageStatus::ERROR);17211722XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)parent_data->xdg_surface_handle.get()->data;1723ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);17241725WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(toplevel_data->wl_subsurface_id)->data;1726ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);17271728uint32_t new_global_id = client->new_object(new_local_id, &zwp_locked_pointer_v1_interface, object->version);1729ERR_FAIL_COND_V(new_global_id == INVALID_ID, MessageStatus::HANDLED);17301731uint32_t x = subsurf_data->position.x;1732uint32_t y = subsurf_data->position.y;1733uint32_t width = toplevel_data->size.width;1734uint32_t height = toplevel_data->size.height;17351736// NOTE: At least on sway I can't seem to be able to get this region1737// working but the calls check out.1738DEBUG_LOG_WAYLAND_EMBED(vformat("Creating custom region x%d y%d w%d h%d", x, y, width, height));17391740uint32_t new_region_id = allocate_global_id();1741get_object(new_region_id)->interface = &wl_region_interface;1742get_object(new_region_id)->version = get_object(wl_compositor_id)->version;17431744// wl_compostor::create_region(n).1745send_wayland_message(compositor_socket, wl_compositor_id, 1, { new_region_id });17461747// wl_region::add(iiii).1748send_wayland_message(compositor_socket, new_region_id, 1, { x, y, width, height });17491750send_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 });17511752// wl_region::destroy().1753send_wayland_message(compositor_socket, new_region_id, 0, {});17541755return MessageStatus::HANDLED;1756}1757}1758}17591760if (interface == &godot_embedded_client_interface) {1761EmbeddedClientData *eclient_data = (EmbeddedClientData *)object->data;1762ERR_FAIL_NULL_V(eclient_data, MessageStatus::ERROR);17631764Client *eclient = eclient_data->client;1765ERR_FAIL_NULL_V(eclient, MessageStatus::ERROR);17661767if (p_opcode == GODOT_EMBEDDED_CLIENT_DESTROY) {1768if (!eclient_data->disconnected) {1769close(eclient->socket);1770}17711772client->delete_object(local_id);17731774return MessageStatus::HANDLED;1775}17761777if (eclient_data->disconnected) {1778// Object is inert.1779return MessageStatus::HANDLED;1780}17811782ERR_FAIL_COND_V(eclient->embedded_window_id == 0, MessageStatus::ERROR);17831784XdgToplevelData *toplevel_data = (XdgToplevelData *)eclient->get_object(eclient->embedded_window_id)->data;1785ERR_FAIL_NULL_V(toplevel_data, MessageStatus::ERROR);17861787if (p_opcode == GODOT_EMBEDDED_CLIENT_SET_EMBEDDED_WINDOW_RECT && toplevel_data->wl_subsurface_id != INVALID_ID) {1788uint32_t x = body[0];1789uint32_t y = body[1];1790uint32_t width = body[2];1791uint32_t height = body[3];17921793WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(toplevel_data->wl_subsurface_id)->data;1794ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);17951796toplevel_data->size.width = width;1797toplevel_data->size.height = height;17981799subsurf_data->position.x = x;1800subsurf_data->position.y = y;18011802// wl_subsurface::set_position1803send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 1, { x, y });18041805// xdg_toplevel::configure1806send_wayland_message(eclient->socket, eclient->embedded_window_id, 0, { width, height, 0 });18071808// xdg_surface::configure1809send_wayland_message(eclient->socket, toplevel_data->xdg_surface_handle.get_local_id(), 0, { configure_serial_counter++ });18101811return MessageStatus::HANDLED;1812} else if (p_opcode == GODOT_EMBEDDED_CLIENT_SET_EMBEDDED_WINDOW_PARENT) {1813uint32_t main_client_parent_id = body[0];18141815if (toplevel_data->parent_handle.get_local_id() == main_client_parent_id) {1816return MessageStatus::HANDLED;1817}18181819if (main_client_parent_id == INVALID_ID && toplevel_data->wl_subsurface_id != INVALID_ID) {1820// Window hiding logic.18211822// wl_subsurface::destroy()1823send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 0, {});18241825toplevel_data->parent_handle.invalidate();1826toplevel_data->wl_subsurface_id = INVALID_ID;18271828return MessageStatus::HANDLED;1829}18301831XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)client->get_object(main_client_parent_id)->data;1832ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);1833XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)parent_toplevel_data->xdg_surface_handle.get()->data;1834ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);18351836XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)toplevel_data->xdg_surface_handle.get()->data;1837ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);18381839if (toplevel_data->wl_subsurface_id != INVALID_ID) {1840// wl_subsurface::destroy()1841send_wayland_message(compositor_socket, toplevel_data->wl_subsurface_id, 0, {});1842}18431844uint32_t new_sub_id = allocate_global_id();1845WaylandObject *new_sub_object = get_object(new_sub_id);1846new_sub_object->interface = &wl_subsurface_interface;1847new_sub_object->data = memnew(WaylandSubsurfaceData);1848new_sub_object->version = get_object(wl_subcompositor_id)->version;18491850toplevel_data->wl_subsurface_id = new_sub_id;1851toplevel_data->parent_handle = LocalObjectHandle(main_client, main_client_parent_id);18521853DEBUG_LOG_WAYLAND_EMBED(vformat("Binding subsurface g0x%x.", new_sub_id));18541855// wl_subcompositor::get_subsurface1856send_wayland_message(compositor_socket, wl_subcompositor_id, 1, { new_sub_id, xdg_surf_data->wl_surface_id, parent_xdg_surf_data->wl_surface_id });18571858// wl_subsurface::set_desync1859send_wayland_message(compositor_socket, new_sub_id, 5, {});18601861return MessageStatus::HANDLED;1862} else if (p_opcode == GODOT_EMBEDDED_CLIENT_FOCUS_WINDOW) {1863XdgSurfaceData *xdg_surf_data = (XdgSurfaceData *)toplevel_data->xdg_surface_handle.get()->data;1864ERR_FAIL_NULL_V(xdg_surf_data, MessageStatus::ERROR);18651866for (uint32_t wl_seat_name : wl_seat_names) {1867RegistryGlobalInfo &global_seat_info = registry_globals[wl_seat_name];1868WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;18691870if (global_seat_data->focused_surface_id != INVALID_ID) {1871seat_name_leave_surface(wl_seat_name, global_seat_data->focused_surface_id);1872}1873global_seat_data->focused_surface_id = xdg_surf_data->wl_surface_id;18741875seat_name_enter_surface(wl_seat_name, xdg_surf_data->wl_surface_id);1876}1877} else if (p_opcode == GODOT_EMBEDDED_CLIENT_EMBEDDED_WINDOW_REQUEST_CLOSE) {1878// xdg_toplevel::close1879send_wayland_message(eclient->socket, eclient->embedded_window_id, 1, {});18801881return MessageStatus::HANDLED;1882}1883}18841885// Server-allocated objects are a bit annoying to handle for us. Right now we1886// use a heuristic. See: https://ppaalanen.blogspot.com/2014/07/wayland-protocol-design-object-lifespan.html1887if (strcmp(message.name, "destroy") == 0 || strcmp(message.name, "release") == 0) {1888if (object->shared) {1889// We must not delete shared objects.1890client->delete_object(local_id);1891return MessageStatus::HANDLED;1892}18931894if (global_id != INVALID_ID) {1895send_wayland_message(compositor_socket, global_id, p_opcode, {});1896object->destroyed = true;1897}18981899if (local_id & 0xff000000) {1900DEBUG_LOG_WAYLAND_EMBED(vformat("!!!!!! Deallocating server object l0x%x", local_id));1901client->delete_object(local_id);1902}19031904return MessageStatus::HANDLED;1905}19061907if (client->fake_objects.has(local_id)) {1908// Object is fake, we're done.1909DEBUG_LOG_WAYLAND_EMBED("Dropping unhandled request for fake object.");1910return MessageStatus::HANDLED;1911}19121913if (global_id == INVALID_ID) {1914DEBUG_LOG_WAYLAND_EMBED("Dropping request with invalid global object id");1915return MessageStatus::HANDLED;1916}19171918return MessageStatus::UNHANDLED;1919}19201921WaylandEmbedder::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) {1922WaylandObject *global_object = get_object(p_global_id);1923ERR_FAIL_NULL_V_MSG(global_object, MessageStatus::ERROR, "Compositor messages must always have a global object.");19241925#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED1926ERR_FAIL_NULL_V(global_object->interface, MessageStatus::ERROR);1927const struct wl_interface *interface = global_object->interface;19281929ERR_FAIL_COND_V((int)p_opcode >= interface->event_count, MessageStatus::ERROR);1930const struct wl_message message = interface->events[p_opcode];19311932if (p_local_handle.is_valid()) {1933int socket = p_local_handle.get_client()->socket;1934DEBUG_LOG_WAYLAND_EMBED(vformat("Client #%d <- %s::%s(%s) g0x%x", socket, interface->name, message.name, message.signature, p_global_id));1935} else {1936DEBUG_LOG_WAYLAND_EMBED(vformat("Client N/A <- %s::%s(%s) g0x%x", interface->name, message.name, message.signature, p_global_id));1937}1938#endif //WAYLAND_EMBED_DEBUG_LOGS_ENABLED19391940const uint32_t *body = msg_data + 2;1941//size_t body_len = msg_len - (WL_WORD_SIZE * 2);19421943// FIXME: Make sure that it makes sense to track this protocol. Not only is it1944// old and getting deprecated, but I can't even get this code branch to hit1945// probably because, at the time of writing, we only get the "main" display1946// through the proxy.1947if (global_object->interface == &wl_drm_interface) {1948// wl_drm can't ever be destroyed, so we need to track its state as it's going1949// to be instanced at least few times.1950uint32_t global_name = registry_globals_names[p_global_id];1951WaylandDrmGlobalData *global_data = (WaylandDrmGlobalData *)registry_globals[global_name].data;1952ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);19531954if (p_opcode == WL_DRM_DEVICE) {1955// signature: s1956uint32_t name_len = body[0];1957uint8_t *name = (uint8_t *)(body + 1);1958global_data->device = String::utf8((const char *)name, name_len);19591960return MessageStatus::UNHANDLED;1961}19621963if (p_opcode == WL_DRM_FORMAT) {1964// signature: u1965uint32_t format = body[0];1966global_data->formats.push_back(format);19671968return MessageStatus::UNHANDLED;1969}19701971if (p_opcode == WL_DRM_AUTHENTICATED) {1972// signature: N/A1973global_data->authenticated = true;19741975return MessageStatus::UNHANDLED;1976}19771978if (p_opcode == WL_DRM_CAPABILITIES) {1979// signature: u1980uint32_t capabilities = body[0];1981global_data->capabilities = capabilities;1982}19831984return MessageStatus::UNHANDLED;1985}19861987if (global_object->interface == &wl_shm_interface) {1988uint32_t global_name = registry_globals_names[p_global_id];1989WaylandShmGlobalData *global_data = (WaylandShmGlobalData *)registry_globals[global_name].data;1990ERR_FAIL_NULL_V(global_data, MessageStatus::ERROR);19911992if (p_opcode == WL_SHM_FORMAT) {1993// Signature: u1994uint32_t format = body[0];1995global_data->formats.push_back(format);1996}1997}19981999if (!p_local_handle.is_valid()) {2000// Some requests might not have a valid local object handle for various2001// reasons, such as when certain events are directed to this proxy or when the2002// destination client of a message disconnected in the meantime.20032004if (global_object->interface == &wl_display_interface) {2005if (p_opcode == WL_DISPLAY_DELETE_ID) {2006// [Event] wl_display::delete_id(u)2007uint32_t global_delete_id = body[0];2008DEBUG_LOG_WAYLAND_EMBED(vformat("Compositor requested deletion of g0x%x (no client)", global_delete_id));20092010delete_object(global_delete_id);20112012return MessageStatus::HANDLED;2013} else if (p_opcode == WL_DISPLAY_ERROR) {2014// [Event] wl_display::error(ous)2015uint32_t obj_id = body[0];2016uint32_t err_code = body[1];20172018CRASH_NOW_MSG(vformat("Error obj g0x%x code %d: %s", obj_id, err_code, (const char *)(body + 3)));2019}2020}20212022if (global_object->interface == &wl_callback_interface && p_opcode == WL_CALLBACK_DONE) {2023if (sync_callback_id != INVALID_ID && p_global_id == sync_callback_id) {2024sync_callback_id = 0;2025DEBUG_LOG_WAYLAND_EMBED("Sync response received");2026return MessageStatus::HANDLED;2027}2028}20292030if (global_object->interface == &wl_registry_interface) {2031if (p_opcode == WL_REGISTRY_GLOBAL) {2032// [Event] wl_registry::global(usu).20332034uint32_t global_name = body[0];2035uint32_t interface_name_len = body[1];2036const char *interface_name = (const char *)(body + 2);2037uint32_t global_version = body[2 + wl_array_word_offset(interface_name_len)];20382039DEBUG_LOG_WAYLAND_EMBED("Global c#%d %s %d", global_name, interface_name, global_version);20402041const struct wl_interface *global_interface = wl_interface_from_string(interface_name, interface_name_len);2042if (global_interface) {2043RegistryGlobalInfo global_info = {};2044global_info.interface = global_interface;2045global_info.version = MIN(global_version, (uint32_t)global_interface->version);2046DEBUG_LOG_WAYLAND_EMBED("Clamped global %s to version %d.", interface_name, global_info.version);2047global_info.compositor_name = global_name;20482049int new_global_name = registry_globals_counter++;20502051if (global_info.interface == &wl_shm_interface) {2052DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_shm data.");2053global_info.data = memnew(WaylandShmGlobalData);2054}20552056if (global_info.interface == &wl_seat_interface) {2057DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_seat data.");2058global_info.data = memnew(WaylandSeatGlobalData);2059wl_seat_names.push_back(new_global_name);2060}20612062if (global_info.interface == &wl_drm_interface) {2063DEBUG_LOG_WAYLAND_EMBED("Allocating global wl_drm data.");2064global_info.data = memnew(WaylandDrmGlobalData);2065}20662067registry_globals[new_global_name] = global_info;20682069// We need some interfaces directly. It's better to bind a "copy" ourselves2070// than to wait for the client to ask one.2071if (global_interface == &xdg_wm_base_interface && xdg_wm_base_id == 0) {2072xdg_wm_base_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);2073ERR_FAIL_COND_V(xdg_wm_base_id == INVALID_ID, MessageStatus::ERROR);2074} else if (global_interface == &wl_compositor_interface && wl_compositor_id == 0) {2075wl_compositor_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);2076ERR_FAIL_COND_V(wl_compositor_id == INVALID_ID, MessageStatus::ERROR);2077} else if (global_interface == &wl_subcompositor_interface && wl_subcompositor_id == 0) {2078wl_subcompositor_id = wl_registry_bind(p_global_id, new_global_name, global_info.version);2079ERR_FAIL_COND_V(wl_subcompositor_id == INVALID_ID, MessageStatus::ERROR);2080}20812082DEBUG_LOG_WAYLAND_EMBED(vformat("Local registry object name: l#%d", new_global_name));20832084if (clients.is_empty()) {2085// Let's not waste time.2086return MessageStatus::HANDLED;2087}20882089// Notify all clients.2090LocalVector<wl_argument> args;2091args.push_back(wl_arg_uint(new_global_name));2092args.push_back(wl_arg_string(interface_name));2093args.push_back(wl_arg_uint(global_info.version));2094for (KeyValue<int, Client> &pair : clients) {2095Client &client = pair.value;2096for (uint32_t local_registry_id : client.wl_registry_instances) {2097send_wayland_event(client.socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL, args);2098}2099}21002101return MessageStatus::HANDLED;2102} else {2103DEBUG_LOG_WAYLAND_EMBED("Skipping unknown global %s version %d.", interface_name, global_version);21042105return MessageStatus::HANDLED;2106}2107} else if (p_opcode == WL_REGISTRY_GLOBAL_REMOVE) {2108uint32_t compositor_name = body[0];2109uint32_t local_name = 0;2110RegistryGlobalInfo *global_info = nullptr;21112112// FIXME: Use a map or something.2113for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {2114uint32_t name = pair.key;2115RegistryGlobalInfo &info = pair.value;21162117if (info.compositor_name == compositor_name) {2118local_name = name;2119global_info = &info;2120break;2121}2122}21232124ERR_FAIL_NULL_V(global_info, MessageStatus::ERROR);21252126if (global_info->instance_counter == 0) {2127memdelete(global_info->data);2128registry_globals.erase(local_name);2129} else {2130global_info->destroyed = true;2131}21322133// Notify all clients.2134LocalVector<wl_argument> args;2135args.push_back(wl_arg_uint(local_name));2136for (KeyValue<int, Client> &pair : clients) {2137Client &client = pair.value;2138for (uint32_t local_registry_id : client.wl_registry_instances) {2139send_wayland_event(client.socket, local_registry_id, wl_registry_interface, WL_REGISTRY_GLOBAL_REMOVE, args);2140}2141}21422143return MessageStatus::HANDLED;2144}2145}21462147DEBUG_LOG_WAYLAND_EMBED("No valid local object handle, falling back to generic handler.");2148return MessageStatus::UNHANDLED;2149}21502151Client *client = p_local_handle.get_client();21522153ERR_FAIL_NULL_V(client, MessageStatus::ERROR);21542155WaylandObject *object = p_local_handle.get();2156uint32_t local_id = p_local_handle.get_local_id();21572158if (global_object->interface == &wl_display_interface) {2159if (p_opcode == WL_DISPLAY_DELETE_ID) {2160// [Event] wl_display::delete_id(u)2161uint32_t global_delete_id = body[0];2162uint32_t local_delete_id = client->get_local_id(global_delete_id);2163DEBUG_LOG_WAYLAND_EMBED(vformat("Compositor requested delete of g0x%x l0x%x", global_delete_id, local_delete_id));2164if (local_delete_id == INVALID_ID) {2165// No idea what this object is, might be of the other client. This2166// definitely does not make sense to us, so we're done.2167return MessageStatus::INVALID;2168}21692170client->delete_object(local_delete_id);21712172send_wayland_message(client->socket, DISPLAY_ID, WL_DISPLAY_DELETE_ID, { local_delete_id });21732174return MessageStatus::HANDLED;2175}21762177return MessageStatus::UNHANDLED;2178}21792180if (object->interface == &wl_keyboard_interface) {2181WaylandKeyboardData *data = (WaylandKeyboardData *)object->data;2182ERR_FAIL_NULL_V(data, MessageStatus::ERROR);21832184uint32_t global_seat_name = registry_globals_names[data->wl_seat_id];2185RegistryGlobalInfo &global_seat_info = registry_globals[global_seat_name];2186WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;2187ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);21882189if (p_opcode == WL_KEYBOARD_ENTER) {2190// [Event] wl_keyboard::enter(uoa)2191uint32_t surface = body[1];21922193if (global_seat_data->focused_surface_id != surface) {2194DEBUG_LOG_WAYLAND_EMBED(vformat("Focused g0x%x", surface));2195global_seat_data->focused_surface_id = surface;2196}2197} else if (p_opcode == WL_KEYBOARD_LEAVE) {2198// [Event] wl_keyboard::leave(uo)2199uint32_t surface = body[1];22002201if (global_seat_data->focused_surface_id == surface) {2202global_seat_data->focused_surface_id = INVALID_ID;2203}2204} else if (p_opcode == WL_KEYBOARD_KEY) {2205// NOTE: modifiers event can be sent even without focus, according to the2206// spec, so there's no need to skip it.2207if (global_seat_data->focused_surface_id != INVALID_ID && !client->local_ids.has(global_seat_data->focused_surface_id)) {2208DEBUG_LOG_WAYLAND_EMBED(vformat("Skipped wl_keyboard event due to unfocused surface 0x%x", global_seat_data->focused_surface_id));2209return MessageStatus::HANDLED;2210}2211}22122213return MessageStatus::UNHANDLED;2214}22152216if (object->interface == &wl_pointer_interface) {2217WaylandPointerData *data = (WaylandPointerData *)object->data;2218ERR_FAIL_NULL_V(data, MessageStatus::ERROR);22192220uint32_t global_seat_name = registry_globals_names[data->wl_seat_id];2221RegistryGlobalInfo &global_seat_info = registry_globals[global_seat_name];2222WaylandSeatGlobalData *global_seat_data = (WaylandSeatGlobalData *)global_seat_info.data;2223ERR_FAIL_NULL_V(global_seat_data, MessageStatus::ERROR);22242225WaylandSeatInstanceData *seat_data = (WaylandSeatInstanceData *)object->data;2226ERR_FAIL_NULL_V(seat_data, MessageStatus::ERROR);22272228if (p_opcode == WL_POINTER_BUTTON && global_seat_data->pointed_surface_id != INVALID_ID) {2229// [Event] wl_pointer::button(uuuu);2230uint32_t button = body[2];2231uint32_t state = body[3];22322233DEBUG_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));22342235bool client_pointed = client->local_ids.has(global_seat_data->pointed_surface_id);22362237if (button != BTN_LEFT || state != WL_POINTER_BUTTON_STATE_RELEASED) {2238return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;2239}22402241if (global_seat_data->focused_surface_id == global_seat_data->pointed_surface_id) {2242return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;2243}22442245if (!global_surface_is_window(global_seat_data->pointed_surface_id)) {2246return client_pointed ? MessageStatus::UNHANDLED : MessageStatus::HANDLED;2247}22482249if (global_seat_data->focused_surface_id != INVALID_ID) {2250seat_name_leave_surface(global_seat_name, global_seat_data->focused_surface_id);2251}22522253global_seat_data->focused_surface_id = global_seat_data->pointed_surface_id;2254seat_name_enter_surface(global_seat_name, global_seat_data->focused_surface_id);2255} else if (p_opcode == WL_POINTER_ENTER) {2256// [Event] wl_pointer::enter(uoff).2257uint32_t surface = body[1];2258WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(surface)->data;2259ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);22602261if (global_seat_data->pointed_surface_id != surface) {2262DEBUG_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));22632264global_seat_data->pointed_surface_id = surface;2265}2266} else if (p_opcode == WL_POINTER_LEAVE) {2267// [Event] wl_pointer::leave(uo).2268uint32_t surface = body[1];2269WaylandSurfaceData *surface_data = (WaylandSurfaceData *)get_object(surface)->data;2270ERR_FAIL_NULL_V(surface_data, MessageStatus::ERROR);22712272if (global_seat_data->pointed_surface_id == surface) {2273DEBUG_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));2274global_seat_data->pointed_surface_id = INVALID_ID;2275}2276}22772278return MessageStatus::UNHANDLED;2279}22802281if (object->interface == &xdg_popup_interface) {2282if (p_opcode == XDG_POPUP_CONFIGURE) {2283// [Event] xdg_popup::configure(iiii);2284int32_t x = body[0];2285int32_t y = body[1];2286int32_t width = body[2];2287int32_t height = body[3];22882289XdgPopupData *data = (XdgPopupData *)object->data;2290ERR_FAIL_NULL_V(data, MessageStatus::ERROR);22912292XdgSurfaceData *parent_xdg_surf_data = (XdgSurfaceData *)data->parent_handle.get()->data;2293ERR_FAIL_NULL_V(parent_xdg_surf_data, MessageStatus::ERROR);22942295WaylandSurfaceData *parent_surface_data = (WaylandSurfaceData *)get_object(parent_xdg_surf_data->wl_surface_id)->data;2296ERR_FAIL_NULL_V(parent_surface_data, MessageStatus::ERROR);22972298WaylandObject *parent_role_obj = parent_surface_data->role_object_handle.get();2299ERR_FAIL_NULL_V(parent_role_obj, MessageStatus::ERROR);23002301if (parent_role_obj->interface == &xdg_toplevel_interface) {2302XdgToplevelData *parent_toplevel_data = (XdgToplevelData *)parent_role_obj->data;2303ERR_FAIL_NULL_V(parent_toplevel_data, MessageStatus::ERROR);23042305if (parent_toplevel_data->is_embedded()) {2306WaylandSubsurfaceData *subsurf_data = (WaylandSubsurfaceData *)get_object(parent_toplevel_data->wl_subsurface_id)->data;2307ERR_FAIL_NULL_V(subsurf_data, MessageStatus::ERROR);23082309// The coordinates passed will be shifted by the embedded window position,2310// so we need to fix them back.2311Point2i fixed_position = Point2i(x, y) - subsurf_data->position;23122313DEBUG_LOG_WAYLAND_EMBED(vformat("Correcting popup configure position to %s", fixed_position));23142315send_wayland_message(client->socket, local_id, p_opcode, { (uint32_t)fixed_position.x, (uint32_t)fixed_position.y, (uint32_t)width, (uint32_t)height });23162317return MessageStatus::HANDLED;2318}2319}2320}2321}23222323return MessageStatus::UNHANDLED;2324}23252326void WaylandEmbedder::shutdown() {2327thread_done.set();23282329{2330// First making a list of all clients so that we can iteratively delete them.2331LocalVector<int> sockets;2332for (KeyValue<int, Client> &pair : clients) {2333sockets.push_back(pair.key);2334}23352336for (int socket : sockets) {2337cleanup_socket(socket);2338}2339}23402341close(compositor_socket);2342compositor_socket = -1;23432344for (KeyValue<uint32_t, RegistryGlobalInfo> &pair : registry_globals) {2345RegistryGlobalInfo &info = pair.value;2346if (info.data) {2347memdelete(info.data);2348info.data = nullptr;2349}2350}2351}23522353Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *info, uint32_t *buf, LocalVector<int> &r_sent_fds) {2354ERR_FAIL_NULL_V(info, ERR_BUG);2355ERR_FAIL_NULL_V_MSG(info->direction == ProxyDirection::COMPOSITOR && client, ERR_BUG, "Wait, where did this message come from?");23562357WaylandObject *object = nullptr;23582359uint32_t global_id = INVALID_ID;2360if (info->direction == ProxyDirection::CLIENT) {2361global_id = info->raw_id;2362} else if (info->direction == ProxyDirection::COMPOSITOR) {2363global_id = client->get_global_id(info->raw_id);2364}23652366if (global_id != INVALID_ID) {2367object = get_object(global_id);2368} else if (client) {2369object = client->get_object(info->raw_id);2370}23712372if (object == nullptr) {2373if (info->direction == ProxyDirection::COMPOSITOR) {2374uint32_t local_id = info->raw_id;2375ERR_PRINT(vformat("Couldn't find requested object l0x%x for client %d, disconnecting.", local_id, client->socket));23762377socket_error(client->socket, local_id, WL_DISPLAY_ERROR_INVALID_OBJECT, vformat("Object l0x%x not found.", local_id));2378return OK;2379} else {2380CRASH_NOW_MSG(vformat("No object found for r0x%x", info->raw_id));2381}2382}23832384const struct wl_interface *interface = nullptr;2385interface = object->interface;23862387if (interface == nullptr && info->raw_id & 0xff000000) {2388// Regular clients have no confirmation about deleted server objects (why2389// should they?) but since we share connections there's the risk of receiving2390// messages about deleted server objects. The simplest solution is to ignore2391// unknown server-side objects. Not the safest thing, I know, but it should do2392// the job.2393DEBUG_LOG_WAYLAND_EMBED(vformat("Ignoring unknown server-side object r0x%x", info->raw_id));2394return OK;2395}23962397ERR_FAIL_NULL_V_MSG(interface, ERR_BUG, vformat("Object r0x%x has no interface", info->raw_id));23982399const struct wl_message *message = nullptr;2400if (info->direction == ProxyDirection::CLIENT) {2401ERR_FAIL_COND_V(info->opcode >= interface->event_count, ERR_BUG);2402message = &interface->events[info->opcode];2403} else {2404ERR_FAIL_COND_V(info->opcode >= interface->method_count, ERR_BUG);2405message = &interface->methods[info->opcode];2406}2407ERR_FAIL_NULL_V(message, ERR_BUG);24082409int fds_requested = String(message->signature).count("h");2410if (fds_requested > 0) {2411DEBUG_LOG_WAYLAND_EMBED(vformat("Requested %d FDs.", fds_requested));24122413List<int> &fd_queue = info->direction == ProxyDirection::COMPOSITOR ? client->fds : compositor_fds;2414for (int i = 0; i < fds_requested; ++i) {2415ERR_FAIL_COND_V_MSG(fd_queue.is_empty(), ERR_BUG, "Out of FDs.");2416DEBUG_LOG_WAYLAND_EMBED(vformat("Fetching FD %d.", fd_queue.front()->get()));2417r_sent_fds.push_back(fd_queue.front()->get());2418fd_queue.pop_front();2419}24202421DEBUG_LOG_WAYLAND_EMBED(vformat("Remaining FDs: %d.", fd_queue.size()));2422}24232424if (object->destroyed) {2425DEBUG_LOG_WAYLAND_EMBED("Ignoring message for inert object.");24262427// Inert object.2428return OK;2429}24302431if (info->direction == ProxyDirection::COMPOSITOR) {2432MessageStatus request_status = handle_request(LocalObjectHandle(client, info->raw_id), info->opcode, buf, info->size);2433if (request_status == MessageStatus::ERROR) {2434return ERR_BUG;2435}24362437if (request_status == MessageStatus::HANDLED) {2438DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");2439return OK;2440}24412442if (global_id != INVALID_ID) {2443buf[0] = global_id;2444}24452446DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");24472448if (handle_generic_msg(client, object, message, info, buf)) {2449send_raw_message(compositor_socket, { { buf, info->size } }, r_sent_fds);2450}2451} else {2452uint32_t global_name = 0;24532454bool is_global = false;2455if (registry_globals_names.has(global_id)) {2456global_name = registry_globals_names[global_id];2457is_global = true;2458}24592460// FIXME: For compatibility, mirror events with instanced registry globals as2461// object arguments. For example, `wl_surface.enter` returns a `wl_output`. If2462// said `wl_output` has been instanced multiple times, we need to resend the2463// same event with each instance as the argument, or the client might miss the2464// event by looking for the "wrong" instance.2465//2466// Note that this missing behavior is exclusively a compatibility mechanism2467// for old compositors which only implement undestroyable globals. We2468// otherwise passthrough every bind request and then the compositor takes care2469// of everything.2470// See: https://lore.freedesktop.org/wayland-devel/[email protected]/2471if (object->shared) {2472bool handled = false;24732474for (KeyValue<int, Client> &pair : clients) {2475Client &c = pair.value;2476if (c.socket < 0) {2477continue;2478}24792480if (!c.local_ids.has(global_id)) {2481DEBUG_LOG_WAYLAND_EMBED("!!!!!!!!!!! Instance missing?");2482continue;2483}24842485if (is_global) {2486if (!c.registry_globals_instances.has(global_name)) {2487continue;2488}24892490DEBUG_LOG_WAYLAND_EMBED(vformat("Broadcasting to all global instances for client %d (socket %d)", c.pid, c.socket));2491for (uint32_t instance_id : c.registry_globals_instances[global_name]) {2492DEBUG_LOG_WAYLAND_EMBED(vformat("Global instance l0x%x", instance_id));24932494LocalObjectHandle local_obj = LocalObjectHandle(&c, instance_id);2495if (!local_obj.is_valid()) {2496continue;2497}24982499MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);25002501if (event_status == MessageStatus::ERROR) {2502return ERR_BUG;2503}25042505if (event_status == MessageStatus::HANDLED) {2506DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");2507handled = true;2508continue;2509}25102511if (event_status == MessageStatus::INVALID) {2512continue;2513}25142515DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");25162517buf[0] = instance_id;25182519if (handle_generic_msg(&c, local_obj.get(), message, info, buf, instance_id)) {2520send_raw_message(c.socket, { { buf, info->size } }, r_sent_fds);2521}25222523handled = true;2524}2525} else if (interface == &wl_display_interface) {2526// NOTE: The only shared non-global objects are `wl_display` and2527// `wl_registry`, both of which require custom handlers. Additionally, of2528// those only `wl_display` has client-specific handlers, which is what this2529// branch manages.25302531LocalObjectHandle local_obj = LocalObjectHandle(&c, c.get_local_id(global_id));2532if (!local_obj.is_valid()) {2533continue;2534}25352536DEBUG_LOG_WAYLAND_EMBED(vformat("Shared non-global l0x%x g0x%x", c.get_local_id(global_id), global_id));25372538MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);2539if (event_status == MessageStatus::ERROR) {2540return ERR_BUG;2541}25422543if (event_status == MessageStatus::HANDLED) {2544DEBUG_LOG_WAYLAND_EMBED("Custom handler success.");2545handled = true;2546continue;2547}25482549if (event_status == MessageStatus::INVALID) {2550continue;2551}25522553DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler.");25542555if (handle_generic_msg(&c, local_obj.get(), message, info, buf)) {2556send_raw_message(c.socket, { { buf, info->size } }, r_sent_fds);2557}25582559handled = true;2560}2561}25622563if (!handled) {2564// No client handled this, it's going to be handled as a client-less event.2565// We do this only at the end to avoid handling certain events (e.g.2566// deletion) twice.2567handle_event(global_id, LocalObjectHandle(nullptr, INVALID_ID), info->opcode, buf, info->size);2568}2569} else {2570LocalObjectHandle local_obj = LocalObjectHandle(client, client ? client->get_local_id(global_id) : INVALID_ID);25712572MessageStatus event_status = handle_event(global_id, local_obj, info->opcode, buf, info->size);2573if (event_status == MessageStatus::ERROR) {2574return ERR_BUG;2575}25762577if (event_status == MessageStatus::HANDLED || event_status == MessageStatus::INVALID) {2578// We're done.2579return OK;2580}25812582// Generic passthrough.25832584if (client) {2585uint32_t local_id = client->get_local_id(global_id);2586ERR_FAIL_COND_V(local_id == INVALID_ID, OK);25872588DEBUG_LOG_WAYLAND_EMBED(vformat("%s::%s(%s) g0x%x -> l0x%x", interface->name, message->name, message->signature, global_id, local_id));2589buf[0] = local_id;25902591if (handle_generic_msg(client, local_obj.get(), message, info, buf)) {2592send_raw_message(client->socket, { { buf, info->size } }, r_sent_fds);2593}2594} else {2595WARN_PRINT_ONCE(vformat("[Wayland Embedder] Unexpected client-less event from %s#g0x%x. Object has probably leaked.", object->interface->name, global_id));2596handle_generic_msg(nullptr, object, message, info, buf);2597}2598}2599}26002601return OK;2602}26032604Error WaylandEmbedder::handle_sock(int p_fd) {2605ERR_FAIL_COND_V(p_fd < 0, ERR_INVALID_PARAMETER);26062607struct msg_info info = {};26082609{2610struct msghdr head_msg = {};2611uint32_t header[2];2612struct iovec vec = { header, sizeof header };26132614head_msg.msg_iov = &vec;2615head_msg.msg_iovlen = 1;26162617ssize_t head_rec = recvmsg(p_fd, &head_msg, MSG_PEEK);26182619if (head_rec == 0) {2620// Client disconnected.2621return ERR_CONNECTION_ERROR;2622}26232624if (head_rec == -1) {2625if (errno == ECONNRESET) {2626// No need to print the error, the client forcefully disconnected, that's2627// fine.2628return ERR_CONNECTION_ERROR;2629}26302631ERR_FAIL_V_MSG(FAILED, vformat("Can't read message header: %s", strerror(errno)));2632}26332634ERR_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));26352636// Header is two 32-bit words: first is ID, second has size in most significant2637// half and opcode in the other half.2638info.raw_id = header[0];2639info.size = header[1] >> 16;2640info.opcode = header[1] & 0xFFFF;2641info.direction = p_fd != compositor_socket ? ProxyDirection::COMPOSITOR : ProxyDirection::CLIENT;2642}26432644if (msg_buf.size() < info.words()) {2645msg_buf.resize(info.words());2646}26472648ERR_FAIL_COND_V_MSG(info.size % WL_WORD_SIZE != 0, ERR_CONNECTION_ERROR, "Invalid message length.");26492650struct msghdr full_msg = {};2651struct iovec vec = { msg_buf.ptr(), info.size };2652{2653full_msg.msg_iov = &vec;2654full_msg.msg_iovlen = 1;2655full_msg.msg_control = ancillary_buf.ptr();2656full_msg.msg_controllen = ancillary_buf.size();26572658ssize_t full_rec = recvmsg(p_fd, &full_msg, 0);26592660if (full_rec == -1) {2661if (errno == ECONNRESET) {2662// No need to print the error, the client forcefully disconnected, that's2663// fine.2664return ERR_CONNECTION_ERROR;2665}26662667ERR_FAIL_V_MSG(FAILED, vformat("Can't read message: %s", strerror(errno)));2668}26692670ERR_FAIL_COND_V_MSG(((size_t)full_rec) != info.size, ERR_CONNECTION_ERROR, "Invalid message length.");26712672DEBUG_LOG_WAYLAND_EMBED(" === START PACKET === ");26732674#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED2675printf("[PROXY] Received bytes: ");2676for (ssize_t i = 0; i < full_rec; ++i) {2677printf("%.2x", ((const uint8_t *)msg_buf.ptr())[i]);2678}2679printf("\n");2680#endif2681}26822683if (full_msg.msg_controllen > 0) {2684struct cmsghdr *cmsg = CMSG_FIRSTHDR(&full_msg);2685while (cmsg) {2686// TODO: Check for validity of message fields.2687size_t data_len = cmsg->cmsg_len - sizeof *cmsg;26882689if (cmsg->cmsg_type == SCM_RIGHTS) {2690// NOTE: Linux docs say that we can't just cast data to pointer type because2691// of alignment concerns. So we have to memcpy into a new buffer.2692int *cmsg_fds = (int *)malloc(data_len);2693memcpy(cmsg_fds, CMSG_DATA(cmsg), data_len);26942695size_t cmsg_fds_count = data_len / sizeof *cmsg_fds;2696for (size_t i = 0; i < cmsg_fds_count; ++i) {2697int fd = cmsg_fds[i];26982699if (info.direction == ProxyDirection::COMPOSITOR) {2700clients[p_fd].fds.push_back(fd);2701} else {2702compositor_fds.push_back(fd);2703}2704}27052706#ifdef WAYLAND_EMBED_DEBUG_LOGS_ENABLED2707printf("[PROXY] Received %ld file descriptors: ", cmsg_fds_count);2708for (size_t i = 0; i < cmsg_fds_count; ++i) {2709printf("%d ", cmsg_fds[i]);2710}2711printf("\n");2712#endif27132714free(cmsg_fds);2715}27162717cmsg = CMSG_NXTHDR(&full_msg, cmsg);2718}2719}2720full_msg.msg_control = nullptr;2721full_msg.msg_controllen = 0;27222723Client *client = nullptr;2724if (p_fd == compositor_socket) {2725// Let's figure out the recipient of the message.2726for (KeyValue<int, Client> &pair : clients) {2727Client &c = pair.value;27282729if (c.local_ids.has(info.raw_id)) {2730client = &c;2731}2732}2733} else {2734CRASH_COND(!clients.has(p_fd));2735client = &clients[p_fd];2736}27372738LocalVector<int> sent_fds;2739Error err = handle_msg_info(client, &info, msg_buf.ptr(), sent_fds);27402741for (int fd : sent_fds) {2742DEBUG_LOG_WAYLAND_EMBED(vformat("Closing fd %d.", fd));2743close(fd);2744}27452746DEBUG_LOG_WAYLAND_EMBED(" === END PACKET === ");27472748if (err != OK) {2749return ERR_BUG;2750}27512752return OK;2753}27542755void WaylandEmbedder::_thread_loop(void *p_data) {2756Thread::set_name("Wayland Embed");27572758ERR_FAIL_NULL(p_data);2759WaylandEmbedder *proxy = (WaylandEmbedder *)p_data;27602761DEBUG_LOG_WAYLAND_EMBED("Proxy thread started");27622763while (!proxy->thread_done.is_set()) {2764proxy->poll_sockets();2765}2766}27672768Error WaylandEmbedder::init() {2769ancillary_buf.resize(EMBED_ANCILLARY_BUF_SIZE);27702771proxy_socket = socket(AF_UNIX, SOCK_STREAM, 0);27722773struct sockaddr_un addr = {};2774addr.sun_family = AF_UNIX;27752776String runtime_dir_path = OS::get_singleton()->get_environment("XDG_RUNTIME_DIR");2777ERR_FAIL_COND_V_MSG(runtime_dir_path.is_empty(), ERR_DOES_NOT_EXIST, "XDG_RUNTIME_DIR is not set or empty.");27782779runtime_dir = DirAccess::create_for_path(runtime_dir_path);2780ERR_FAIL_COND_V(!runtime_dir.is_valid(), ERR_BUG);2781ERR_FAIL_COND_V_MSG(!runtime_dir->is_writable(runtime_dir_path), ERR_FILE_CANT_WRITE, "XDG_RUNTIME_DIR points to an invalid directory.");27822783int socket_id = 0;2784while (socket_path.is_empty()) {2785String test_socket_path = runtime_dir_path + "/godot-wayland-" + itos(socket_id);2786String test_socket_lock_path = test_socket_path + ".lock";27872788print_verbose(vformat("Trying to get socket %s", test_socket_path));2789print_verbose(vformat("Opening lock %s", test_socket_lock_path));2790int test_lock_fd = open(test_socket_lock_path.utf8().get_data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);27912792if (flock(test_lock_fd, LOCK_EX | LOCK_NB) == -1) {2793print_verbose(vformat("Can't lock %s", test_socket_lock_path));2794close(test_lock_fd);2795++socket_id;2796continue;2797} else {2798lock_fd = test_lock_fd;2799socket_path = test_socket_path;2800socket_lock_path = test_socket_lock_path;28012802break;2803}2804}28052806DirAccess::remove_absolute(socket_path);2807strncpy(addr.sun_path, socket_path.utf8().get_data(), sizeof(addr.sun_path) - 1);28082809if (bind(proxy_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {2810ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't bind embedding socket.");2811}28122813if (listen(proxy_socket, 1) == -1) {2814ERR_FAIL_V_MSG(ERR_CANT_OPEN, "Can't listen embedding socket.");2815}28162817struct wl_display *display = wl_display_connect(nullptr);2818ERR_FAIL_NULL_V(display, ERR_CANT_OPEN);2819compositor_socket = wl_display_get_fd(display);28202821pollfds.push_back({ proxy_socket, POLLIN, 0 });2822pollfds.push_back({ compositor_socket, POLLIN, 0 });28232824RegistryGlobalInfo control_global_info = {};2825control_global_info.interface = &godot_embedding_compositor_interface;2826control_global_info.version = godot_embedding_compositor_interface.version;28272828godot_embedding_compositor_name = registry_globals_counter++;2829registry_globals[godot_embedding_compositor_name] = control_global_info;28302831{2832uint32_t invalid_id = INVALID_ID;2833objects.request(invalid_id);28342835CRASH_COND(invalid_id != INVALID_ID);2836}28372838{2839uint32_t display_id = new_object(&wl_display_interface);2840CRASH_COND(display_id != DISPLAY_ID);28412842get_object(DISPLAY_ID)->shared = true;2843}28442845{2846uint32_t registry_id = new_object(&wl_registry_interface);2847CRASH_COND(registry_id != REGISTRY_ID);28482849get_object(REGISTRY_ID)->shared = true;2850}28512852// wl_display::get_registry(n)2853send_wayland_message(compositor_socket, DISPLAY_ID, 1, { REGISTRY_ID });28542855sync();28562857proxy_thread.start(_thread_loop, this);28582859return OK;2860}28612862void WaylandEmbedder::handle_fd(int p_fd, int p_revents) {2863if (p_fd == proxy_socket && p_revents & POLLIN) {2864// Client init.2865int new_fd = accept(proxy_socket, nullptr, nullptr);2866ERR_FAIL_COND_MSG(new_fd == -1, "Failed to accept client.");28672868struct ucred cred = {};2869socklen_t cred_size = sizeof cred;2870getsockopt(new_fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_size);28712872Client &client = clients.insert_new(new_fd, {})->value;28732874client.embedder = this;2875client.socket = new_fd;2876client.pid = cred.pid;28772878client.global_ids[DISPLAY_ID] = Client::GlobalIdInfo(DISPLAY_ID, nullptr);2879client.local_ids[DISPLAY_ID] = DISPLAY_ID;28802881pollfds.push_back({ new_fd, POLLIN, 0 });28822883if (main_client == nullptr) {2884main_client = &client;2885}28862887if (new_fd != main_client->socket && main_client->registry_globals_instances.has(godot_embedding_compositor_name)) {2888uint32_t new_local_id = main_client->allocate_server_id();28892890client.embedded_client_id = new_local_id;28912892for (uint32_t local_id : main_client->registry_globals_instances[godot_embedding_compositor_name]) {2893EmbeddedClientData *eclient_data = memnew(EmbeddedClientData);2894eclient_data->client = &client;28952896main_client->new_fake_object(new_local_id, &godot_embedded_client_interface, 1, eclient_data);28972898// godot_embedding_compositor::client(nu)2899send_wayland_message(main_client->socket, local_id, 0, { new_local_id, (uint32_t)cred.pid });2900}2901}29022903DEBUG_LOG_WAYLAND_EMBED(vformat("New client %d (pid %d) initialized.", client.socket, cred.pid));2904return;2905}29062907if (p_fd == compositor_socket && p_revents & POLLIN) {2908Error err = handle_sock(p_fd);29092910if (err == ERR_BUG) {2911ERR_PRINT("Unexpected error while handling socket, shutting down.");2912shutdown();2913return;2914}29152916return;2917}29182919const Client *client = clients.getptr(p_fd);2920if (client) {2921if (main_client && client == main_client && p_revents & (POLLHUP | POLLERR)) {2922DEBUG_LOG_WAYLAND_EMBED("Main client disconnected, shutting down.");2923shutdown();2924return;2925}29262927if (p_revents & POLLIN) {2928Error err = handle_sock(p_fd);2929if (err == ERR_BUG) {2930ERR_PRINT("Unexpected error while handling socket, shutting down.");2931shutdown();2932return;2933}29342935if (err != OK) {2936DEBUG_LOG_WAYLAND_EMBED("disconnecting");2937cleanup_socket(p_fd);2938return;2939}29402941return;2942} else if (p_revents & (POLLHUP | POLLERR | POLLNVAL)) {2943if (p_revents & POLLHUP) {2944DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d hangup.", p_fd));2945}2946if (p_revents & POLLERR) {2947DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d error.", p_fd));2948}2949if (p_revents & POLLNVAL) {2950DEBUG_LOG_WAYLAND_EMBED(vformat("Socket %d invalid FD.", p_fd));2951}29522953cleanup_socket(p_fd);29542955return;2956}2957}2958}29592960WaylandEmbedder::~WaylandEmbedder() {2961shutdown();2962if (proxy_thread.is_started()) {2963proxy_thread.wait_to_finish();2964}2965}29662967#endif // TOOLS_ENABLED29682969#endif // WAYLAND_ENABLED297029712972