Path: blob/master/platform/linuxbsd/wayland/display_server_wayland.cpp
20801 views
/**************************************************************************/1/* display_server_wayland.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 "display_server_wayland.h"3132#ifdef WAYLAND_ENABLED3334#define WAYLAND_DISPLAY_SERVER_DEBUG_LOGS_ENABLED35#ifdef WAYLAND_DISPLAY_SERVER_DEBUG_LOGS_ENABLED36#define DEBUG_LOG_WAYLAND(...) print_verbose(__VA_ARGS__)37#else38#define DEBUG_LOG_WAYLAND(...)39#endif4041#include "core/input/input.h"42#include "core/os/main_loop.h"43#include "servers/rendering/dummy/rasterizer_dummy.h"4445#ifdef VULKAN_ENABLED46#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"47#endif4849#ifdef GLES3_ENABLED50#include "core/io/file_access.h"51#include "detect_prime_egl.h"52#include "drivers/gles3/rasterizer_gles3.h"53#include "wayland/egl_manager_wayland.h"54#include "wayland/egl_manager_wayland_gles.h"55#endif5657#ifdef ACCESSKIT_ENABLED58#include "drivers/accesskit/accessibility_driver_accesskit.h"59#endif6061#ifdef DBUS_ENABLED62#ifdef SOWRAP_ENABLED63#include "dbus-so_wrap.h"64#else65#include <dbus/dbus.h>66#endif67#endif6869#define WAYLAND_MAX_FRAME_TIME_US (1'000'000)7071String DisplayServerWayland::_get_app_id_from_context(Context p_context) {72String app_id;7374switch (p_context) {75case CONTEXT_EDITOR: {76app_id = "org.godotengine.Editor";77} break;7879case CONTEXT_PROJECTMAN: {80app_id = "org.godotengine.ProjectManager";81} break;8283case CONTEXT_ENGINE:84default: {85String config_name = GLOBAL_GET("application/config/name");86if (config_name.length() != 0) {87app_id = config_name;88} else {89app_id = "org.godotengine.Godot";90}91}92}9394return app_id;95}9697void DisplayServerWayland::_send_window_event(WindowEvent p_event, WindowID p_window_id) {98ERR_FAIL_COND(!windows.has(p_window_id));99100WindowData &wd = windows[p_window_id];101102if (wd.window_event_callback.is_valid()) {103Variant event = int(p_event);104wd.window_event_callback.call(event);105}106}107108void DisplayServerWayland::dispatch_input_events(const Ref<InputEvent> &p_event) {109static_cast<DisplayServerWayland *>(get_singleton())->_dispatch_input_event(p_event);110}111112void DisplayServerWayland::_dispatch_input_event(const Ref<InputEvent> &p_event) {113Ref<InputEventFromWindow> event_from_window = p_event;114115if (event_from_window.is_valid()) {116WindowID window_id = event_from_window->get_window_id();117118Ref<InputEventKey> key_event = p_event;119if (!popup_menu_list.is_empty() && key_event.is_valid()) {120// Redirect to the highest popup menu.121window_id = popup_menu_list.back()->get();122}123124// Send to a single window.125if (windows.has(window_id)) {126Callable callable = windows[window_id].input_event_callback;127if (callable.is_valid()) {128callable.call(p_event);129}130}131} else {132// Send to all windows. Copy all pending callbacks, since callback can erase window.133Vector<Callable> cbs;134for (KeyValue<WindowID, WindowData> &E : windows) {135Callable callable = E.value.input_event_callback;136if (callable.is_valid()) {137cbs.push_back(callable);138}139}140141for (const Callable &cb : cbs) {142cb.call(p_event);143}144}145}146147void DisplayServerWayland::_update_window_rect(const Rect2i &p_rect, WindowID p_window_id) {148ERR_FAIL_COND(!windows.has(p_window_id));149150WindowData &wd = windows[p_window_id];151152if (wd.rect == p_rect) {153return;154}155156wd.rect = p_rect;157158#ifdef RD_ENABLED159if (wd.visible && rendering_context) {160rendering_context->window_set_size(p_window_id, wd.rect.size.width, wd.rect.size.height);161}162#endif163164#ifdef GLES3_ENABLED165if (wd.visible && egl_manager) {166wl_egl_window_resize(wd.wl_egl_window, wd.rect.size.width, wd.rect.size.height, 0, 0);167}168#endif169170if (wd.rect_changed_callback.is_valid()) {171wd.rect_changed_callback.call(wd.rect);172}173}174175// Interface methods.176177bool DisplayServerWayland::has_feature(Feature p_feature) const {178switch (p_feature) {179#ifndef DISABLE_DEPRECATED180case FEATURE_GLOBAL_MENU: {181return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));182} break;183#endif184case FEATURE_MOUSE:185case FEATURE_MOUSE_WARP:186case FEATURE_CLIPBOARD:187case FEATURE_CURSOR_SHAPE:188case FEATURE_CUSTOM_CURSOR_SHAPE:189case FEATURE_WINDOW_TRANSPARENCY:190case FEATURE_ICON:191case FEATURE_HIDPI:192case FEATURE_SWAP_BUFFERS:193case FEATURE_KEEP_SCREEN_ON:194case FEATURE_IME:195case FEATURE_WINDOW_DRAG:196case FEATURE_CLIPBOARD_PRIMARY:197case FEATURE_SUBWINDOWS:198case FEATURE_WINDOW_EMBEDDING:199case FEATURE_SELF_FITTING_WINDOWS: {200return true;201} break;202203//case FEATURE_NATIVE_DIALOG:204//case FEATURE_NATIVE_DIALOG_INPUT:205#ifdef DBUS_ENABLED206case FEATURE_NATIVE_DIALOG_FILE:207case FEATURE_NATIVE_DIALOG_FILE_EXTRA:208case FEATURE_NATIVE_DIALOG_FILE_MIME: {209return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_file_chooser_supported());210} break;211case FEATURE_NATIVE_COLOR_PICKER: {212return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_screenshot_supported());213} break;214#endif215216#ifdef SPEECHD_ENABLED217case FEATURE_TEXT_TO_SPEECH: {218return true;219} break;220#endif221222#ifdef ACCESSKIT_ENABLED223case FEATURE_ACCESSIBILITY_SCREEN_READER: {224return (accessibility_driver != nullptr);225} break;226#endif227228default: {229return false;230}231}232}233234String DisplayServerWayland::get_name() const {235return "Wayland";236}237238#ifdef SPEECHD_ENABLED239240void DisplayServerWayland::initialize_tts() const {241const_cast<DisplayServerWayland *>(this)->tts = memnew(TTS_Linux);242}243244bool DisplayServerWayland::tts_is_speaking() const {245if (unlikely(!tts)) {246initialize_tts();247}248ERR_FAIL_NULL_V(tts, false);249return tts->is_speaking();250}251252bool DisplayServerWayland::tts_is_paused() const {253if (unlikely(!tts)) {254initialize_tts();255}256ERR_FAIL_NULL_V(tts, false);257return tts->is_paused();258}259260TypedArray<Dictionary> DisplayServerWayland::tts_get_voices() const {261if (unlikely(!tts)) {262initialize_tts();263}264ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());265return tts->get_voices();266}267268void DisplayServerWayland::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int64_t p_utterance_id, bool p_interrupt) {269if (unlikely(!tts)) {270initialize_tts();271}272ERR_FAIL_NULL(tts);273tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);274}275276void DisplayServerWayland::tts_pause() {277if (unlikely(!tts)) {278initialize_tts();279}280ERR_FAIL_NULL(tts);281tts->pause();282}283284void DisplayServerWayland::tts_resume() {285if (unlikely(!tts)) {286initialize_tts();287}288ERR_FAIL_NULL(tts);289tts->resume();290}291292void DisplayServerWayland::tts_stop() {293if (unlikely(!tts)) {294initialize_tts();295}296ERR_FAIL_NULL(tts);297tts->stop();298}299300#endif301302#ifdef DBUS_ENABLED303304bool DisplayServerWayland::is_dark_mode_supported() const {305return portal_desktop && portal_desktop->is_supported() && portal_desktop->is_settings_supported();306}307308bool DisplayServerWayland::is_dark_mode() const {309if (!is_dark_mode_supported()) {310return false;311}312switch (portal_desktop->get_appearance_color_scheme()) {313case 1:314// Prefers dark theme.315return true;316case 2:317// Prefers light theme.318return false;319default:320// Preference unknown.321return false;322}323}324325Color DisplayServerWayland::get_accent_color() const {326if (!portal_desktop) {327return Color();328}329return portal_desktop->get_appearance_accent_color();330}331332void DisplayServerWayland::set_system_theme_change_callback(const Callable &p_callable) {333ERR_FAIL_COND(!portal_desktop);334portal_desktop->set_system_theme_change_callback(p_callable);335}336337Error DisplayServerWayland::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) {338ERR_FAIL_COND_V(!portal_desktop, ERR_UNAVAILABLE);339MutexLock mutex_lock(wayland_thread.mutex);340341WindowID window_id = p_window_id;342if (!windows.has(window_id) || window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, window_id)) {343window_id = MAIN_WINDOW_ID;344}345346WaylandThread::WindowState *ws = wayland_thread.window_get_state(window_id);347ERR_FAIL_NULL_V(ws, ERR_BUG);348349return portal_desktop->file_dialog_show(window_id, (ws ? ws->exported_handle : String()), p_title, p_current_directory, String(), p_filename, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);350}351352Error DisplayServerWayland::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, WindowID p_window_id) {353ERR_FAIL_COND_V(!portal_desktop, ERR_UNAVAILABLE);354MutexLock mutex_lock(wayland_thread.mutex);355356WindowID window_id = p_window_id;357if (!windows.has(window_id) || window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, window_id)) {358window_id = MAIN_WINDOW_ID;359}360361WaylandThread::WindowState *ws = wayland_thread.window_get_state(window_id);362ERR_FAIL_NULL_V(ws, ERR_BUG);363364return portal_desktop->file_dialog_show(window_id, (ws ? ws->exported_handle : String()), p_title, p_current_directory, p_root, p_filename, p_mode, p_filters, p_options, p_callback, true);365}366367#endif368369void DisplayServerWayland::beep() const {370wayland_thread.beep();371}372373void DisplayServerWayland::_mouse_update_mode() {374MouseMode wanted_mouse_mode = mouse_mode_override_enabled375? mouse_mode_override376: mouse_mode_base;377378if (wanted_mouse_mode == mouse_mode) {379return;380}381382MutexLock mutex_lock(wayland_thread.mutex);383384bool show_cursor = (wanted_mouse_mode == MOUSE_MODE_VISIBLE || wanted_mouse_mode == MOUSE_MODE_CONFINED);385386wayland_thread.cursor_set_visible(show_cursor);387388WaylandThread::PointerConstraint constraint = WaylandThread::PointerConstraint::NONE;389390switch (wanted_mouse_mode) {391case DisplayServer::MOUSE_MODE_CAPTURED: {392constraint = WaylandThread::PointerConstraint::LOCKED;393} break;394395case DisplayServer::MOUSE_MODE_CONFINED:396case DisplayServer::MOUSE_MODE_CONFINED_HIDDEN: {397constraint = WaylandThread::PointerConstraint::CONFINED;398} break;399400default: {401}402}403404wayland_thread.pointer_set_constraint(constraint);405406if (wanted_mouse_mode == DisplayServer::MOUSE_MODE_CAPTURED) {407WindowData *pointed_win = windows.getptr(wayland_thread.pointer_get_pointed_window_id());408ERR_FAIL_NULL(pointed_win);409wayland_thread.pointer_set_hint(pointed_win->rect.size / 2);410}411412mouse_mode = wanted_mouse_mode;413}414415void DisplayServerWayland::mouse_set_mode(MouseMode p_mode) {416ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);417if (p_mode == mouse_mode_base) {418return;419}420mouse_mode_base = p_mode;421_mouse_update_mode();422}423424DisplayServerWayland::MouseMode DisplayServerWayland::mouse_get_mode() const {425return mouse_mode;426}427428void DisplayServerWayland::mouse_set_mode_override(MouseMode p_mode) {429ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);430if (p_mode == mouse_mode_override) {431return;432}433mouse_mode_override = p_mode;434_mouse_update_mode();435}436437DisplayServerWayland::MouseMode DisplayServerWayland::mouse_get_mode_override() const {438return mouse_mode_override;439}440441void DisplayServerWayland::mouse_set_mode_override_enabled(bool p_override_enabled) {442if (p_override_enabled == mouse_mode_override_enabled) {443return;444}445mouse_mode_override_enabled = p_override_enabled;446_mouse_update_mode();447}448449bool DisplayServerWayland::mouse_is_mode_override_enabled() const {450return mouse_mode_override_enabled;451}452453// NOTE: This is hacked together (and not guaranteed to work in the first place)454// as for some reason the there's no proper way to ask the compositor to warp455// the pointer, although, at the time of writing, there's a proposal for a456// proper protocol for this. See:457// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/158458void DisplayServerWayland::warp_mouse(const Point2i &p_to) {459MutexLock mutex_lock(wayland_thread.mutex);460461WaylandThread::PointerConstraint old_constraint = wayland_thread.pointer_get_constraint();462463wayland_thread.pointer_set_constraint(WaylandThread::PointerConstraint::LOCKED);464wayland_thread.pointer_set_hint(p_to);465466wayland_thread.pointer_set_constraint(old_constraint);467}468469Point2i DisplayServerWayland::mouse_get_position() const {470MutexLock mutex_lock(wayland_thread.mutex);471472WindowID pointed_id = wayland_thread.pointer_get_pointed_window_id();473474if (pointed_id != INVALID_WINDOW_ID && windows.has(pointed_id)) {475return Input::get_singleton()->get_mouse_position() + windows[pointed_id].rect.position;476}477478// We can't properly implement this method by design.479// This is the best we can do unfortunately.480return Input::get_singleton()->get_mouse_position();481}482483BitField<MouseButtonMask> DisplayServerWayland::mouse_get_button_state() const {484MutexLock mutex_lock(wayland_thread.mutex);485486// Are we sure this is the only way? This seems sus.487// TODO: Handle tablets properly.488//mouse_button_mask.set_flag(MouseButtonMask((int64_t)wls.current_seat->tablet_tool_data.pressed_button_mask));489490return wayland_thread.pointer_get_button_mask();491}492493// NOTE: According to the Wayland specification, this method will only do494// anything if the user has interacted with the application by sending a495// "recent enough" input event.496// TODO: Add this limitation to the documentation.497void DisplayServerWayland::clipboard_set(const String &p_text) {498MutexLock mutex_lock(wayland_thread.mutex);499500wayland_thread.selection_set_text(p_text);501}502503String DisplayServerWayland::clipboard_get() const {504MutexLock mutex_lock(wayland_thread.mutex);505506Vector<uint8_t> data;507508const String text_mimes[] = {509"text/plain;charset=utf-8",510"text/plain",511};512513for (String mime : text_mimes) {514if (wayland_thread.selection_has_mime(mime)) {515print_verbose(vformat("Selecting media type \"%s\" from offered types.", mime));516data = wayland_thread.selection_get_mime(mime);517break;518}519}520521return String::utf8((const char *)data.ptr(), data.size());522}523524Ref<Image> DisplayServerWayland::clipboard_get_image() const {525MutexLock mutex_lock(wayland_thread.mutex);526527Ref<Image> image;528image.instantiate();529530Error err = OK;531532// TODO: Fallback to next media type on missing module or parse error.533if (wayland_thread.selection_has_mime("image/png")) {534err = image->load_png_from_buffer(wayland_thread.selection_get_mime("image/png"));535} else if (wayland_thread.selection_has_mime("image/jpeg")) {536err = image->load_jpg_from_buffer(wayland_thread.selection_get_mime("image/jpeg"));537} else if (wayland_thread.selection_has_mime("image/webp")) {538err = image->load_webp_from_buffer(wayland_thread.selection_get_mime("image/webp"));539} else if (wayland_thread.selection_has_mime("image/svg+xml")) {540err = image->load_svg_from_buffer(wayland_thread.selection_get_mime("image/svg+xml"));541} else if (wayland_thread.selection_has_mime("image/bmp")) {542err = image->load_bmp_from_buffer(wayland_thread.selection_get_mime("image/bmp"));543} else if (wayland_thread.selection_has_mime("image/x-tga")) {544err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-tga"));545} else if (wayland_thread.selection_has_mime("image/x-targa")) {546err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-targa"));547} else if (wayland_thread.selection_has_mime("image/ktx")) {548err = image->load_ktx_from_buffer(wayland_thread.selection_get_mime("image/ktx"));549} else if (wayland_thread.selection_has_mime("image/x-exr")) {550err = image->load_exr_from_buffer(wayland_thread.selection_get_mime("image/x-exr"));551}552553ERR_FAIL_COND_V(err != OK, Ref<Image>());554555return image;556}557558void DisplayServerWayland::clipboard_set_primary(const String &p_text) {559MutexLock mutex_lock(wayland_thread.mutex);560561wayland_thread.primary_set_text(p_text);562}563564String DisplayServerWayland::clipboard_get_primary() const {565MutexLock mutex_lock(wayland_thread.mutex);566567Vector<uint8_t> data;568569const String text_mimes[] = {570"text/plain;charset=utf-8",571"text/plain",572};573574for (String mime : text_mimes) {575if (wayland_thread.primary_has_mime(mime)) {576print_verbose(vformat("Selecting media type \"%s\" from offered types.", mime));577data = wayland_thread.primary_get_mime(mime);578break;579}580}581582return String::utf8((const char *)data.ptr(), data.size());583}584585int DisplayServerWayland::get_screen_count() const {586MutexLock mutex_lock(wayland_thread.mutex);587return wayland_thread.get_screen_count();588}589590int DisplayServerWayland::get_primary_screen() const {591// AFAIK Wayland doesn't allow knowing (nor we care) about which screen is592// primary.593return 0;594}595596Point2i DisplayServerWayland::screen_get_position(int p_screen) const {597MutexLock mutex_lock(wayland_thread.mutex);598599p_screen = _get_screen_index(p_screen);600int screen_count = get_screen_count();601ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());602603return wayland_thread.screen_get_data(p_screen).position;604}605606Size2i DisplayServerWayland::screen_get_size(int p_screen) const {607MutexLock mutex_lock(wayland_thread.mutex);608609p_screen = _get_screen_index(p_screen);610int screen_count = get_screen_count();611ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());612613return wayland_thread.screen_get_data(p_screen).size;614}615616Rect2i DisplayServerWayland::screen_get_usable_rect(int p_screen) const {617p_screen = _get_screen_index(p_screen);618int screen_count = get_screen_count();619ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());620621return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));622}623624int DisplayServerWayland::screen_get_dpi(int p_screen) const {625MutexLock mutex_lock(wayland_thread.mutex);626627p_screen = _get_screen_index(p_screen);628int screen_count = get_screen_count();629ERR_FAIL_INDEX_V(p_screen, screen_count, 96);630631const WaylandThread::ScreenData &data = wayland_thread.screen_get_data(p_screen);632633int width_mm = data.physical_size.width;634int height_mm = data.physical_size.height;635636double xdpi = (width_mm ? data.size.width / (double)width_mm * 25.4 : 0);637double ydpi = (height_mm ? data.size.height / (double)height_mm * 25.4 : 0);638639if (xdpi || ydpi) {640return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1);641}642643// Could not get DPI.644return 96;645}646647float DisplayServerWayland::screen_get_scale(int p_screen) const {648MutexLock mutex_lock(wayland_thread.mutex);649650if (p_screen == SCREEN_OF_MAIN_WINDOW) {651// Wayland does not expose fractional scale factors at the screen-level, but652// some code relies on it. Since this special screen is the default and a lot653// of code relies on it, we'll return the window's scale, which is what we654// really care about. After all, we have very little use of the actual screen655// enumeration APIs and we're (for now) in single-window mode anyways.656struct wl_surface *wl_surface = wayland_thread.window_get_wl_surface(MAIN_WINDOW_ID);657WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wl_surface);658659return wayland_thread.window_state_get_scale_factor(ws);660}661662p_screen = _get_screen_index(p_screen);663int screen_count = get_screen_count();664ERR_FAIL_INDEX_V(p_screen, screen_count, 1.0f);665666return wayland_thread.screen_get_data(p_screen).scale;667}668669float DisplayServerWayland::screen_get_refresh_rate(int p_screen) const {670MutexLock mutex_lock(wayland_thread.mutex);671672p_screen = _get_screen_index(p_screen);673int screen_count = get_screen_count();674ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);675676return wayland_thread.screen_get_data(p_screen).refresh_rate;677}678679void DisplayServerWayland::screen_set_keep_on(bool p_enable) {680MutexLock mutex_lock(wayland_thread.mutex);681682// FIXME: For some reason this does not also windows from the wayland thread.683684if (screen_is_kept_on() == p_enable) {685return;686}687688wayland_thread.window_set_idle_inhibition(MAIN_WINDOW_ID, p_enable);689690#ifdef DBUS_ENABLED691if (portal_desktop && portal_desktop->is_inhibit_supported()) {692if (p_enable) {693// Attach the inhibit request to the main window, not the last focused window,694// on the basis that inhibiting the screensaver is global state for the application.695WindowID window_id = MAIN_WINDOW_ID;696WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wayland_thread.window_get_wl_surface(window_id));697screensaver_inhibited = portal_desktop->inhibit(ws ? ws->exported_handle : String());698} else {699portal_desktop->uninhibit();700screensaver_inhibited = false;701}702} else if (screensaver) {703if (p_enable) {704screensaver->inhibit();705} else {706screensaver->uninhibit();707}708709screensaver_inhibited = p_enable;710}711#endif712}713714bool DisplayServerWayland::screen_is_kept_on() const {715// FIXME: Multiwindow support.716#ifdef DBUS_ENABLED717return wayland_thread.window_get_idle_inhibition(MAIN_WINDOW_ID) || screensaver_inhibited;718#else719return wayland_thread.window_get_idle_inhibition(MAIN_WINDOW_ID);720#endif721}722723Vector<DisplayServer::WindowID> DisplayServerWayland::get_window_list() const {724MutexLock mutex_lock(wayland_thread.mutex);725726Vector<int> ret;727for (const KeyValue<WindowID, WindowData> &E : windows) {728ret.push_back(E.key);729}730return ret;731}732733DisplayServer::WindowID DisplayServerWayland::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent) {734WindowID id = ++window_id_counter;735WindowData &wd = windows[id];736737wd.id = id;738wd.mode = p_mode;739wd.flags = p_flags;740wd.vsync_mode = p_vsync_mode;741742#ifdef ACCESSKIT_ENABLED743if (accessibility_driver && !accessibility_driver->window_create(wd.id, nullptr)) {744if (OS::get_singleton()->is_stdout_verbose()) {745ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");746}747memdelete(accessibility_driver);748accessibility_driver = nullptr;749}750#endif751752// NOTE: Remember to clear its position if this window will be a toplevel. We753// can only know once we show it.754wd.rect = p_rect;755756wd.title = "Godot";757wd.parent_id = p_transient_parent;758return id;759}760761void DisplayServerWayland::show_window(WindowID p_window_id) {762MutexLock mutex_lock(wayland_thread.mutex);763764ERR_FAIL_COND(!windows.has(p_window_id));765766WindowData &wd = windows[p_window_id];767768if (!wd.visible) {769DEBUG_LOG_WAYLAND(vformat("Showing window %d", p_window_id));770// Showing this window will reset its mode with whatever the compositor771// reports. We'll save the mode beforehand so that we can reapply it later.772// TODO: Fix/Port/Move/Whatever to `WaylandThread` APIs.773WindowMode setup_mode = wd.mode;774775// Let's determine the closest toplevel. For toplevels it will be themselves,776// for popups the first toplevel ancestor it finds.777WindowID root_id = wd.id;778while (root_id != INVALID_WINDOW_ID && window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, root_id)) {779root_id = windows[root_id].parent_id;780}781ERR_FAIL_COND(root_id == INVALID_WINDOW_ID);782783wd.root_id = root_id;784785if (!window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, p_window_id)) {786// NOTE: DO **NOT** KEEP THE POSITION SET FOR TOPLEVELS. Wayland does not787// track them and we're gonna get our events transformed in unexpected ways.788wd.rect.position = Point2i();789790DEBUG_LOG_WAYLAND(vformat("Creating regular window of size %s", wd.rect.size));791wayland_thread.window_create(p_window_id, wd.rect.size, wd.parent_id);792wayland_thread.window_set_min_size(p_window_id, wd.min_size);793wayland_thread.window_set_max_size(p_window_id, wd.max_size);794wayland_thread.window_set_app_id(p_window_id, _get_app_id_from_context(context));795wayland_thread.window_set_borderless(p_window_id, window_get_flag(WINDOW_FLAG_BORDERLESS, p_window_id));796797// Since it can't have a position. Let's tell the window node the news by798// the actual rect to it.799if (wd.rect_changed_callback.is_valid()) {800wd.rect_changed_callback.call(wd.rect);801}802} else {803DEBUG_LOG_WAYLAND("!!!!! Making popup !!!!!");804805windows[root_id].popup_stack.push_back(p_window_id);806807if (window_get_flag(WINDOW_FLAG_POPUP, p_window_id)) {808// Reroutes all input to it.809popup_menu_list.push_back(p_window_id);810}811812wayland_thread.window_create_popup(p_window_id, wd.parent_id, wd.rect);813}814815// NOTE: The XDG shell protocol is built in a way that causes the window to816// be immediately shown as soon as a valid buffer is assigned to it. Hence,817// the only acceptable way of implementing window showing is to move the818// graphics context window creation logic here.819#ifdef RD_ENABLED820if (rendering_context) {821union {822#ifdef VULKAN_ENABLED823RenderingContextDriverVulkanWayland::WindowPlatformData vulkan;824#endif825} wpd;826#ifdef VULKAN_ENABLED827if (rendering_driver == "vulkan") {828wpd.vulkan.surface = wayland_thread.window_get_wl_surface(wd.id);829ERR_FAIL_NULL(wpd.vulkan.surface);830wpd.vulkan.display = wayland_thread.get_wl_display();831}832#endif833Error err = rendering_context->window_create(wd.id, &wpd);834ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s window", rendering_driver));835836rendering_context->window_set_size(wd.id, wd.rect.size.width, wd.rect.size.height);837838// NOTE: Looks like we have to set the vsync mode before creating the screen839// or it won't work. Resist any temptation.840window_set_vsync_mode(wd.vsync_mode, p_window_id);841}842843if (rendering_device) {844rendering_device->screen_create(wd.id);845}846#endif847848#ifdef GLES3_ENABLED849if (egl_manager) {850struct wl_surface *wl_surface = wayland_thread.window_get_wl_surface(wd.id);851ERR_FAIL_NULL(wl_surface);852wd.wl_egl_window = wl_egl_window_create(wl_surface, wd.rect.size.width, wd.rect.size.height);853854Error err = egl_manager->window_create(p_window_id, wayland_thread.get_wl_display(), wd.wl_egl_window, wd.rect.size.width, wd.rect.size.height);855ERR_FAIL_COND_MSG(err == ERR_CANT_CREATE, "Can't show a GLES3 window.");856857window_set_vsync_mode(wd.vsync_mode, p_window_id);858}859#endif860861// NOTE: Some public window-handling methods might depend on this flag being862// set. Make sure the method you're calling does not depend on it before this863// assignment.864wd.visible = true;865866// Actually try to apply the window's mode now that it's visible.867window_set_mode(setup_mode, wd.id);868869wayland_thread.window_set_title(p_window_id, wd.title);870}871}872873void DisplayServerWayland::delete_sub_window(WindowID p_window_id) {874MutexLock mutex_lock(wayland_thread.mutex);875876ERR_FAIL_COND(!windows.has(p_window_id));877WindowData &wd = windows[p_window_id];878879ERR_FAIL_COND(!windows.has(wd.root_id));880WindowData &root_wd = windows[wd.root_id];881882// NOTE: By the time the Wayland thread will send a `WINDOW_EVENT_MOUSE_EXIT`883// the window will be gone and the message will be discarded, confusing the884// engine. We thus have to send it ourselves.885if (wayland_thread.pointer_get_pointed_window_id() == p_window_id) {886_send_window_event(WINDOW_EVENT_MOUSE_EXIT, p_window_id);887}888889// The XDG shell specification requires us to clear all popups in reverse order.890while (!root_wd.popup_stack.is_empty() && root_wd.popup_stack.back()->get() != p_window_id) {891_send_window_event(WINDOW_EVENT_FORCE_CLOSE, root_wd.popup_stack.back()->get());892}893894if (root_wd.popup_stack.back() && root_wd.popup_stack.back()->get() == p_window_id) {895root_wd.popup_stack.pop_back();896}897898if (popup_menu_list.back() && popup_menu_list.back()->get() == p_window_id) {899popup_menu_list.pop_back();900}901902#ifdef ACCESSKIT_ENABLED903if (accessibility_driver) {904accessibility_driver->window_destroy(p_window_id);905}906#endif907908if (wd.visible) {909#ifdef VULKAN_ENABLED910if (rendering_device) {911rendering_device->screen_free(p_window_id);912}913914if (rendering_context) {915rendering_context->window_destroy(p_window_id);916}917#endif918919#ifdef GLES3_ENABLED920if (egl_manager) {921egl_manager->window_destroy(p_window_id);922}923#endif924925wayland_thread.window_destroy(p_window_id);926}927928windows.erase(p_window_id);929930DEBUG_LOG_WAYLAND(vformat("Destroyed window %d", p_window_id));931}932933DisplayServer::WindowID DisplayServerWayland::window_get_active_popup() const {934MutexLock mutex_lock(wayland_thread.mutex);935936if (!popup_menu_list.is_empty()) {937return popup_menu_list.back()->get();938}939940return INVALID_WINDOW_ID;941}942943void DisplayServerWayland::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {944MutexLock mutex_lock(wayland_thread.mutex);945946ERR_FAIL_COND(!windows.has(p_window));947948windows[p_window].safe_rect = p_rect;949}950951Rect2i DisplayServerWayland::window_get_popup_safe_rect(WindowID p_window) const {952MutexLock mutex_lock(wayland_thread.mutex);953954ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());955956return windows[p_window].safe_rect;957}958959int64_t DisplayServerWayland::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {960MutexLock mutex_lock(wayland_thread.mutex);961962switch (p_handle_type) {963case DISPLAY_HANDLE: {964return (int64_t)wayland_thread.get_wl_display();965} break;966967case WINDOW_HANDLE: {968return (int64_t)wayland_thread.window_get_wl_surface(p_window);969} break;970971case WINDOW_VIEW: {972return 0; // Not supported.973} break;974975#ifdef GLES3_ENABLED976case OPENGL_CONTEXT: {977if (egl_manager) {978return (int64_t)egl_manager->get_context(p_window);979}980return 0;981} break;982case EGL_DISPLAY: {983if (egl_manager) {984return (int64_t)egl_manager->get_display(p_window);985}986return 0;987}988case EGL_CONFIG: {989if (egl_manager) {990return (int64_t)egl_manager->get_config(p_window);991}992return 0;993}994#endif // GLES3_ENABLED995996default: {997return 0;998} break;999}1000}10011002DisplayServer::WindowID DisplayServerWayland::get_window_at_screen_position(const Point2i &p_position) const {1003// Standard Wayland APIs don't support this.1004return MAIN_WINDOW_ID;1005}10061007void DisplayServerWayland::window_attach_instance_id(ObjectID p_instance, WindowID p_window_id) {1008MutexLock mutex_lock(wayland_thread.mutex);10091010ERR_FAIL_COND(!windows.has(p_window_id));10111012windows[p_window_id].instance_id = p_instance;1013}10141015ObjectID DisplayServerWayland::window_get_attached_instance_id(WindowID p_window_id) const {1016MutexLock mutex_lock(wayland_thread.mutex);10171018ERR_FAIL_COND_V(!windows.has(p_window_id), ObjectID());10191020return windows[p_window_id].instance_id;1021}10221023void DisplayServerWayland::window_set_title(const String &p_title, DisplayServer::WindowID p_window_id) {1024MutexLock mutex_lock(wayland_thread.mutex);10251026ERR_FAIL_COND(!windows.has(p_window_id));10271028WindowData &wd = windows[p_window_id];10291030wd.title = p_title;10311032if (wd.visible) {1033wayland_thread.window_set_title(p_window_id, wd.title);1034}1035}10361037void DisplayServerWayland::window_set_mouse_passthrough(const Vector<Vector2> &p_region, DisplayServer::WindowID p_window_id) {1038// TODO1039DEBUG_LOG_WAYLAND(vformat("wayland stub window_set_mouse_passthrough region %s", p_region));1040}10411042void DisplayServerWayland::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1043MutexLock mutex_lock(wayland_thread.mutex);10441045ERR_FAIL_COND(!windows.has(p_window_id));10461047windows[p_window_id].rect_changed_callback = p_callable;1048}10491050void DisplayServerWayland::window_set_window_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1051MutexLock mutex_lock(wayland_thread.mutex);10521053ERR_FAIL_COND(!windows.has(p_window_id));10541055windows[p_window_id].window_event_callback = p_callable;1056}10571058void DisplayServerWayland::window_set_input_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1059MutexLock mutex_lock(wayland_thread.mutex);10601061ERR_FAIL_COND(!windows.has(p_window_id));10621063windows[p_window_id].input_event_callback = p_callable;1064}10651066void DisplayServerWayland::window_set_input_text_callback(const Callable &p_callable, WindowID p_window_id) {1067MutexLock mutex_lock(wayland_thread.mutex);10681069ERR_FAIL_COND(!windows.has(p_window_id));10701071windows[p_window_id].input_text_callback = p_callable;1072}10731074void DisplayServerWayland::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1075MutexLock mutex_lock(wayland_thread.mutex);10761077ERR_FAIL_COND(!windows.has(p_window_id));10781079windows[p_window_id].drop_files_callback = p_callable;1080}10811082int DisplayServerWayland::window_get_current_screen(DisplayServer::WindowID p_window_id) const {1083ERR_FAIL_COND_V(!windows.has(p_window_id), INVALID_SCREEN);1084// Standard Wayland APIs don't support getting the screen of a window.1085return 0;1086}10871088void DisplayServerWayland::window_set_current_screen(int p_screen, DisplayServer::WindowID p_window_id) {1089// Standard Wayland APIs don't support setting the screen of a window.1090}10911092Point2i DisplayServerWayland::window_get_position(DisplayServer::WindowID p_window_id) const {1093MutexLock mutex_lock(wayland_thread.mutex);10941095return windows[p_window_id].rect.position;1096}10971098Point2i DisplayServerWayland::window_get_position_with_decorations(DisplayServer::WindowID p_window_id) const {1099MutexLock mutex_lock(wayland_thread.mutex);11001101return windows[p_window_id].rect.position;1102}11031104void DisplayServerWayland::window_set_position(const Point2i &p_position, DisplayServer::WindowID p_window_id) {1105// Unsupported with toplevels.1106}11071108void DisplayServerWayland::window_set_max_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1109MutexLock mutex_lock(wayland_thread.mutex);11101111DEBUG_LOG_WAYLAND(vformat("window max size set to %s", p_size));11121113if (p_size.x < 0 || p_size.y < 0) {1114ERR_FAIL_MSG("Maximum window size can't be negative!");1115}11161117ERR_FAIL_COND(!windows.has(p_window_id));1118WindowData &wd = windows[p_window_id];11191120// FIXME: Is `p_size.x < wd.min_size.x || p_size.y < wd.min_size.y` == `p_size < wd.min_size`?1121if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {1122ERR_PRINT("Maximum window size can't be smaller than minimum window size!");1123return;1124}11251126wd.max_size = p_size;11271128if (wd.visible) {1129wayland_thread.window_set_max_size(p_window_id, p_size);1130}1131}11321133Size2i DisplayServerWayland::window_get_max_size(DisplayServer::WindowID p_window_id) const {1134MutexLock mutex_lock(wayland_thread.mutex);11351136ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1137return windows[p_window_id].max_size;1138}11391140void DisplayServerWayland::gl_window_make_current(DisplayServer::WindowID p_window_id) {1141#ifdef GLES3_ENABLED1142if (egl_manager) {1143egl_manager->window_make_current(p_window_id);1144}1145#endif1146}11471148void DisplayServerWayland::window_set_transient(WindowID p_window_id, WindowID p_parent) {1149MutexLock mutex_lock(wayland_thread.mutex);11501151ERR_FAIL_COND(!windows.has(p_window_id));1152WindowData &wd = windows[p_window_id];11531154ERR_FAIL_COND(wd.parent_id == p_parent);11551156if (p_parent != INVALID_WINDOW_ID) {1157ERR_FAIL_COND(!windows.has(p_parent));1158ERR_FAIL_COND_MSG(wd.parent_id != INVALID_WINDOW_ID, "Window already has a transient parent");1159wd.parent_id = p_parent;11601161// NOTE: Looks like live unparenting is not really practical unfortunately.1162// See WaylandThread::window_set_parent for more info.1163if (wd.visible) {1164wayland_thread.window_set_parent(p_window_id, p_parent);1165}1166}1167}11681169void DisplayServerWayland::window_set_min_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1170MutexLock mutex_lock(wayland_thread.mutex);11711172DEBUG_LOG_WAYLAND(vformat("window minsize set to %s", p_size));11731174ERR_FAIL_COND(!windows.has(p_window_id));1175WindowData &wd = windows[p_window_id];11761177if (p_size.x < 0 || p_size.y < 0) {1178ERR_FAIL_MSG("Minimum window size can't be negative!");1179}11801181// FIXME: Is `p_size.x > wd.max_size.x || p_size.y > wd.max_size.y` == `p_size > wd.max_size`?1182if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {1183ERR_PRINT("Minimum window size can't be larger than maximum window size!");1184return;1185}11861187wd.min_size = p_size;11881189if (wd.visible) {1190wayland_thread.window_set_min_size(p_window_id, p_size);1191}1192}11931194Size2i DisplayServerWayland::window_get_min_size(DisplayServer::WindowID p_window_id) const {1195MutexLock mutex_lock(wayland_thread.mutex);11961197ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1198return windows[p_window_id].min_size;1199}12001201void DisplayServerWayland::window_set_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1202MutexLock mutex_lock(wayland_thread.mutex);12031204ERR_FAIL_COND(!windows.has(p_window_id));1205WindowData &wd = windows[p_window_id];12061207Size2i new_size = p_size;1208if (wd.visible) {1209new_size = wayland_thread.window_set_size(p_window_id, p_size);1210}12111212_update_window_rect(Rect2i(wd.rect.position, new_size), p_window_id);1213}12141215Size2i DisplayServerWayland::window_get_size(DisplayServer::WindowID p_window_id) const {1216MutexLock mutex_lock(wayland_thread.mutex);12171218ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1219return windows[p_window_id].rect.size;1220}12211222Size2i DisplayServerWayland::window_get_size_with_decorations(DisplayServer::WindowID p_window_id) const {1223MutexLock mutex_lock(wayland_thread.mutex);12241225// I don't think there's a way of actually knowing the size of the window1226// decoration in Wayland, at least in the case of SSDs, nor that it would be1227// that useful in this case. We'll just return the main window's size.1228ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1229return windows[p_window_id].rect.size;1230}12311232float DisplayServerWayland::window_get_scale(WindowID p_window_id) const {1233MutexLock mutex_lock(wayland_thread.mutex);12341235const WaylandThread::WindowState *ws = wayland_thread.window_get_state(p_window_id);1236ERR_FAIL_NULL_V(ws, 1);12371238return wayland_thread.window_state_get_scale_factor(ws);1239}12401241void DisplayServerWayland::window_set_mode(WindowMode p_mode, DisplayServer::WindowID p_window_id) {1242MutexLock mutex_lock(wayland_thread.mutex);12431244ERR_FAIL_COND(!windows.has(p_window_id));1245WindowData &wd = windows[p_window_id];12461247if (!wd.visible) {1248return;1249}12501251wayland_thread.window_try_set_mode(p_window_id, p_mode);1252}12531254DisplayServer::WindowMode DisplayServerWayland::window_get_mode(DisplayServer::WindowID p_window_id) const {1255MutexLock mutex_lock(wayland_thread.mutex);12561257ERR_FAIL_COND_V(!windows.has(p_window_id), WINDOW_MODE_WINDOWED);1258const WindowData &wd = windows[p_window_id];12591260if (!wd.visible) {1261return WINDOW_MODE_WINDOWED;1262}12631264return wayland_thread.window_get_mode(p_window_id);1265}12661267bool DisplayServerWayland::window_is_maximize_allowed(DisplayServer::WindowID p_window_id) const {1268MutexLock mutex_lock(wayland_thread.mutex);12691270return wayland_thread.window_can_set_mode(p_window_id, WINDOW_MODE_MAXIMIZED);1271}12721273void DisplayServerWayland::window_set_flag(WindowFlags p_flag, bool p_enabled, DisplayServer::WindowID p_window_id) {1274MutexLock mutex_lock(wayland_thread.mutex);12751276ERR_FAIL_COND(!windows.has(p_window_id));1277WindowData &wd = windows[p_window_id];12781279DEBUG_LOG_WAYLAND(vformat("Window set flag %d", p_flag));12801281switch (p_flag) {1282case WINDOW_FLAG_BORDERLESS: {1283wayland_thread.window_set_borderless(p_window_id, p_enabled);1284} break;12851286case WINDOW_FLAG_POPUP: {1287ERR_FAIL_COND_MSG(p_window_id == MAIN_WINDOW_ID, "Main window can't be popup.");1288ERR_FAIL_COND_MSG(wd.visible && (wd.flags & WINDOW_FLAG_POPUP_BIT) != p_enabled, "Popup flag can't changed while window is opened.");1289} break;12901291case WINDOW_FLAG_POPUP_WM_HINT: {1292ERR_FAIL_COND_MSG(p_window_id == MAIN_WINDOW_ID, "Main window can't have popup hint.");1293ERR_FAIL_COND_MSG(wd.visible && (wd.flags & WINDOW_FLAG_POPUP_WM_HINT_BIT) != p_enabled, "Popup hint can't changed while window is opened.");1294} break;12951296default: {1297}1298}12991300if (p_enabled) {1301wd.flags |= 1 << p_flag;1302} else {1303wd.flags &= ~(1 << p_flag);1304}1305}13061307bool DisplayServerWayland::window_get_flag(WindowFlags p_flag, DisplayServer::WindowID p_window_id) const {1308MutexLock mutex_lock(wayland_thread.mutex);13091310ERR_FAIL_COND_V(!windows.has(p_window_id), false);1311return windows[p_window_id].flags & (1 << p_flag);1312}13131314void DisplayServerWayland::window_request_attention(DisplayServer::WindowID p_window_id) {1315MutexLock mutex_lock(wayland_thread.mutex);13161317DEBUG_LOG_WAYLAND("Requested attention.");13181319wayland_thread.window_request_attention(p_window_id);1320}13211322void DisplayServerWayland::window_move_to_foreground(DisplayServer::WindowID p_window_id) {1323// Standard Wayland APIs don't support this.1324}13251326bool DisplayServerWayland::window_is_focused(WindowID p_window_id) const {1327MutexLock mutex_lock(wayland_thread.mutex);13281329return wayland_thread.pointer_get_pointed_window_id() == p_window_id;1330}13311332bool DisplayServerWayland::window_can_draw(DisplayServer::WindowID p_window_id) const {1333MutexLock mutex_lock(wayland_thread.mutex);13341335uint64_t last_frame_time = wayland_thread.window_get_last_frame_time(p_window_id);1336uint64_t time_since_frame = OS::get_singleton()->get_ticks_usec() - last_frame_time;13371338if (time_since_frame > WAYLAND_MAX_FRAME_TIME_US) {1339return false;1340}13411342if (wayland_thread.window_is_suspended(p_window_id)) {1343return false;1344}13451346return suspend_state == SuspendState::NONE;1347}13481349bool DisplayServerWayland::can_any_window_draw() const {1350return suspend_state == SuspendState::NONE;1351}13521353void DisplayServerWayland::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) {1354MutexLock mutex_lock(wayland_thread.mutex);13551356wayland_thread.window_set_ime_active(p_active, p_window_id);1357}13581359void DisplayServerWayland::window_set_ime_position(const Point2i &p_pos, DisplayServer::WindowID p_window_id) {1360MutexLock mutex_lock(wayland_thread.mutex);13611362wayland_thread.window_set_ime_position(p_pos, p_window_id);1363}13641365int DisplayServerWayland::accessibility_should_increase_contrast() const {1366#ifdef DBUS_ENABLED1367if (!portal_desktop) {1368return -1;1369}1370return portal_desktop->get_high_contrast();1371#endif1372return -1;1373}13741375int DisplayServerWayland::accessibility_screen_reader_active() const {1376#ifdef DBUS_ENABLED1377if (atspi_monitor && atspi_monitor->is_supported()) {1378return atspi_monitor->is_active();1379}1380#endif1381return -1;1382}13831384Point2i DisplayServerWayland::ime_get_selection() const {1385return ime_selection;1386}13871388String DisplayServerWayland::ime_get_text() const {1389return ime_text;1390}13911392// NOTE: While Wayland is supposed to be tear-free, wayland-protocols version1393// 1.30 added a protocol for allowing async flips which is supposed to be1394// handled by drivers such as Vulkan. We can then just ask to disable v-sync and1395// hope for the best. See: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/commit/6394f0b4f3be151076f10a845a2fb131eeb567061396void DisplayServerWayland::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, DisplayServer::WindowID p_window_id) {1397MutexLock mutex_lock(wayland_thread.mutex);13981399WindowData &wd = windows[p_window_id];14001401#ifdef RD_ENABLED1402if (rendering_context) {1403rendering_context->window_set_vsync_mode(p_window_id, p_vsync_mode);14041405wd.emulate_vsync = (!wayland_thread.is_fifo_available() && rendering_context->window_get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED);14061407if (wd.emulate_vsync) {1408print_verbose("VSYNC: manually throttling frames using MAILBOX.");1409rendering_context->window_set_vsync_mode(p_window_id, DisplayServer::VSYNC_MAILBOX);1410}1411}1412#endif // VULKAN_ENABLED14131414#ifdef GLES3_ENABLED1415if (egl_manager) {1416egl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);14171418// NOTE: Mesa's EGL implementation does not seem to make use of fifo_v1 so1419// we'll have to always emulate V-Sync.1420wd.emulate_vsync = egl_manager->is_using_vsync();14211422if (wd.emulate_vsync) {1423print_verbose("VSYNC: manually throttling frames with swap delay 0.");1424egl_manager->set_use_vsync(false);1425}1426}1427#endif // GLES3_ENABLED1428}14291430DisplayServer::VSyncMode DisplayServerWayland::window_get_vsync_mode(DisplayServer::WindowID p_window_id) const {1431const WindowData &wd = windows[p_window_id];1432if (wd.emulate_vsync) {1433return DisplayServer::VSYNC_ENABLED;1434}14351436#ifdef VULKAN_ENABLED1437if (rendering_context) {1438return rendering_context->window_get_vsync_mode(p_window_id);1439}1440#endif // VULKAN_ENABLED14411442#ifdef GLES3_ENABLED1443if (egl_manager) {1444return egl_manager->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;1445}1446#endif // GLES3_ENABLED14471448return DisplayServer::VSYNC_ENABLED;1449}14501451void DisplayServerWayland::window_start_drag(WindowID p_window) {1452MutexLock mutex_lock(wayland_thread.mutex);14531454wayland_thread.window_start_drag(p_window);1455}14561457void DisplayServerWayland::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {1458MutexLock mutex_lock(wayland_thread.mutex);14591460ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);1461wayland_thread.window_start_resize(p_edge, p_window);1462}14631464void DisplayServerWayland::cursor_set_shape(CursorShape p_shape) {1465ERR_FAIL_INDEX(p_shape, CURSOR_MAX);14661467MutexLock mutex_lock(wayland_thread.mutex);14681469if (p_shape == cursor_shape) {1470return;1471}14721473cursor_shape = p_shape;14741475if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {1476// Hidden.1477return;1478}14791480wayland_thread.cursor_set_shape(p_shape);1481}14821483DisplayServerWayland::CursorShape DisplayServerWayland::cursor_get_shape() const {1484MutexLock mutex_lock(wayland_thread.mutex);14851486return cursor_shape;1487}14881489void DisplayServerWayland::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {1490MutexLock mutex_lock(wayland_thread.mutex);14911492if (p_cursor.is_valid()) {1493HashMap<CursorShape, CustomCursor>::Iterator cursor_c = custom_cursors.find(p_shape);14941495if (cursor_c) {1496if (cursor_c->value.resource == p_cursor && cursor_c->value.hotspot == p_hotspot) {1497// We have a cached cursor. Nice.1498wayland_thread.cursor_set_shape(p_shape);1499return;1500}15011502// We're changing this cursor; we'll have to rebuild it.1503custom_cursors.erase(p_shape);1504wayland_thread.cursor_shape_clear_custom_image(p_shape);1505}15061507Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);1508ERR_FAIL_COND(image.is_null());15091510CustomCursor &cursor = custom_cursors[p_shape];15111512cursor.resource = p_cursor;1513cursor.hotspot = p_hotspot;15141515wayland_thread.cursor_shape_set_custom_image(p_shape, image, p_hotspot);15161517wayland_thread.cursor_set_shape(p_shape);1518} else {1519// Clear cache and reset to default system cursor.1520wayland_thread.cursor_shape_clear_custom_image(p_shape);15211522if (cursor_shape == p_shape) {1523wayland_thread.cursor_set_shape(p_shape);1524}15251526if (custom_cursors.has(p_shape)) {1527custom_cursors.erase(p_shape);1528}1529}1530}15311532bool DisplayServerWayland::get_swap_cancel_ok() {1533return swap_cancel_ok;1534}15351536Error DisplayServerWayland::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {1537MutexLock mutex_lock(wayland_thread.mutex);15381539struct godot_embedding_compositor *ec = wayland_thread.get_embedding_compositor();1540ERR_FAIL_NULL_V_MSG(ec, ERR_BUG, "Missing embedded compositor interface");15411542struct WaylandThread::EmbeddingCompositorState *ecs = WaylandThread::godot_embedding_compositor_get_state(ec);1543ERR_FAIL_NULL_V(ecs, ERR_BUG);15441545if (!ecs->mapped_clients.has(p_pid)) {1546return ERR_DOES_NOT_EXIST;1547}15481549struct godot_embedded_client *embedded_client = ecs->mapped_clients[p_pid];1550WaylandThread::EmbeddedClientState *client_data = (WaylandThread::EmbeddedClientState *)godot_embedded_client_get_user_data(embedded_client);1551ERR_FAIL_NULL_V(client_data, ERR_BUG);15521553if (p_grab_focus) {1554godot_embedded_client_focus_window(embedded_client);1555}15561557if (p_visible) {1558WaylandThread::WindowState *ws = wayland_thread.window_get_state(p_window);1559ERR_FAIL_NULL_V(ws, ERR_BUG);15601561struct xdg_toplevel *toplevel = ws->xdg_toplevel;1562#ifdef LIBDECOR_ENABLED1563if (toplevel == nullptr && ws->libdecor_frame) {1564toplevel = libdecor_frame_get_xdg_toplevel(ws->libdecor_frame);1565}1566#endif15671568ERR_FAIL_NULL_V(toplevel, ERR_CANT_CREATE);15691570godot_embedded_client_set_embedded_window_parent(embedded_client, toplevel);15711572double window_scale = WaylandThread::window_state_get_scale_factor(ws);15731574Rect2i scaled_rect = p_rect;1575scaled_rect.position = WaylandThread::scale_vector2i(scaled_rect.position, 1 / window_scale);1576scaled_rect.size = WaylandThread::scale_vector2i(scaled_rect.size, 1 / window_scale);15771578print_verbose(vformat("Scaling embedded rect down by %f from %s to %s.", window_scale, p_rect, scaled_rect));15791580godot_embedded_client_set_embedded_window_rect(embedded_client, scaled_rect.position.x, scaled_rect.position.y, scaled_rect.size.width, scaled_rect.size.height);1581} else {1582godot_embedded_client_set_embedded_window_parent(embedded_client, nullptr);1583}15841585return OK;1586}15871588Error DisplayServerWayland::request_close_embedded_process(OS::ProcessID p_pid) {1589MutexLock mutex_lock(wayland_thread.mutex);15901591struct godot_embedding_compositor *ec = wayland_thread.get_embedding_compositor();1592ERR_FAIL_NULL_V_MSG(ec, ERR_BUG, "Missing embedded compositor interface");15931594struct WaylandThread::EmbeddingCompositorState *ecs = WaylandThread::godot_embedding_compositor_get_state(ec);1595ERR_FAIL_NULL_V(ecs, ERR_BUG);15961597if (!ecs->mapped_clients.has(p_pid)) {1598return ERR_DOES_NOT_EXIST;1599}16001601struct godot_embedded_client *embedded_client = ecs->mapped_clients[p_pid];1602WaylandThread::EmbeddedClientState *client_data = (WaylandThread::EmbeddedClientState *)godot_embedded_client_get_user_data(embedded_client);1603ERR_FAIL_NULL_V(client_data, ERR_BUG);16041605godot_embedded_client_embedded_window_request_close(embedded_client);1606return OK;1607}16081609Error DisplayServerWayland::remove_embedded_process(OS::ProcessID p_pid) {1610return request_close_embedded_process(p_pid);1611}16121613OS::ProcessID DisplayServerWayland::get_focused_process_id() {1614MutexLock mutex_lock(wayland_thread.mutex);16151616OS::ProcessID embedded_pid = wayland_thread.embedded_compositor_get_focused_pid();16171618if (embedded_pid < 0) {1619return OS::get_singleton()->get_process_id();1620}16211622return embedded_pid;1623}16241625int DisplayServerWayland::keyboard_get_layout_count() const {1626MutexLock mutex_lock(wayland_thread.mutex);16271628return wayland_thread.keyboard_get_layout_count();1629}16301631int DisplayServerWayland::keyboard_get_current_layout() const {1632MutexLock mutex_lock(wayland_thread.mutex);16331634return wayland_thread.keyboard_get_current_layout_index();1635}16361637void DisplayServerWayland::keyboard_set_current_layout(int p_index) {1638MutexLock mutex_lock(wayland_thread.mutex);16391640wayland_thread.keyboard_set_current_layout_index(p_index);1641}16421643String DisplayServerWayland::keyboard_get_layout_language(int p_index) const {1644MutexLock mutex_lock(wayland_thread.mutex);16451646// xkbcommon exposes only the layout's name, which looks like it overlaps with1647// its language.1648return wayland_thread.keyboard_get_layout_name(p_index);1649}16501651String DisplayServerWayland::keyboard_get_layout_name(int p_index) const {1652MutexLock mutex_lock(wayland_thread.mutex);16531654return wayland_thread.keyboard_get_layout_name(p_index);1655}16561657Key DisplayServerWayland::keyboard_get_keycode_from_physical(Key p_keycode) const {1658MutexLock mutex_lock(wayland_thread.mutex);16591660Key key = wayland_thread.keyboard_get_key_from_physical(p_keycode);16611662// If not found, fallback to QWERTY.1663// This should match the behavior of the event pump.1664if (key == Key::NONE) {1665return p_keycode;1666}16671668if (key >= Key::A + 32 && key <= Key::Z + 32) {1669key -= 'a' - 'A';1670}16711672// Make it consistent with the keys returned by `Input`.1673if (key == Key::BACKTAB) {1674key = Key::TAB;1675}16761677return key;1678}16791680Key DisplayServerWayland::keyboard_get_label_from_physical(Key p_keycode) const {1681MutexLock mutex_lock(wayland_thread.mutex);16821683return wayland_thread.keyboard_get_label_from_physical(p_keycode);1684}16851686bool DisplayServerWayland::color_picker(const Callable &p_callback) {1687#ifdef DBUS_ENABLED1688if (!portal_desktop) {1689return false;1690}1691MutexLock mutex_lock(wayland_thread.mutex);1692WindowID window_id = MAIN_WINDOW_ID;1693// TODO: Use window IDs for multiwindow support.1694WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wayland_thread.window_get_wl_surface(window_id));1695return portal_desktop->color_picker((ws ? ws->exported_handle : String()), p_callback);1696#else1697return false;1698#endif1699}17001701void DisplayServerWayland::try_suspend() {1702// Due to various reasons, we manually handle display synchronization by1703// waiting for a frame event (request to draw) or, if available, the actual1704// window's suspend status. When a window is suspended, we can avoid drawing1705// altogether, either because the compositor told us that we don't need to or1706// because the pace of the frame events became unreliable.1707bool frame = wayland_thread.wait_frame_suspend_ms(WAYLAND_MAX_FRAME_TIME_US / 1000);1708if (!frame) {1709suspend_state = SuspendState::TIMEOUT;1710}1711}17121713void DisplayServerWayland::process_events() {1714wayland_thread.mutex.lock();17151716wayland_thread.keyboard_echo_keys();17171718while (wayland_thread.has_message()) {1719Ref<WaylandThread::Message> msg = wayland_thread.pop_message();17201721// Generic check. Not actual message handling.1722Ref<WaylandThread::WindowMessage> win_msg = msg;1723if (win_msg.is_valid()) {1724ERR_CONTINUE_MSG(win_msg->id == INVALID_WINDOW_ID, "Invalid window ID received from Wayland thread.");17251726if (!windows.has(win_msg->id)) {1727// Window got probably deleted.1728continue;1729}1730}17311732Ref<WaylandThread::WindowRectMessage> winrect_msg = msg;1733if (winrect_msg.is_valid()) {1734_update_window_rect(winrect_msg->rect, winrect_msg->id);1735continue;1736}17371738Ref<WaylandThread::WindowEventMessage> winev_msg = msg;1739if (winev_msg.is_valid() && windows.has(winev_msg->id)) {1740_send_window_event(winev_msg->event, winev_msg->id);17411742if (winev_msg->event == WINDOW_EVENT_FOCUS_IN) {1743#ifdef ACCESSKIT_ENABLED1744if (accessibility_driver) {1745accessibility_driver->accessibility_set_window_focused(winev_msg->id, true);1746}1747#endif1748if (OS::get_singleton()->get_main_loop()) {1749OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);1750}1751} else if (winev_msg->event == WINDOW_EVENT_FOCUS_OUT) {1752#ifdef ACCESSKIT_ENABLED1753if (accessibility_driver) {1754accessibility_driver->accessibility_set_window_focused(winev_msg->id, false);1755}1756#endif1757if (OS::get_singleton()->get_main_loop()) {1758OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);1759}1760Input::get_singleton()->release_pressed_events();1761}1762continue;1763}17641765Ref<WaylandThread::InputEventMessage> inputev_msg = msg;1766if (inputev_msg.is_valid()) {1767Ref<InputEventMouseButton> mb = inputev_msg->event;17681769bool handled = false;1770if (mb.is_valid()) {1771BitField<MouseButtonMask> mouse_mask = mb->get_button_mask();1772if (!popup_menu_list.is_empty() && mb->is_pressed() && mouse_mask != last_mouse_monitor_mask) {1773// Popup menu handling.1774List<WindowID>::Element *E = popup_menu_list.back();1775List<WindowID>::Element *C = nullptr;17761777// Looking for the oldest popup to close.1778while (E) {1779WindowData &wd = windows[E->get()];1780Point2 global_pos = mb->get_position() + window_get_position(mb->get_window_id());1781if (wd.rect.has_point(global_pos)) {1782break;1783} else if (wd.safe_rect.has_point(global_pos)) {1784break;1785}17861787C = E;1788E = E->prev();1789}17901791if (C) {1792handled = true;1793_send_window_event(WINDOW_EVENT_CLOSE_REQUEST, C->get());1794}1795}17961797last_mouse_monitor_mask = mouse_mask;1798}17991800if (!handled) {1801Input::get_singleton()->parse_input_event(inputev_msg->event);1802}1803continue;1804}18051806Ref<WaylandThread::DropFilesEventMessage> dropfiles_msg = msg;1807if (dropfiles_msg.is_valid()) {1808WindowData wd = windows[dropfiles_msg->id];18091810if (wd.drop_files_callback.is_valid()) {1811Variant v_files = dropfiles_msg->files;1812const Variant *v_args[1] = { &v_files };1813Variant ret;1814Callable::CallError ce;1815wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);1816if (ce.error != Callable::CallError::CALL_OK) {1817ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce)));1818}1819}1820continue;1821}18221823Ref<WaylandThread::IMECommitEventMessage> ime_commit_msg = msg;1824if (ime_commit_msg.is_valid()) {1825for (int i = 0; i < ime_commit_msg->text.length(); i++) {1826const char32_t codepoint = ime_commit_msg->text[i];18271828Ref<InputEventKey> ke;1829ke.instantiate();1830ke->set_window_id(ime_commit_msg->id);1831ke->set_pressed(true);1832ke->set_echo(false);1833ke->set_keycode(Key::NONE);1834ke->set_physical_keycode(Key::NONE);1835ke->set_key_label(Key::NONE);1836ke->set_unicode(codepoint);18371838Input::get_singleton()->parse_input_event(ke);1839}1840ime_text = String();1841ime_selection = Vector2i();18421843OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);1844continue;1845}18461847Ref<WaylandThread::IMEUpdateEventMessage> ime_update_msg = msg;1848if (ime_update_msg.is_valid()) {1849if (ime_text != ime_update_msg->text || ime_selection != ime_update_msg->selection) {1850ime_text = ime_update_msg->text;1851ime_selection = ime_update_msg->selection;18521853OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);1854}1855continue;1856}1857}18581859switch (suspend_state) {1860case SuspendState::NONE: {1861bool emulate_vsync = false;1862for (KeyValue<DisplayServer::WindowID, WindowData> &pair : windows) {1863if (pair.value.emulate_vsync) {1864emulate_vsync = true;1865break;1866}1867}18681869if (emulate_vsync) {1870// Due to the way legacy suspension works, we have to treat low processor1871// usage mode very differently than the regular one.1872if (OS::get_singleton()->is_in_low_processor_usage_mode()) {1873// NOTE: We must avoid committing a surface if we expect a new frame, as we1874// might otherwise commit some inconsistent data (e.g. buffer scale). Note1875// that if a new frame is expected it's going to be committed by the renderer1876// soon anyways.1877if (!RenderingServer::get_singleton()->has_changed()) {1878// We _can't_ commit in a different thread (such as in the frame callback1879// itself) because we would risk to step on the renderer's feet, which would1880// cause subtle but severe issues, such as crashes on setups with explicit1881// sync. This isn't normally a problem, as the renderer commits at every1882// frame (which is what we need for atomic surface updates anyways), but in1883// low processor usage mode that expectation is broken. When it's on, our1884// frame rate stops being constant. This also reflects in the frame1885// information we use for legacy suspension. In order to avoid issues, let's1886// manually commit all surfaces, so that we can get fresh frame data.1887wayland_thread.commit_surfaces();1888try_suspend();1889}1890} else {1891try_suspend();1892}1893}18941895if (wayland_thread.is_suspended()) {1896suspend_state = SuspendState::CAPABILITY;1897}18981899if (suspend_state == SuspendState::TIMEOUT) {1900DEBUG_LOG_WAYLAND("Suspending. Reason: timeout.");1901} else if (suspend_state == SuspendState::CAPABILITY) {1902DEBUG_LOG_WAYLAND("Suspending. Reason: capability.");1903}1904} break;19051906case SuspendState::TIMEOUT: {1907// Certain compositors might not report the "suspended" wm_capability flag.1908// Because of this we'll wake up at the next frame event, indicating the1909// desire for the compositor to let us repaint.1910if (wayland_thread.get_reset_frame()) {1911suspend_state = SuspendState::NONE;1912DEBUG_LOG_WAYLAND("Unsuspending from timeout.");1913}19141915// Since we're not rendering, nothing is committing the windows'1916// surfaces. We have to do it ourselves.1917wayland_thread.commit_surfaces();1918} break;19191920case SuspendState::CAPABILITY: {1921// If we suspended by capability we can assume that it will be reset when1922// the compositor wants us to repaint.1923if (!wayland_thread.is_suspended()) {1924suspend_state = SuspendState::NONE;1925DEBUG_LOG_WAYLAND("Unsuspending from capability.");1926}1927} break;1928}19291930#ifdef DBUS_ENABLED1931if (portal_desktop) {1932portal_desktop->process_callbacks();1933}1934#endif19351936wayland_thread.mutex.unlock();19371938Input::get_singleton()->flush_buffered_events();1939}19401941void DisplayServerWayland::release_rendering_thread() {1942#ifdef GLES3_ENABLED1943if (egl_manager) {1944egl_manager->release_current();1945}1946#endif1947}19481949void DisplayServerWayland::swap_buffers() {1950#ifdef GLES3_ENABLED1951if (egl_manager) {1952egl_manager->swap_buffers();1953}1954#endif1955}19561957void DisplayServerWayland::set_icon(const Ref<Image> &p_icon) {1958MutexLock mutex_lock(wayland_thread.mutex);1959wayland_thread.set_icon(p_icon);1960}19611962void DisplayServerWayland::set_context(Context p_context) {1963MutexLock mutex_lock(wayland_thread.mutex);19641965DEBUG_LOG_WAYLAND(vformat("Setting context %d.", p_context));19661967context = p_context;19681969String app_id = _get_app_id_from_context(p_context);1970wayland_thread.window_set_app_id(MAIN_WINDOW_ID, app_id);1971}19721973bool DisplayServerWayland::is_window_transparency_available() const {1974#if defined(RD_ENABLED)1975if (rendering_device && !rendering_device->is_composite_alpha_supported()) {1976return false;1977}1978#endif1979return OS::get_singleton()->is_layered_allowed();1980}19811982Vector<String> DisplayServerWayland::get_rendering_drivers_func() {1983Vector<String> drivers;19841985#ifdef VULKAN_ENABLED1986drivers.push_back("vulkan");1987#endif19881989#ifdef GLES3_ENABLED1990drivers.push_back("opengl3");1991drivers.push_back("opengl3_es");1992#endif1993drivers.push_back("dummy");19941995return drivers;1996}19971998DisplayServer *DisplayServerWayland::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {1999DisplayServer *ds = memnew(DisplayServerWayland(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, p_context, p_parent_window, r_error));2000if (r_error != OK) {2001ERR_PRINT("Can't create the Wayland display server.");2002memdelete(ds);20032004return nullptr;2005}2006return ds;2007}20082009DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Context p_context, int64_t p_parent_window, Error &r_error) {2010#if defined(GLES3_ENABLED) || defined(DBUS_ENABLED)2011#ifdef SOWRAP_ENABLED2012#ifdef DEBUG_ENABLED2013int dylibloader_verbose = 1;2014#else2015int dylibloader_verbose = 0;2016#endif // DEBUG_ENABLED2017#endif // SOWRAP_ENABLED2018#endif // defined(GLES3_ENABLED) || defined(DBUS_ENABLED)20192020r_error = ERR_UNAVAILABLE;2021context = p_context;20222023String current_desk = OS::get_singleton()->get_environment("XDG_CURRENT_DESKTOP").to_lower();2024String session_desk = OS::get_singleton()->get_environment("XDG_SESSION_DESKTOP").to_lower();2025swap_cancel_ok = (current_desk.contains("kde") || session_desk.contains("kde") || current_desk.contains("lxqt") || session_desk.contains("lxqt"));20262027Error thread_err = wayland_thread.init();20282029if (thread_err != OK) {2030r_error = thread_err;2031ERR_FAIL_MSG("Could not initialize the Wayland thread.");2032}20332034// Input.2035Input::get_singleton()->set_event_dispatch_function(dispatch_input_events);20362037native_menu = memnew(NativeMenu);20382039#ifdef SPEECHD_ENABLED2040// Init TTS2041bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");2042if (tts_enabled) {2043initialize_tts();2044}2045#endif20462047#ifdef ACCESSKIT_ENABLED2048if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {2049accessibility_driver = memnew(AccessibilityDriverAccessKit);2050if (accessibility_driver->init() != OK) {2051memdelete(accessibility_driver);2052accessibility_driver = nullptr;2053}2054}2055#endif20562057rendering_driver = p_rendering_driver;20582059bool driver_found = false;2060String executable_name = OS::get_singleton()->get_executable_path().get_file();20612062if (rendering_driver == "dummy") {2063RasterizerDummy::make_current();2064driver_found = true;2065}20662067#ifdef RD_ENABLED2068#ifdef VULKAN_ENABLED2069if (rendering_driver == "vulkan") {2070rendering_context = memnew(RenderingContextDriverVulkanWayland);2071}2072#endif // VULKAN_ENABLED20732074if (rendering_context) {2075if (rendering_context->initialize() != OK) {2076memdelete(rendering_context);2077rendering_context = nullptr;2078#if defined(GLES3_ENABLED)2079bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");2080if (fallback_to_opengl3 && rendering_driver != "opengl3") {2081WARN_PRINT("Your video card drivers seem not to support the required Vulkan version, switching to OpenGL 3.");2082rendering_driver = "opengl3";2083OS::get_singleton()->set_current_rendering_method("gl_compatibility", OS::RENDERING_SOURCE_FALLBACK);2084OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, OS::RENDERING_SOURCE_FALLBACK);2085} else2086#endif // GLES3_ENABLED2087{2088r_error = ERR_CANT_CREATE;20892090if (p_rendering_driver == "vulkan") {2091OS::get_singleton()->alert(2092vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"2093"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"2094"You can enable the OpenGL 3 driver by starting the engine from the\n"2095"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"2096"If you recently updated your video card drivers, try rebooting.",2097executable_name),2098"Unable to initialize Vulkan video driver");2099}21002101ERR_FAIL_MSG(vformat("Could not initialize %s", rendering_driver));2102}2103}21042105driver_found = true;2106}2107#endif // RD_ENABLED21082109#ifdef GLES3_ENABLED2110if (rendering_driver == "opengl3" || rendering_driver == "opengl3_es") {2111#ifdef SOWRAP_ENABLED2112if (initialize_wayland_egl(dylibloader_verbose) != 0) {2113WARN_PRINT("Can't load the Wayland EGL library.");2114return;2115}2116#endif // SOWRAP_ENABLED21172118if (getenv("DRI_PRIME") == nullptr) {2119int prime_idx = -1;21202121if (getenv("PRIMUS_DISPLAY") ||2122getenv("PRIMUS_libGLd") ||2123getenv("PRIMUS_libGLa") ||2124getenv("PRIMUS_libGL") ||2125getenv("PRIMUS_LOAD_GLOBAL") ||2126getenv("BUMBLEBEE_SOCKET") ||2127getenv("__NV_PRIME_RENDER_OFFLOAD")) {2128print_verbose("Optirun/primusrun detected. Skipping GPU detection");2129prime_idx = 0;2130}21312132// Some tools use fake libGL libraries and have them override the real one using2133// LD_LIBRARY_PATH, so we skip them. *But* Steam also sets LD_LIBRARY_PATH for its2134// runtime and includes system `/lib` and `/lib64`... so ignore Steam.2135if (prime_idx == -1 && getenv("LD_LIBRARY_PATH") && !getenv("STEAM_RUNTIME_LIBRARY_PATH")) {2136String ld_library_path(getenv("LD_LIBRARY_PATH"));2137Vector<String> libraries = ld_library_path.split(":");21382139for (int i = 0; i < libraries.size(); ++i) {2140if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||2141FileAccess::exists(libraries[i] + "/libGL.so")) {2142print_verbose("Custom libGL override detected. Skipping GPU detection");2143prime_idx = 0;2144}2145}2146}21472148if (prime_idx == -1) {2149print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");2150prime_idx = DetectPrimeEGL::detect_prime(EGL_PLATFORM_WAYLAND_KHR);2151}21522153if (prime_idx) {2154print_line(vformat("Found discrete GPU, setting DRI_PRIME=%d to use it.", prime_idx));2155print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");2156setenv("DRI_PRIME", itos(prime_idx).utf8().ptr(), 1);2157}2158}21592160if (rendering_driver == "opengl3") {2161egl_manager = memnew(EGLManagerWayland);21622163if (egl_manager->initialize(wayland_thread.get_wl_display()) != OK || egl_manager->open_display(wayland_thread.get_wl_display()) != OK) {2164memdelete(egl_manager);2165egl_manager = nullptr;21662167bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_gles");2168if (fallback) {2169WARN_PRINT("Your video card drivers seem not to support the required OpenGL version, switching to OpenGLES.");2170rendering_driver = "opengl3_es";2171OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, OS::RENDERING_SOURCE_FALLBACK);2172} else {2173r_error = ERR_UNAVAILABLE;21742175OS::get_singleton()->alert(2176vformat("Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"2177"If possible, consider updating your video card drivers or using the Vulkan driver.\n\n"2178"You can enable the Vulkan driver by starting the engine from the\n"2179"command line with the command:\n\n \"%s\" --rendering-driver vulkan\n\n"2180"If you recently updated your video card drivers, try rebooting.",2181executable_name),2182"Unable to initialize OpenGL video driver");21832184ERR_FAIL_MSG("Could not initialize OpenGL.");2185}2186} else {2187RasterizerGLES3::make_current(true);2188driver_found = true;2189}2190}21912192if (rendering_driver == "opengl3_es") {2193egl_manager = memnew(EGLManagerWaylandGLES);21942195if (egl_manager->initialize(wayland_thread.get_wl_display()) != OK || egl_manager->open_display(wayland_thread.get_wl_display()) != OK) {2196memdelete(egl_manager);2197egl_manager = nullptr;2198r_error = ERR_CANT_CREATE;21992200OS::get_singleton()->alert(2201vformat("Your video card drivers seem not to support the required OpenGL ES 3.0 version.\n\n"2202"If possible, consider updating your video card drivers or using the Vulkan driver.\n\n"2203"You can enable the Vulkan driver by starting the engine from the\n"2204"command line with the command:\n\n \"%s\" --rendering-driver vulkan\n\n"2205"If you recently updated your video card drivers, try rebooting.",2206executable_name),2207"Unable to initialize OpenGL ES video driver");22082209ERR_FAIL_MSG("Could not initialize OpenGL ES.");2210}22112212RasterizerGLES3::make_current(false);2213driver_found = true;2214}2215}2216#endif // GLES3_ENABLED22172218if (!driver_found) {2219r_error = ERR_UNAVAILABLE;2220ERR_FAIL_MSG("Video driver not found.");2221}22222223cursor_set_shape(CURSOR_BUSY);22242225WindowData &wd = windows[MAIN_WINDOW_ID];22262227wd.id = MAIN_WINDOW_ID;2228wd.mode = p_mode;2229wd.flags = p_flags;2230wd.vsync_mode = p_vsync_mode;2231wd.rect.size = p_resolution;2232wd.title = "Godot";22332234#ifdef ACCESSKIT_ENABLED2235if (accessibility_driver && !accessibility_driver->window_create(wd.id, nullptr)) {2236if (OS::get_singleton()->is_stdout_verbose()) {2237ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");2238}2239memdelete(accessibility_driver);2240accessibility_driver = nullptr;2241}2242#endif22432244show_window(MAIN_WINDOW_ID);22452246#ifdef RD_ENABLED2247if (rendering_context) {2248rendering_device = memnew(RenderingDevice);2249if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {2250memdelete(rendering_device);2251rendering_device = nullptr;2252memdelete(rendering_context);2253rendering_context = nullptr;2254r_error = ERR_UNAVAILABLE;2255return;2256}2257rendering_device->screen_create(MAIN_WINDOW_ID);22582259RendererCompositorRD::make_current();2260}2261#endif // RD_ENABLED22622263#ifdef DBUS_ENABLED2264bool dbus_ok = true;2265#ifdef SOWRAP_ENABLED2266if (initialize_dbus(dylibloader_verbose) != 0) {2267print_verbose("Failed to load DBus library!");2268dbus_ok = false;2269}2270#endif2271if (dbus_ok) {2272bool ver_ok = false;2273int version_major = 0;2274int version_minor = 0;2275int version_rev = 0;2276dbus_get_version(&version_major, &version_minor, &version_rev);2277ver_ok = (version_major == 1 && version_minor >= 10) || (version_major > 1); // 1.10.02278print_verbose(vformat("DBus %d.%d.%d detected.", version_major, version_minor, version_rev));2279if (!ver_ok) {2280print_verbose("Unsupported DBus library version!");2281dbus_ok = false;2282}2283}2284if (dbus_ok) {2285screensaver = memnew(FreeDesktopScreenSaver);2286portal_desktop = memnew(FreeDesktopPortalDesktop);2287atspi_monitor = memnew(FreeDesktopAtSPIMonitor);2288}2289#endif // DBUS_ENABLED22902291screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));22922293r_error = OK;2294}22952296DisplayServerWayland::~DisplayServerWayland() {2297if (native_menu) {2298memdelete(native_menu);2299native_menu = nullptr;2300}23012302// Iterating on the window map while we delete stuff from it is a bit2303// uncomfortable, plus we can't even delete /all/ windows in an arbitrary order2304// (due to popups).2305List<WindowID> toplevels;23062307for (const KeyValue<WindowID, WindowData> &pair : windows) {2308WindowID id = pair.key;23092310if (!window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, id)) {2311toplevels.push_back(id);2312#ifdef ACCESSKIT_ENABLED2313} else if (accessibility_driver) {2314accessibility_driver->window_destroy(id);2315#endif2316}2317}23182319for (WindowID &id : toplevels) {2320delete_sub_window(id);2321}2322windows.clear();23232324wayland_thread.destroy();23252326// Destroy all drivers.2327#ifdef RD_ENABLED2328if (rendering_device) {2329memdelete(rendering_device);2330}23312332if (rendering_context) {2333memdelete(rendering_context);2334}2335#endif23362337#ifdef SPEECHD_ENABLED2338if (tts) {2339memdelete(tts);2340}2341#endif23422343#ifdef ACCESSKIT_ENABLED2344if (accessibility_driver) {2345memdelete(accessibility_driver);2346}2347#endif23482349#ifdef DBUS_ENABLED2350if (portal_desktop) {2351memdelete(portal_desktop);2352}2353if (screensaver) {2354memdelete(screensaver);2355}2356if (atspi_monitor) {2357memdelete(atspi_monitor);2358}2359#endif2360}23612362void DisplayServerWayland::register_wayland_driver() {2363register_create_function("wayland", create_func, get_rendering_drivers_func);2364}23652366#endif //WAYLAND_ENABLED236723682369