Path: blob/master/platform/linuxbsd/wayland/display_server_wayland.cpp
11353 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/os/main_loop.h"42#include "servers/rendering/dummy/rasterizer_dummy.h"4344#ifdef VULKAN_ENABLED45#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"46#endif4748#ifdef GLES3_ENABLED49#include "core/io/file_access.h"50#include "detect_prime_egl.h"51#include "drivers/gles3/rasterizer_gles3.h"52#include "wayland/egl_manager_wayland.h"53#include "wayland/egl_manager_wayland_gles.h"54#endif5556#ifdef ACCESSKIT_ENABLED57#include "drivers/accesskit/accessibility_driver_accesskit.h"58#endif5960#ifdef DBUS_ENABLED61#ifdef SOWRAP_ENABLED62#include "dbus-so_wrap.h"63#else64#include <dbus/dbus.h>65#endif66#endif6768#define WAYLAND_MAX_FRAME_TIME_US (1'000'000)6970String DisplayServerWayland::_get_app_id_from_context(Context p_context) {71String app_id;7273switch (p_context) {74case CONTEXT_EDITOR: {75app_id = "org.godotengine.Editor";76} break;7778case CONTEXT_PROJECTMAN: {79app_id = "org.godotengine.ProjectManager";80} break;8182case CONTEXT_ENGINE:83default: {84String config_name = GLOBAL_GET("application/config/name");85if (config_name.length() != 0) {86app_id = config_name;87} else {88app_id = "org.godotengine.Godot";89}90}91}9293return app_id;94}9596void DisplayServerWayland::_send_window_event(WindowEvent p_event, WindowID p_window_id) {97ERR_FAIL_COND(!windows.has(p_window_id));9899WindowData &wd = windows[p_window_id];100101if (wd.window_event_callback.is_valid()) {102Variant event = int(p_event);103wd.window_event_callback.call(event);104}105}106107void DisplayServerWayland::dispatch_input_events(const Ref<InputEvent> &p_event) {108static_cast<DisplayServerWayland *>(get_singleton())->_dispatch_input_event(p_event);109}110111void DisplayServerWayland::_dispatch_input_event(const Ref<InputEvent> &p_event) {112Ref<InputEventFromWindow> event_from_window = p_event;113114if (event_from_window.is_valid()) {115WindowID window_id = event_from_window->get_window_id();116117Ref<InputEventKey> key_event = p_event;118if (!popup_menu_list.is_empty() && key_event.is_valid()) {119// Redirect to the highest popup menu.120window_id = popup_menu_list.back()->get();121}122123// Send to a single window.124if (windows.has(window_id)) {125Callable callable = windows[window_id].input_event_callback;126if (callable.is_valid()) {127callable.call(p_event);128}129}130} else {131// Send to all windows. Copy all pending callbacks, since callback can erase window.132Vector<Callable> cbs;133for (KeyValue<WindowID, WindowData> &E : windows) {134Callable callable = E.value.input_event_callback;135if (callable.is_valid()) {136cbs.push_back(callable);137}138}139140for (const Callable &cb : cbs) {141cb.call(p_event);142}143}144}145146void DisplayServerWayland::_update_window_rect(const Rect2i &p_rect, WindowID p_window_id) {147ERR_FAIL_COND(!windows.has(p_window_id));148149WindowData &wd = windows[p_window_id];150151if (wd.rect == p_rect) {152return;153}154155wd.rect = p_rect;156157#ifdef RD_ENABLED158if (wd.visible && rendering_context) {159rendering_context->window_set_size(p_window_id, wd.rect.size.width, wd.rect.size.height);160}161#endif162163#ifdef GLES3_ENABLED164if (wd.visible && egl_manager) {165wl_egl_window_resize(wd.wl_egl_window, wd.rect.size.width, wd.rect.size.height, 0, 0);166}167#endif168169if (wd.rect_changed_callback.is_valid()) {170wd.rect_changed_callback.call(wd.rect);171}172}173174// Interface methods.175176bool DisplayServerWayland::has_feature(Feature p_feature) const {177switch (p_feature) {178#ifndef DISABLE_DEPRECATED179case FEATURE_GLOBAL_MENU: {180return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));181} break;182#endif183case FEATURE_MOUSE:184case FEATURE_MOUSE_WARP:185case FEATURE_CLIPBOARD:186case FEATURE_CURSOR_SHAPE:187case FEATURE_CUSTOM_CURSOR_SHAPE:188case FEATURE_WINDOW_TRANSPARENCY:189case FEATURE_ICON:190case FEATURE_HIDPI:191case FEATURE_SWAP_BUFFERS:192case FEATURE_KEEP_SCREEN_ON:193case FEATURE_IME:194case FEATURE_WINDOW_DRAG:195case FEATURE_CLIPBOARD_PRIMARY:196case FEATURE_SUBWINDOWS:197case FEATURE_SELF_FITTING_WINDOWS: {198return true;199} break;200201//case FEATURE_NATIVE_DIALOG:202//case FEATURE_NATIVE_DIALOG_INPUT:203#ifdef DBUS_ENABLED204case FEATURE_NATIVE_DIALOG_FILE:205case FEATURE_NATIVE_DIALOG_FILE_EXTRA:206case FEATURE_NATIVE_DIALOG_FILE_MIME: {207return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_file_chooser_supported());208} break;209case FEATURE_NATIVE_COLOR_PICKER: {210return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_screenshot_supported());211} break;212#endif213214#ifdef SPEECHD_ENABLED215case FEATURE_TEXT_TO_SPEECH: {216return true;217} break;218#endif219220#ifdef ACCESSKIT_ENABLED221case FEATURE_ACCESSIBILITY_SCREEN_READER: {222return (accessibility_driver != nullptr);223} break;224#endif225226default: {227return false;228}229}230}231232String DisplayServerWayland::get_name() const {233return "Wayland";234}235236#ifdef SPEECHD_ENABLED237238void DisplayServerWayland::initialize_tts() const {239const_cast<DisplayServerWayland *>(this)->tts = memnew(TTS_Linux);240}241242bool DisplayServerWayland::tts_is_speaking() const {243if (unlikely(!tts)) {244initialize_tts();245}246ERR_FAIL_NULL_V(tts, false);247return tts->is_speaking();248}249250bool DisplayServerWayland::tts_is_paused() const {251if (unlikely(!tts)) {252initialize_tts();253}254ERR_FAIL_NULL_V(tts, false);255return tts->is_paused();256}257258TypedArray<Dictionary> DisplayServerWayland::tts_get_voices() const {259if (unlikely(!tts)) {260initialize_tts();261}262ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());263return tts->get_voices();264}265266void DisplayServerWayland::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {267if (unlikely(!tts)) {268initialize_tts();269}270ERR_FAIL_NULL(tts);271tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);272}273274void DisplayServerWayland::tts_pause() {275if (unlikely(!tts)) {276initialize_tts();277}278ERR_FAIL_NULL(tts);279tts->pause();280}281282void DisplayServerWayland::tts_resume() {283if (unlikely(!tts)) {284initialize_tts();285}286ERR_FAIL_NULL(tts);287tts->resume();288}289290void DisplayServerWayland::tts_stop() {291if (unlikely(!tts)) {292initialize_tts();293}294ERR_FAIL_NULL(tts);295tts->stop();296}297298#endif299300#ifdef DBUS_ENABLED301302bool DisplayServerWayland::is_dark_mode_supported() const {303return portal_desktop && portal_desktop->is_supported() && portal_desktop->is_settings_supported();304}305306bool DisplayServerWayland::is_dark_mode() const {307if (!is_dark_mode_supported()) {308return false;309}310switch (portal_desktop->get_appearance_color_scheme()) {311case 1:312// Prefers dark theme.313return true;314case 2:315// Prefers light theme.316return false;317default:318// Preference unknown.319return false;320}321}322323Color DisplayServerWayland::get_accent_color() const {324if (!portal_desktop) {325return Color();326}327return portal_desktop->get_appearance_accent_color();328}329330void DisplayServerWayland::set_system_theme_change_callback(const Callable &p_callable) {331ERR_FAIL_COND(!portal_desktop);332portal_desktop->set_system_theme_change_callback(p_callable);333}334335Error 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) {336ERR_FAIL_COND_V(!portal_desktop, ERR_UNAVAILABLE);337MutexLock mutex_lock(wayland_thread.mutex);338339WindowID window_id = p_window_id;340if (!windows.has(window_id) || window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, window_id)) {341window_id = MAIN_WINDOW_ID;342}343344WaylandThread::WindowState *ws = wayland_thread.window_get_state(window_id);345ERR_FAIL_NULL_V(ws, ERR_BUG);346347return 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);348}349350Error 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) {351ERR_FAIL_COND_V(!portal_desktop, ERR_UNAVAILABLE);352MutexLock mutex_lock(wayland_thread.mutex);353354WindowID window_id = p_window_id;355if (!windows.has(window_id) || window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, window_id)) {356window_id = MAIN_WINDOW_ID;357}358359WaylandThread::WindowState *ws = wayland_thread.window_get_state(window_id);360ERR_FAIL_NULL_V(ws, ERR_BUG);361362return 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);363}364365#endif366367void DisplayServerWayland::beep() const {368wayland_thread.beep();369}370371void DisplayServerWayland::_mouse_update_mode() {372MouseMode wanted_mouse_mode = mouse_mode_override_enabled373? mouse_mode_override374: mouse_mode_base;375376if (wanted_mouse_mode == mouse_mode) {377return;378}379380MutexLock mutex_lock(wayland_thread.mutex);381382bool show_cursor = (wanted_mouse_mode == MOUSE_MODE_VISIBLE || wanted_mouse_mode == MOUSE_MODE_CONFINED);383384wayland_thread.cursor_set_visible(show_cursor);385386WaylandThread::PointerConstraint constraint = WaylandThread::PointerConstraint::NONE;387388switch (wanted_mouse_mode) {389case DisplayServer::MOUSE_MODE_CAPTURED: {390constraint = WaylandThread::PointerConstraint::LOCKED;391} break;392393case DisplayServer::MOUSE_MODE_CONFINED:394case DisplayServer::MOUSE_MODE_CONFINED_HIDDEN: {395constraint = WaylandThread::PointerConstraint::CONFINED;396} break;397398default: {399}400}401402wayland_thread.pointer_set_constraint(constraint);403404if (wanted_mouse_mode == DisplayServer::MOUSE_MODE_CAPTURED) {405WindowData *pointed_win = windows.getptr(wayland_thread.pointer_get_pointed_window_id());406ERR_FAIL_NULL(pointed_win);407wayland_thread.pointer_set_hint(pointed_win->rect.size / 2);408}409410mouse_mode = wanted_mouse_mode;411}412413void DisplayServerWayland::mouse_set_mode(MouseMode p_mode) {414ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);415if (p_mode == mouse_mode_base) {416return;417}418mouse_mode_base = p_mode;419_mouse_update_mode();420}421422DisplayServerWayland::MouseMode DisplayServerWayland::mouse_get_mode() const {423return mouse_mode;424}425426void DisplayServerWayland::mouse_set_mode_override(MouseMode p_mode) {427ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);428if (p_mode == mouse_mode_override) {429return;430}431mouse_mode_override = p_mode;432_mouse_update_mode();433}434435DisplayServerWayland::MouseMode DisplayServerWayland::mouse_get_mode_override() const {436return mouse_mode_override;437}438439void DisplayServerWayland::mouse_set_mode_override_enabled(bool p_override_enabled) {440if (p_override_enabled == mouse_mode_override_enabled) {441return;442}443mouse_mode_override_enabled = p_override_enabled;444_mouse_update_mode();445}446447bool DisplayServerWayland::mouse_is_mode_override_enabled() const {448return mouse_mode_override_enabled;449}450451// NOTE: This is hacked together (and not guaranteed to work in the first place)452// as for some reason the there's no proper way to ask the compositor to warp453// the pointer, although, at the time of writing, there's a proposal for a454// proper protocol for this. See:455// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/158456void DisplayServerWayland::warp_mouse(const Point2i &p_to) {457MutexLock mutex_lock(wayland_thread.mutex);458459WaylandThread::PointerConstraint old_constraint = wayland_thread.pointer_get_constraint();460461wayland_thread.pointer_set_constraint(WaylandThread::PointerConstraint::LOCKED);462wayland_thread.pointer_set_hint(p_to);463464wayland_thread.pointer_set_constraint(old_constraint);465}466467Point2i DisplayServerWayland::mouse_get_position() const {468MutexLock mutex_lock(wayland_thread.mutex);469470WindowID pointed_id = wayland_thread.pointer_get_pointed_window_id();471472if (pointed_id != INVALID_WINDOW_ID && windows.has(pointed_id)) {473return Input::get_singleton()->get_mouse_position() + windows[pointed_id].rect.position;474}475476// We can't properly implement this method by design.477// This is the best we can do unfortunately.478return Input::get_singleton()->get_mouse_position();479}480481BitField<MouseButtonMask> DisplayServerWayland::mouse_get_button_state() const {482MutexLock mutex_lock(wayland_thread.mutex);483484// Are we sure this is the only way? This seems sus.485// TODO: Handle tablets properly.486//mouse_button_mask.set_flag(MouseButtonMask((int64_t)wls.current_seat->tablet_tool_data.pressed_button_mask));487488return wayland_thread.pointer_get_button_mask();489}490491// NOTE: According to the Wayland specification, this method will only do492// anything if the user has interacted with the application by sending a493// "recent enough" input event.494// TODO: Add this limitation to the documentation.495void DisplayServerWayland::clipboard_set(const String &p_text) {496MutexLock mutex_lock(wayland_thread.mutex);497498wayland_thread.selection_set_text(p_text);499}500501String DisplayServerWayland::clipboard_get() const {502MutexLock mutex_lock(wayland_thread.mutex);503504Vector<uint8_t> data;505506const String text_mimes[] = {507"text/plain;charset=utf-8",508"text/plain",509};510511for (String mime : text_mimes) {512if (wayland_thread.selection_has_mime(mime)) {513print_verbose(vformat("Selecting media type \"%s\" from offered types.", mime));514data = wayland_thread.selection_get_mime(mime);515break;516}517}518519return String::utf8((const char *)data.ptr(), data.size());520}521522Ref<Image> DisplayServerWayland::clipboard_get_image() const {523MutexLock mutex_lock(wayland_thread.mutex);524525Ref<Image> image;526image.instantiate();527528Error err = OK;529530// TODO: Fallback to next media type on missing module or parse error.531if (wayland_thread.selection_has_mime("image/png")) {532err = image->load_png_from_buffer(wayland_thread.selection_get_mime("image/png"));533} else if (wayland_thread.selection_has_mime("image/jpeg")) {534err = image->load_jpg_from_buffer(wayland_thread.selection_get_mime("image/jpeg"));535} else if (wayland_thread.selection_has_mime("image/webp")) {536err = image->load_webp_from_buffer(wayland_thread.selection_get_mime("image/webp"));537} else if (wayland_thread.selection_has_mime("image/svg+xml")) {538err = image->load_svg_from_buffer(wayland_thread.selection_get_mime("image/svg+xml"));539} else if (wayland_thread.selection_has_mime("image/bmp")) {540err = image->load_bmp_from_buffer(wayland_thread.selection_get_mime("image/bmp"));541} else if (wayland_thread.selection_has_mime("image/x-tga")) {542err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-tga"));543} else if (wayland_thread.selection_has_mime("image/x-targa")) {544err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-targa"));545} else if (wayland_thread.selection_has_mime("image/ktx")) {546err = image->load_ktx_from_buffer(wayland_thread.selection_get_mime("image/ktx"));547}548549ERR_FAIL_COND_V(err != OK, Ref<Image>());550551return image;552}553554void DisplayServerWayland::clipboard_set_primary(const String &p_text) {555MutexLock mutex_lock(wayland_thread.mutex);556557wayland_thread.primary_set_text(p_text);558}559560String DisplayServerWayland::clipboard_get_primary() const {561MutexLock mutex_lock(wayland_thread.mutex);562563Vector<uint8_t> data;564565const String text_mimes[] = {566"text/plain;charset=utf-8",567"text/plain",568};569570for (String mime : text_mimes) {571if (wayland_thread.primary_has_mime(mime)) {572print_verbose(vformat("Selecting media type \"%s\" from offered types.", mime));573data = wayland_thread.primary_get_mime(mime);574break;575}576}577578return String::utf8((const char *)data.ptr(), data.size());579}580581int DisplayServerWayland::get_screen_count() const {582MutexLock mutex_lock(wayland_thread.mutex);583return wayland_thread.get_screen_count();584}585586int DisplayServerWayland::get_primary_screen() const {587// AFAIK Wayland doesn't allow knowing (nor we care) about which screen is588// primary.589return 0;590}591592Point2i DisplayServerWayland::screen_get_position(int p_screen) const {593MutexLock mutex_lock(wayland_thread.mutex);594595p_screen = _get_screen_index(p_screen);596int screen_count = get_screen_count();597ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());598599return wayland_thread.screen_get_data(p_screen).position;600}601602Size2i DisplayServerWayland::screen_get_size(int p_screen) const {603MutexLock mutex_lock(wayland_thread.mutex);604605p_screen = _get_screen_index(p_screen);606int screen_count = get_screen_count();607ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());608609return wayland_thread.screen_get_data(p_screen).size;610}611612Rect2i DisplayServerWayland::screen_get_usable_rect(int p_screen) const {613p_screen = _get_screen_index(p_screen);614int screen_count = get_screen_count();615ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());616617return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));618}619620int DisplayServerWayland::screen_get_dpi(int p_screen) const {621MutexLock mutex_lock(wayland_thread.mutex);622623p_screen = _get_screen_index(p_screen);624int screen_count = get_screen_count();625ERR_FAIL_INDEX_V(p_screen, screen_count, 96);626627const WaylandThread::ScreenData &data = wayland_thread.screen_get_data(p_screen);628629int width_mm = data.physical_size.width;630int height_mm = data.physical_size.height;631632double xdpi = (width_mm ? data.size.width / (double)width_mm * 25.4 : 0);633double ydpi = (height_mm ? data.size.height / (double)height_mm * 25.4 : 0);634635if (xdpi || ydpi) {636return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1);637}638639// Could not get DPI.640return 96;641}642643float DisplayServerWayland::screen_get_scale(int p_screen) const {644MutexLock mutex_lock(wayland_thread.mutex);645646if (p_screen == SCREEN_OF_MAIN_WINDOW) {647// Wayland does not expose fractional scale factors at the screen-level, but648// some code relies on it. Since this special screen is the default and a lot649// of code relies on it, we'll return the window's scale, which is what we650// really care about. After all, we have very little use of the actual screen651// enumeration APIs and we're (for now) in single-window mode anyways.652struct wl_surface *wl_surface = wayland_thread.window_get_wl_surface(MAIN_WINDOW_ID);653WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wl_surface);654655return wayland_thread.window_state_get_scale_factor(ws);656}657658p_screen = _get_screen_index(p_screen);659int screen_count = get_screen_count();660ERR_FAIL_INDEX_V(p_screen, screen_count, 1.0f);661662return wayland_thread.screen_get_data(p_screen).scale;663}664665float DisplayServerWayland::screen_get_refresh_rate(int p_screen) const {666MutexLock mutex_lock(wayland_thread.mutex);667668p_screen = _get_screen_index(p_screen);669int screen_count = get_screen_count();670ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);671672return wayland_thread.screen_get_data(p_screen).refresh_rate;673}674675void DisplayServerWayland::screen_set_keep_on(bool p_enable) {676MutexLock mutex_lock(wayland_thread.mutex);677678// FIXME: For some reason this does not also windows from the wayland thread.679680if (screen_is_kept_on() == p_enable) {681return;682}683684wayland_thread.window_set_idle_inhibition(MAIN_WINDOW_ID, p_enable);685686#ifdef DBUS_ENABLED687if (screensaver) {688if (p_enable) {689screensaver->inhibit();690} else {691screensaver->uninhibit();692}693694screensaver_inhibited = p_enable;695}696#endif697}698699bool DisplayServerWayland::screen_is_kept_on() const {700// FIXME: Multiwindow support.701#ifdef DBUS_ENABLED702return wayland_thread.window_get_idle_inhibition(MAIN_WINDOW_ID) || screensaver_inhibited;703#else704return wayland_thread.window_get_idle_inhibition(MAIN_WINDOW_ID);705#endif706}707708Vector<DisplayServer::WindowID> DisplayServerWayland::get_window_list() const {709MutexLock mutex_lock(wayland_thread.mutex);710711Vector<int> ret;712for (const KeyValue<WindowID, WindowData> &E : windows) {713ret.push_back(E.key);714}715return ret;716}717718DisplayServer::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) {719WindowID id = ++window_id_counter;720WindowData &wd = windows[id];721722wd.id = id;723wd.mode = p_mode;724wd.flags = p_flags;725wd.vsync_mode = p_vsync_mode;726727#ifdef ACCESSKIT_ENABLED728if (accessibility_driver && !accessibility_driver->window_create(wd.id, nullptr)) {729if (OS::get_singleton()->is_stdout_verbose()) {730ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");731}732memdelete(accessibility_driver);733accessibility_driver = nullptr;734}735#endif736737// NOTE: Remember to clear its position if this window will be a toplevel. We738// can only know once we show it.739wd.rect = p_rect;740741wd.title = "Godot";742wd.parent_id = p_transient_parent;743return id;744}745746void DisplayServerWayland::show_window(WindowID p_window_id) {747MutexLock mutex_lock(wayland_thread.mutex);748749ERR_FAIL_COND(!windows.has(p_window_id));750751WindowData &wd = windows[p_window_id];752753if (!wd.visible) {754DEBUG_LOG_WAYLAND(vformat("Showing window %d", p_window_id));755// Showing this window will reset its mode with whatever the compositor756// reports. We'll save the mode beforehand so that we can reapply it later.757// TODO: Fix/Port/Move/Whatever to `WaylandThread` APIs.758WindowMode setup_mode = wd.mode;759760// Let's determine the closest toplevel. For toplevels it will be themselves,761// for popups the first toplevel ancestor it finds.762WindowID root_id = wd.id;763while (root_id != INVALID_WINDOW_ID && window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, root_id)) {764root_id = windows[root_id].parent_id;765}766ERR_FAIL_COND(root_id == INVALID_WINDOW_ID);767768wd.root_id = root_id;769770if (!window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, p_window_id)) {771// NOTE: DO **NOT** KEEP THE POSITION SET FOR TOPLEVELS. Wayland does not772// track them and we're gonna get our events transformed in unexpected ways.773wd.rect.position = Point2i();774775DEBUG_LOG_WAYLAND(vformat("Creating regular window of size %s", wd.rect.size));776wayland_thread.window_create(p_window_id, wd.rect.size.width, wd.rect.size.height);777wayland_thread.window_set_min_size(p_window_id, wd.min_size);778wayland_thread.window_set_max_size(p_window_id, wd.max_size);779wayland_thread.window_set_app_id(p_window_id, _get_app_id_from_context(context));780wayland_thread.window_set_borderless(p_window_id, window_get_flag(WINDOW_FLAG_BORDERLESS, p_window_id));781782if (wd.parent_id != INVALID_WINDOW_ID) {783wayland_thread.window_set_parent(wd.id, wd.parent_id);784}785786// Since it can't have a position. Let's tell the window node the news by787// the actual rect to it.788if (wd.rect_changed_callback.is_valid()) {789wd.rect_changed_callback.call(wd.rect);790}791} else {792DEBUG_LOG_WAYLAND("!!!!! Making popup !!!!!");793794windows[root_id].popup_stack.push_back(p_window_id);795796if (window_get_flag(WINDOW_FLAG_POPUP, p_window_id)) {797// Reroutes all input to it.798popup_menu_list.push_back(p_window_id);799}800801wayland_thread.window_create_popup(p_window_id, wd.parent_id, wd.rect);802}803804// NOTE: The XDG shell protocol is built in a way that causes the window to805// be immediately shown as soon as a valid buffer is assigned to it. Hence,806// the only acceptable way of implementing window showing is to move the807// graphics context window creation logic here.808#ifdef RD_ENABLED809if (rendering_context) {810union {811#ifdef VULKAN_ENABLED812RenderingContextDriverVulkanWayland::WindowPlatformData vulkan;813#endif814} wpd;815#ifdef VULKAN_ENABLED816if (rendering_driver == "vulkan") {817wpd.vulkan.surface = wayland_thread.window_get_wl_surface(wd.id);818wpd.vulkan.display = wayland_thread.get_wl_display();819}820#endif821Error err = rendering_context->window_create(wd.id, &wpd);822ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s window", rendering_driver));823824rendering_context->window_set_size(wd.id, wd.rect.size.width, wd.rect.size.height);825826// NOTE: Looks like we have to set the vsync mode before creating the screen827// or it won't work. Resist any temptation.828window_set_vsync_mode(wd.vsync_mode, p_window_id);829}830831if (rendering_device) {832rendering_device->screen_create(wd.id);833}834#endif835836#ifdef GLES3_ENABLED837if (egl_manager) {838struct wl_surface *wl_surface = wayland_thread.window_get_wl_surface(wd.id);839wd.wl_egl_window = wl_egl_window_create(wl_surface, wd.rect.size.width, wd.rect.size.height);840841Error 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);842ERR_FAIL_COND_MSG(err == ERR_CANT_CREATE, "Can't show a GLES3 window.");843844window_set_vsync_mode(wd.vsync_mode, p_window_id);845}846#endif847848// NOTE: Some public window-handling methods might depend on this flag being849// set. Make sure the method you're calling does not depend on it before this850// assignment.851wd.visible = true;852853// Actually try to apply the window's mode now that it's visible.854window_set_mode(setup_mode, wd.id);855856wayland_thread.window_set_title(p_window_id, wd.title);857}858}859860void DisplayServerWayland::delete_sub_window(WindowID p_window_id) {861MutexLock mutex_lock(wayland_thread.mutex);862863ERR_FAIL_COND(!windows.has(p_window_id));864WindowData &wd = windows[p_window_id];865866ERR_FAIL_COND(!windows.has(wd.root_id));867WindowData &root_wd = windows[wd.root_id];868869// NOTE: By the time the Wayland thread will send a `WINDOW_EVENT_MOUSE_EXIT`870// the window will be gone and the message will be discarded, confusing the871// engine. We thus have to send it ourselves.872if (wayland_thread.pointer_get_pointed_window_id() == p_window_id) {873_send_window_event(WINDOW_EVENT_MOUSE_EXIT, p_window_id);874}875876// The XDG shell specification requires us to clear all popups in reverse order.877while (!root_wd.popup_stack.is_empty() && root_wd.popup_stack.back()->get() != p_window_id) {878_send_window_event(WINDOW_EVENT_FORCE_CLOSE, root_wd.popup_stack.back()->get());879}880881if (root_wd.popup_stack.back() && root_wd.popup_stack.back()->get() == p_window_id) {882root_wd.popup_stack.pop_back();883}884885if (popup_menu_list.back() && popup_menu_list.back()->get() == p_window_id) {886popup_menu_list.pop_back();887}888889#ifdef ACCESSKIT_ENABLED890if (accessibility_driver) {891accessibility_driver->window_destroy(p_window_id);892}893#endif894895if (wd.visible) {896#ifdef VULKAN_ENABLED897if (rendering_device) {898rendering_device->screen_free(p_window_id);899}900901if (rendering_context) {902rendering_context->window_destroy(p_window_id);903}904#endif905906#ifdef GLES3_ENABLED907if (egl_manager) {908egl_manager->window_destroy(p_window_id);909}910#endif911912wayland_thread.window_destroy(p_window_id);913}914915windows.erase(p_window_id);916917DEBUG_LOG_WAYLAND(vformat("Destroyed window %d", p_window_id));918}919920DisplayServer::WindowID DisplayServerWayland::window_get_active_popup() const {921MutexLock mutex_lock(wayland_thread.mutex);922923if (!popup_menu_list.is_empty()) {924return popup_menu_list.back()->get();925}926927return INVALID_WINDOW_ID;928}929930void DisplayServerWayland::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {931MutexLock mutex_lock(wayland_thread.mutex);932933ERR_FAIL_COND(!windows.has(p_window));934935windows[p_window].safe_rect = p_rect;936}937938Rect2i DisplayServerWayland::window_get_popup_safe_rect(WindowID p_window) const {939MutexLock mutex_lock(wayland_thread.mutex);940941ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());942943return windows[p_window].safe_rect;944}945946int64_t DisplayServerWayland::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {947MutexLock mutex_lock(wayland_thread.mutex);948949switch (p_handle_type) {950case DISPLAY_HANDLE: {951return (int64_t)wayland_thread.get_wl_display();952} break;953954case WINDOW_HANDLE: {955return (int64_t)wayland_thread.window_get_wl_surface(p_window);956} break;957958case WINDOW_VIEW: {959return 0; // Not supported.960} break;961962#ifdef GLES3_ENABLED963case OPENGL_CONTEXT: {964if (egl_manager) {965return (int64_t)egl_manager->get_context(p_window);966}967return 0;968} break;969case EGL_DISPLAY: {970if (egl_manager) {971return (int64_t)egl_manager->get_display(p_window);972}973return 0;974}975case EGL_CONFIG: {976if (egl_manager) {977return (int64_t)egl_manager->get_config(p_window);978}979return 0;980}981#endif // GLES3_ENABLED982983default: {984return 0;985} break;986}987}988989DisplayServer::WindowID DisplayServerWayland::get_window_at_screen_position(const Point2i &p_position) const {990// Standard Wayland APIs don't support this.991return MAIN_WINDOW_ID;992}993994void DisplayServerWayland::window_attach_instance_id(ObjectID p_instance, WindowID p_window_id) {995MutexLock mutex_lock(wayland_thread.mutex);996997ERR_FAIL_COND(!windows.has(p_window_id));998999windows[p_window_id].instance_id = p_instance;1000}10011002ObjectID DisplayServerWayland::window_get_attached_instance_id(WindowID p_window_id) const {1003MutexLock mutex_lock(wayland_thread.mutex);10041005ERR_FAIL_COND_V(!windows.has(p_window_id), ObjectID());10061007return windows[p_window_id].instance_id;1008}10091010void DisplayServerWayland::window_set_title(const String &p_title, DisplayServer::WindowID p_window_id) {1011MutexLock mutex_lock(wayland_thread.mutex);10121013ERR_FAIL_COND(!windows.has(p_window_id));10141015WindowData &wd = windows[p_window_id];10161017wd.title = p_title;10181019if (wd.visible) {1020wayland_thread.window_set_title(p_window_id, wd.title);1021}1022}10231024void DisplayServerWayland::window_set_mouse_passthrough(const Vector<Vector2> &p_region, DisplayServer::WindowID p_window_id) {1025// TODO1026DEBUG_LOG_WAYLAND(vformat("wayland stub window_set_mouse_passthrough region %s", p_region));1027}10281029void DisplayServerWayland::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1030MutexLock mutex_lock(wayland_thread.mutex);10311032ERR_FAIL_COND(!windows.has(p_window_id));10331034windows[p_window_id].rect_changed_callback = p_callable;1035}10361037void DisplayServerWayland::window_set_window_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1038MutexLock mutex_lock(wayland_thread.mutex);10391040ERR_FAIL_COND(!windows.has(p_window_id));10411042windows[p_window_id].window_event_callback = p_callable;1043}10441045void DisplayServerWayland::window_set_input_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1046MutexLock mutex_lock(wayland_thread.mutex);10471048ERR_FAIL_COND(!windows.has(p_window_id));10491050windows[p_window_id].input_event_callback = p_callable;1051}10521053void DisplayServerWayland::window_set_input_text_callback(const Callable &p_callable, WindowID p_window_id) {1054MutexLock mutex_lock(wayland_thread.mutex);10551056ERR_FAIL_COND(!windows.has(p_window_id));10571058windows[p_window_id].input_text_callback = p_callable;1059}10601061void DisplayServerWayland::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1062MutexLock mutex_lock(wayland_thread.mutex);10631064ERR_FAIL_COND(!windows.has(p_window_id));10651066windows[p_window_id].drop_files_callback = p_callable;1067}10681069int DisplayServerWayland::window_get_current_screen(DisplayServer::WindowID p_window_id) const {1070ERR_FAIL_COND_V(!windows.has(p_window_id), INVALID_SCREEN);1071// Standard Wayland APIs don't support getting the screen of a window.1072return 0;1073}10741075void DisplayServerWayland::window_set_current_screen(int p_screen, DisplayServer::WindowID p_window_id) {1076// Standard Wayland APIs don't support setting the screen of a window.1077}10781079Point2i DisplayServerWayland::window_get_position(DisplayServer::WindowID p_window_id) const {1080MutexLock mutex_lock(wayland_thread.mutex);10811082return windows[p_window_id].rect.position;1083}10841085Point2i DisplayServerWayland::window_get_position_with_decorations(DisplayServer::WindowID p_window_id) const {1086MutexLock mutex_lock(wayland_thread.mutex);10871088return windows[p_window_id].rect.position;1089}10901091void DisplayServerWayland::window_set_position(const Point2i &p_position, DisplayServer::WindowID p_window_id) {1092// Unsupported with toplevels.1093}10941095void DisplayServerWayland::window_set_max_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1096MutexLock mutex_lock(wayland_thread.mutex);10971098DEBUG_LOG_WAYLAND(vformat("window max size set to %s", p_size));10991100if (p_size.x < 0 || p_size.y < 0) {1101ERR_FAIL_MSG("Maximum window size can't be negative!");1102}11031104ERR_FAIL_COND(!windows.has(p_window_id));1105WindowData &wd = windows[p_window_id];11061107// FIXME: Is `p_size.x < wd.min_size.x || p_size.y < wd.min_size.y` == `p_size < wd.min_size`?1108if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {1109ERR_PRINT("Maximum window size can't be smaller than minimum window size!");1110return;1111}11121113wd.max_size = p_size;11141115if (wd.visible) {1116wayland_thread.window_set_max_size(p_window_id, p_size);1117}1118}11191120Size2i DisplayServerWayland::window_get_max_size(DisplayServer::WindowID p_window_id) const {1121MutexLock mutex_lock(wayland_thread.mutex);11221123ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1124return windows[p_window_id].max_size;1125}11261127void DisplayServerWayland::gl_window_make_current(DisplayServer::WindowID p_window_id) {1128#ifdef GLES3_ENABLED1129if (egl_manager) {1130egl_manager->window_make_current(p_window_id);1131}1132#endif1133}11341135void DisplayServerWayland::window_set_transient(WindowID p_window_id, WindowID p_parent) {1136MutexLock mutex_lock(wayland_thread.mutex);11371138ERR_FAIL_COND(!windows.has(p_window_id));1139WindowData &wd = windows[p_window_id];11401141ERR_FAIL_COND(wd.parent_id == p_parent);11421143if (p_parent != INVALID_WINDOW_ID) {1144ERR_FAIL_COND(!windows.has(p_parent));1145ERR_FAIL_COND_MSG(wd.parent_id != INVALID_WINDOW_ID, "Window already has a transient parent");1146wd.parent_id = p_parent;11471148// NOTE: Looks like live unparenting is not really practical unfortunately.1149// See WaylandThread::window_set_parent for more info.1150if (wd.visible) {1151wayland_thread.window_set_parent(p_window_id, p_parent);1152}1153}1154}11551156void DisplayServerWayland::window_set_min_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1157MutexLock mutex_lock(wayland_thread.mutex);11581159DEBUG_LOG_WAYLAND(vformat("window minsize set to %s", p_size));11601161ERR_FAIL_COND(!windows.has(p_window_id));1162WindowData &wd = windows[p_window_id];11631164if (p_size.x < 0 || p_size.y < 0) {1165ERR_FAIL_MSG("Minimum window size can't be negative!");1166}11671168// FIXME: Is `p_size.x > wd.max_size.x || p_size.y > wd.max_size.y` == `p_size > wd.max_size`?1169if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {1170ERR_PRINT("Minimum window size can't be larger than maximum window size!");1171return;1172}11731174wd.min_size = p_size;11751176if (wd.visible) {1177wayland_thread.window_set_min_size(p_window_id, p_size);1178}1179}11801181Size2i DisplayServerWayland::window_get_min_size(DisplayServer::WindowID p_window_id) const {1182MutexLock mutex_lock(wayland_thread.mutex);11831184ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1185return windows[p_window_id].min_size;1186}11871188void DisplayServerWayland::window_set_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1189MutexLock mutex_lock(wayland_thread.mutex);11901191ERR_FAIL_COND(!windows.has(p_window_id));1192WindowData &wd = windows[p_window_id];11931194// The XDG spec doesn't allow non-interactive resizes. Let's update the1195// window's internal representation to account for that.1196if (wd.rect_changed_callback.is_valid()) {1197wd.rect_changed_callback.call(wd.rect);1198}1199}12001201Size2i DisplayServerWayland::window_get_size(DisplayServer::WindowID p_window_id) const {1202MutexLock mutex_lock(wayland_thread.mutex);12031204ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1205return windows[p_window_id].rect.size;1206}12071208Size2i DisplayServerWayland::window_get_size_with_decorations(DisplayServer::WindowID p_window_id) const {1209MutexLock mutex_lock(wayland_thread.mutex);12101211// I don't think there's a way of actually knowing the size of the window1212// decoration in Wayland, at least in the case of SSDs, nor that it would be1213// that useful in this case. We'll just return the main window's size.1214ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1215return windows[p_window_id].rect.size;1216}12171218void DisplayServerWayland::window_set_mode(WindowMode p_mode, DisplayServer::WindowID p_window_id) {1219MutexLock mutex_lock(wayland_thread.mutex);12201221ERR_FAIL_COND(!windows.has(p_window_id));1222WindowData &wd = windows[p_window_id];12231224if (!wd.visible) {1225return;1226}12271228wayland_thread.window_try_set_mode(p_window_id, p_mode);1229}12301231DisplayServer::WindowMode DisplayServerWayland::window_get_mode(DisplayServer::WindowID p_window_id) const {1232MutexLock mutex_lock(wayland_thread.mutex);12331234ERR_FAIL_COND_V(!windows.has(p_window_id), WINDOW_MODE_WINDOWED);1235const WindowData &wd = windows[p_window_id];12361237if (!wd.visible) {1238return WINDOW_MODE_WINDOWED;1239}12401241return wayland_thread.window_get_mode(p_window_id);1242}12431244bool DisplayServerWayland::window_is_maximize_allowed(DisplayServer::WindowID p_window_id) const {1245MutexLock mutex_lock(wayland_thread.mutex);12461247return wayland_thread.window_can_set_mode(p_window_id, WINDOW_MODE_MAXIMIZED);1248}12491250void DisplayServerWayland::window_set_flag(WindowFlags p_flag, bool p_enabled, DisplayServer::WindowID p_window_id) {1251MutexLock mutex_lock(wayland_thread.mutex);12521253ERR_FAIL_COND(!windows.has(p_window_id));1254WindowData &wd = windows[p_window_id];12551256DEBUG_LOG_WAYLAND(vformat("Window set flag %d", p_flag));12571258switch (p_flag) {1259case WINDOW_FLAG_BORDERLESS: {1260wayland_thread.window_set_borderless(p_window_id, p_enabled);1261} break;12621263case WINDOW_FLAG_POPUP: {1264ERR_FAIL_COND_MSG(p_window_id == MAIN_WINDOW_ID, "Main window can't be popup.");1265ERR_FAIL_COND_MSG(wd.visible && (wd.flags & WINDOW_FLAG_POPUP_BIT) != p_enabled, "Popup flag can't changed while window is opened.");1266} break;12671268case WINDOW_FLAG_POPUP_WM_HINT: {1269ERR_FAIL_COND_MSG(p_window_id == MAIN_WINDOW_ID, "Main window can't have popup hint.");1270ERR_FAIL_COND_MSG(wd.visible && (wd.flags & WINDOW_FLAG_POPUP_WM_HINT_BIT) != p_enabled, "Popup hint can't changed while window is opened.");1271} break;12721273default: {1274}1275}12761277if (p_enabled) {1278wd.flags |= 1 << p_flag;1279} else {1280wd.flags &= ~(1 << p_flag);1281}1282}12831284bool DisplayServerWayland::window_get_flag(WindowFlags p_flag, DisplayServer::WindowID p_window_id) const {1285MutexLock mutex_lock(wayland_thread.mutex);12861287ERR_FAIL_COND_V(!windows.has(p_window_id), false);1288return windows[p_window_id].flags & (1 << p_flag);1289}12901291void DisplayServerWayland::window_request_attention(DisplayServer::WindowID p_window_id) {1292MutexLock mutex_lock(wayland_thread.mutex);12931294DEBUG_LOG_WAYLAND("Requested attention.");12951296wayland_thread.window_request_attention(p_window_id);1297}12981299void DisplayServerWayland::window_move_to_foreground(DisplayServer::WindowID p_window_id) {1300// Standard Wayland APIs don't support this.1301}13021303bool DisplayServerWayland::window_is_focused(WindowID p_window_id) const {1304return wayland_thread.pointer_get_pointed_window_id() == p_window_id;1305}13061307bool DisplayServerWayland::window_can_draw(DisplayServer::WindowID p_window_id) const {1308MutexLock mutex_lock(wayland_thread.mutex);13091310uint64_t last_frame_time = wayland_thread.window_get_last_frame_time(p_window_id);1311uint64_t time_since_frame = OS::get_singleton()->get_ticks_usec() - last_frame_time;13121313if (time_since_frame > WAYLAND_MAX_FRAME_TIME_US) {1314return false;1315}13161317if (wayland_thread.window_is_suspended(p_window_id)) {1318return false;1319}13201321return suspend_state == SuspendState::NONE;1322}13231324bool DisplayServerWayland::can_any_window_draw() const {1325return suspend_state == SuspendState::NONE;1326}13271328void DisplayServerWayland::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) {1329MutexLock mutex_lock(wayland_thread.mutex);13301331wayland_thread.window_set_ime_active(p_active, p_window_id);1332}13331334void DisplayServerWayland::window_set_ime_position(const Point2i &p_pos, DisplayServer::WindowID p_window_id) {1335MutexLock mutex_lock(wayland_thread.mutex);13361337wayland_thread.window_set_ime_position(p_pos, p_window_id);1338}13391340int DisplayServerWayland::accessibility_should_increase_contrast() const {1341#ifdef DBUS_ENABLED1342if (!portal_desktop) {1343return -1;1344}1345return portal_desktop->get_high_contrast();1346#endif1347return -1;1348}13491350int DisplayServerWayland::accessibility_screen_reader_active() const {1351#ifdef DBUS_ENABLED1352if (atspi_monitor && atspi_monitor->is_supported()) {1353return atspi_monitor->is_active();1354}1355#endif1356return -1;1357}13581359Point2i DisplayServerWayland::ime_get_selection() const {1360return ime_selection;1361}13621363String DisplayServerWayland::ime_get_text() const {1364return ime_text;1365}13661367// NOTE: While Wayland is supposed to be tear-free, wayland-protocols version1368// 1.30 added a protocol for allowing async flips which is supposed to be1369// handled by drivers such as Vulkan. We can then just ask to disable v-sync and1370// hope for the best. See: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/commit/6394f0b4f3be151076f10a845a2fb131eeb567061371void DisplayServerWayland::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, DisplayServer::WindowID p_window_id) {1372MutexLock mutex_lock(wayland_thread.mutex);13731374WindowData &wd = windows[p_window_id];13751376#ifdef RD_ENABLED1377if (rendering_context) {1378rendering_context->window_set_vsync_mode(p_window_id, p_vsync_mode);13791380wd.emulate_vsync = (!wayland_thread.is_fifo_available() && rendering_context->window_get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED);13811382if (wd.emulate_vsync) {1383print_verbose("VSYNC: manually throttling frames using MAILBOX.");1384rendering_context->window_set_vsync_mode(p_window_id, DisplayServer::VSYNC_MAILBOX);1385}1386}1387#endif // VULKAN_ENABLED13881389#ifdef GLES3_ENABLED1390if (egl_manager) {1391egl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);13921393// NOTE: Mesa's EGL implementation does not seem to make use of fifo_v1 so1394// we'll have to always emulate V-Sync.1395wd.emulate_vsync = egl_manager->is_using_vsync();13961397if (wd.emulate_vsync) {1398print_verbose("VSYNC: manually throttling frames with swap delay 0.");1399egl_manager->set_use_vsync(false);1400}1401}1402#endif // GLES3_ENABLED1403}14041405DisplayServer::VSyncMode DisplayServerWayland::window_get_vsync_mode(DisplayServer::WindowID p_window_id) const {1406const WindowData &wd = windows[p_window_id];1407if (wd.emulate_vsync) {1408return DisplayServer::VSYNC_ENABLED;1409}14101411#ifdef VULKAN_ENABLED1412if (rendering_context) {1413return rendering_context->window_get_vsync_mode(p_window_id);1414}1415#endif // VULKAN_ENABLED14161417#ifdef GLES3_ENABLED1418if (egl_manager) {1419return egl_manager->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;1420}1421#endif // GLES3_ENABLED14221423return DisplayServer::VSYNC_ENABLED;1424}14251426void DisplayServerWayland::window_start_drag(WindowID p_window) {1427MutexLock mutex_lock(wayland_thread.mutex);14281429wayland_thread.window_start_drag(p_window);1430}14311432void DisplayServerWayland::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {1433MutexLock mutex_lock(wayland_thread.mutex);14341435ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);1436wayland_thread.window_start_resize(p_edge, p_window);1437}14381439void DisplayServerWayland::cursor_set_shape(CursorShape p_shape) {1440ERR_FAIL_INDEX(p_shape, CURSOR_MAX);14411442MutexLock mutex_lock(wayland_thread.mutex);14431444if (p_shape == cursor_shape) {1445return;1446}14471448cursor_shape = p_shape;14491450if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {1451// Hidden.1452return;1453}14541455wayland_thread.cursor_set_shape(p_shape);1456}14571458DisplayServerWayland::CursorShape DisplayServerWayland::cursor_get_shape() const {1459MutexLock mutex_lock(wayland_thread.mutex);14601461return cursor_shape;1462}14631464void DisplayServerWayland::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {1465MutexLock mutex_lock(wayland_thread.mutex);14661467if (p_cursor.is_valid()) {1468HashMap<CursorShape, CustomCursor>::Iterator cursor_c = custom_cursors.find(p_shape);14691470if (cursor_c) {1471if (cursor_c->value.resource == p_cursor && cursor_c->value.hotspot == p_hotspot) {1472// We have a cached cursor. Nice.1473wayland_thread.cursor_set_shape(p_shape);1474return;1475}14761477// We're changing this cursor; we'll have to rebuild it.1478custom_cursors.erase(p_shape);1479wayland_thread.cursor_shape_clear_custom_image(p_shape);1480}14811482Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);1483ERR_FAIL_COND(image.is_null());14841485CustomCursor &cursor = custom_cursors[p_shape];14861487cursor.resource = p_cursor;1488cursor.hotspot = p_hotspot;14891490wayland_thread.cursor_shape_set_custom_image(p_shape, image, p_hotspot);14911492wayland_thread.cursor_set_shape(p_shape);1493} else {1494// Clear cache and reset to default system cursor.1495wayland_thread.cursor_shape_clear_custom_image(p_shape);14961497if (cursor_shape == p_shape) {1498wayland_thread.cursor_set_shape(p_shape);1499}15001501if (custom_cursors.has(p_shape)) {1502custom_cursors.erase(p_shape);1503}1504}1505}15061507bool DisplayServerWayland::get_swap_cancel_ok() {1508return swap_cancel_ok;1509}15101511int DisplayServerWayland::keyboard_get_layout_count() const {1512MutexLock mutex_lock(wayland_thread.mutex);15131514return wayland_thread.keyboard_get_layout_count();1515}15161517int DisplayServerWayland::keyboard_get_current_layout() const {1518MutexLock mutex_lock(wayland_thread.mutex);15191520return wayland_thread.keyboard_get_current_layout_index();1521}15221523void DisplayServerWayland::keyboard_set_current_layout(int p_index) {1524MutexLock mutex_lock(wayland_thread.mutex);15251526wayland_thread.keyboard_set_current_layout_index(p_index);1527}15281529String DisplayServerWayland::keyboard_get_layout_language(int p_index) const {1530MutexLock mutex_lock(wayland_thread.mutex);15311532// xkbcommon exposes only the layout's name, which looks like it overlaps with1533// its language.1534return wayland_thread.keyboard_get_layout_name(p_index);1535}15361537String DisplayServerWayland::keyboard_get_layout_name(int p_index) const {1538MutexLock mutex_lock(wayland_thread.mutex);15391540return wayland_thread.keyboard_get_layout_name(p_index);1541}15421543Key DisplayServerWayland::keyboard_get_keycode_from_physical(Key p_keycode) const {1544MutexLock mutex_lock(wayland_thread.mutex);15451546Key key = wayland_thread.keyboard_get_key_from_physical(p_keycode);15471548// If not found, fallback to QWERTY.1549// This should match the behavior of the event pump.1550if (key == Key::NONE) {1551return p_keycode;1552}15531554if (key >= Key::A + 32 && key <= Key::Z + 32) {1555key -= 'a' - 'A';1556}15571558// Make it consistent with the keys returned by `Input`.1559if (key == Key::BACKTAB) {1560key = Key::TAB;1561}15621563return key;1564}15651566bool DisplayServerWayland::color_picker(const Callable &p_callback) {1567#ifdef DBUS_ENABLED1568if (!portal_desktop) {1569return false;1570}1571MutexLock mutex_lock(wayland_thread.mutex);1572WindowID window_id = MAIN_WINDOW_ID;1573// TODO: Use window IDs for multiwindow support.1574WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wayland_thread.window_get_wl_surface(window_id));1575return portal_desktop->color_picker((ws ? ws->exported_handle : String()), p_callback);1576#else1577return false;1578#endif1579}15801581void DisplayServerWayland::try_suspend() {1582// Due to various reasons, we manually handle display synchronization by1583// waiting for a frame event (request to draw) or, if available, the actual1584// window's suspend status. When a window is suspended, we can avoid drawing1585// altogether, either because the compositor told us that we don't need to or1586// because the pace of the frame events became unreliable.1587bool frame = wayland_thread.wait_frame_suspend_ms(WAYLAND_MAX_FRAME_TIME_US / 1000);1588if (!frame) {1589suspend_state = SuspendState::TIMEOUT;1590}1591}15921593void DisplayServerWayland::process_events() {1594wayland_thread.mutex.lock();15951596while (wayland_thread.has_message()) {1597Ref<WaylandThread::Message> msg = wayland_thread.pop_message();15981599// Generic check. Not actual message handling.1600Ref<WaylandThread::WindowMessage> win_msg = msg;1601if (win_msg.is_valid()) {1602ERR_CONTINUE_MSG(win_msg->id == INVALID_WINDOW_ID, "Invalid window ID received from Wayland thread.");16031604if (!windows.has(win_msg->id)) {1605// Window got probably deleted.1606continue;1607}1608}16091610Ref<WaylandThread::WindowRectMessage> winrect_msg = msg;1611if (winrect_msg.is_valid()) {1612_update_window_rect(winrect_msg->rect, winrect_msg->id);1613continue;1614}16151616Ref<WaylandThread::WindowEventMessage> winev_msg = msg;1617if (winev_msg.is_valid() && windows.has(winev_msg->id)) {1618_send_window_event(winev_msg->event, winev_msg->id);16191620if (winev_msg->event == WINDOW_EVENT_FOCUS_IN) {1621#ifdef ACCESSKIT_ENABLED1622if (accessibility_driver) {1623accessibility_driver->accessibility_set_window_focused(winev_msg->id, true);1624}1625#endif1626if (OS::get_singleton()->get_main_loop()) {1627OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);1628}1629} else if (winev_msg->event == WINDOW_EVENT_FOCUS_OUT) {1630#ifdef ACCESSKIT_ENABLED1631if (accessibility_driver) {1632accessibility_driver->accessibility_set_window_focused(winev_msg->id, false);1633}1634#endif1635if (OS::get_singleton()->get_main_loop()) {1636OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);1637}1638Input::get_singleton()->release_pressed_events();1639}1640continue;1641}16421643Ref<WaylandThread::InputEventMessage> inputev_msg = msg;1644if (inputev_msg.is_valid()) {1645Ref<InputEventMouseButton> mb = inputev_msg->event;16461647bool handled = false;1648if (!popup_menu_list.is_empty() && mb.is_valid()) {1649// Popup menu handling.16501651BitField<MouseButtonMask> mouse_mask = mb->get_button_mask();1652if (mouse_mask != last_mouse_monitor_mask && mb->is_pressed()) {1653List<WindowID>::Element *E = popup_menu_list.back();1654List<WindowID>::Element *C = nullptr;16551656// Looking for the oldest popup to close.1657while (E) {1658WindowData &wd = windows[E->get()];1659Point2 global_pos = mb->get_position() + window_get_position(mb->get_window_id());1660if (wd.rect.has_point(global_pos)) {1661break;1662} else if (wd.safe_rect.has_point(global_pos)) {1663break;1664}16651666C = E;1667E = E->prev();1668}16691670if (C) {1671handled = true;1672_send_window_event(WINDOW_EVENT_CLOSE_REQUEST, C->get());1673}1674}16751676last_mouse_monitor_mask = mouse_mask;1677}16781679if (!handled) {1680Input::get_singleton()->parse_input_event(inputev_msg->event);1681}1682continue;1683}16841685Ref<WaylandThread::DropFilesEventMessage> dropfiles_msg = msg;1686if (dropfiles_msg.is_valid()) {1687WindowData wd = windows[dropfiles_msg->id];16881689if (wd.drop_files_callback.is_valid()) {1690Variant v_files = dropfiles_msg->files;1691const Variant *v_args[1] = { &v_files };1692Variant ret;1693Callable::CallError ce;1694wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);1695if (ce.error != Callable::CallError::CALL_OK) {1696ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce)));1697}1698}1699continue;1700}17011702Ref<WaylandThread::IMECommitEventMessage> ime_commit_msg = msg;1703if (ime_commit_msg.is_valid()) {1704for (int i = 0; i < ime_commit_msg->text.length(); i++) {1705const char32_t codepoint = ime_commit_msg->text[i];17061707Ref<InputEventKey> ke;1708ke.instantiate();1709ke->set_window_id(ime_commit_msg->id);1710ke->set_pressed(true);1711ke->set_echo(false);1712ke->set_keycode(Key::NONE);1713ke->set_physical_keycode(Key::NONE);1714ke->set_key_label(Key::NONE);1715ke->set_unicode(codepoint);17161717Input::get_singleton()->parse_input_event(ke);1718}1719ime_text = String();1720ime_selection = Vector2i();17211722OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);1723continue;1724}17251726Ref<WaylandThread::IMEUpdateEventMessage> ime_update_msg = msg;1727if (ime_update_msg.is_valid()) {1728if (ime_text != ime_update_msg->text || ime_selection != ime_update_msg->selection) {1729ime_text = ime_update_msg->text;1730ime_selection = ime_update_msg->selection;17311732OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);1733}1734continue;1735}1736}17371738wayland_thread.keyboard_echo_keys();17391740switch (suspend_state) {1741case SuspendState::NONE: {1742bool emulate_vsync = false;1743for (KeyValue<DisplayServer::WindowID, WindowData> &pair : windows) {1744if (pair.value.emulate_vsync) {1745emulate_vsync = true;1746break;1747}1748}17491750if (emulate_vsync) {1751// Due to the way legacy suspension works, we have to treat low processor1752// usage mode very differently than the regular one.1753if (OS::get_singleton()->is_in_low_processor_usage_mode()) {1754// NOTE: We must avoid committing a surface if we expect a new frame, as we1755// might otherwise commit some inconsistent data (e.g. buffer scale). Note1756// that if a new frame is expected it's going to be committed by the renderer1757// soon anyways.1758if (!RenderingServer::get_singleton()->has_changed()) {1759// We _can't_ commit in a different thread (such as in the frame callback1760// itself) because we would risk to step on the renderer's feet, which would1761// cause subtle but severe issues, such as crashes on setups with explicit1762// sync. This isn't normally a problem, as the renderer commits at every1763// frame (which is what we need for atomic surface updates anyways), but in1764// low processor usage mode that expectation is broken. When it's on, our1765// frame rate stops being constant. This also reflects in the frame1766// information we use for legacy suspension. In order to avoid issues, let's1767// manually commit all surfaces, so that we can get fresh frame data.1768wayland_thread.commit_surfaces();1769try_suspend();1770}1771} else {1772try_suspend();1773}1774}17751776if (wayland_thread.is_suspended()) {1777suspend_state = SuspendState::CAPABILITY;1778}17791780if (suspend_state == SuspendState::TIMEOUT) {1781DEBUG_LOG_WAYLAND("Suspending. Reason: timeout.");1782} else if (suspend_state == SuspendState::CAPABILITY) {1783DEBUG_LOG_WAYLAND("Suspending. Reason: capability.");1784}1785} break;17861787case SuspendState::TIMEOUT: {1788// Certain compositors might not report the "suspended" wm_capability flag.1789// Because of this we'll wake up at the next frame event, indicating the1790// desire for the compositor to let us repaint.1791if (wayland_thread.get_reset_frame()) {1792suspend_state = SuspendState::NONE;1793DEBUG_LOG_WAYLAND("Unsuspending from timeout.");1794}17951796// Since we're not rendering, nothing is committing the windows'1797// surfaces. We have to do it ourselves.1798wayland_thread.commit_surfaces();1799} break;18001801case SuspendState::CAPABILITY: {1802// If we suspended by capability we can assume that it will be reset when1803// the compositor wants us to repaint.1804if (!wayland_thread.is_suspended()) {1805suspend_state = SuspendState::NONE;1806DEBUG_LOG_WAYLAND("Unsuspending from capability.");1807}1808} break;1809}18101811#ifdef DBUS_ENABLED1812if (portal_desktop) {1813portal_desktop->process_callbacks();1814}1815#endif18161817wayland_thread.mutex.unlock();18181819Input::get_singleton()->flush_buffered_events();1820}18211822void DisplayServerWayland::release_rendering_thread() {1823#ifdef GLES3_ENABLED1824if (egl_manager) {1825egl_manager->release_current();1826}1827#endif1828}18291830void DisplayServerWayland::swap_buffers() {1831#ifdef GLES3_ENABLED1832if (egl_manager) {1833egl_manager->swap_buffers();1834}1835#endif1836}18371838void DisplayServerWayland::set_icon(const Ref<Image> &p_icon) {1839MutexLock mutex_lock(wayland_thread.mutex);1840wayland_thread.set_icon(p_icon);1841}18421843void DisplayServerWayland::set_context(Context p_context) {1844MutexLock mutex_lock(wayland_thread.mutex);18451846DEBUG_LOG_WAYLAND(vformat("Setting context %d.", p_context));18471848context = p_context;18491850String app_id = _get_app_id_from_context(p_context);1851wayland_thread.window_set_app_id(MAIN_WINDOW_ID, app_id);1852}18531854bool DisplayServerWayland::is_window_transparency_available() const {1855#if defined(RD_ENABLED)1856if (rendering_device && !rendering_device->is_composite_alpha_supported()) {1857return false;1858}1859#endif1860return OS::get_singleton()->is_layered_allowed();1861}18621863Vector<String> DisplayServerWayland::get_rendering_drivers_func() {1864Vector<String> drivers;18651866#ifdef VULKAN_ENABLED1867drivers.push_back("vulkan");1868#endif18691870#ifdef GLES3_ENABLED1871drivers.push_back("opengl3");1872drivers.push_back("opengl3_es");1873#endif1874drivers.push_back("dummy");18751876return drivers;1877}18781879DisplayServer *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) {1880DisplayServer *ds = memnew(DisplayServerWayland(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, p_context, p_parent_window, r_error));1881if (r_error != OK) {1882ERR_PRINT("Can't create the Wayland display server.");1883memdelete(ds);18841885return nullptr;1886}1887return ds;1888}18891890DisplayServerWayland::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) {1891#if defined(GLES3_ENABLED) || defined(DBUS_ENABLED)1892#ifdef SOWRAP_ENABLED1893#ifdef DEBUG_ENABLED1894int dylibloader_verbose = 1;1895#else1896int dylibloader_verbose = 0;1897#endif // DEBUG_ENABLED1898#endif // SOWRAP_ENABLED1899#endif // defined(GLES3_ENABLED) || defined(DBUS_ENABLED)19001901r_error = ERR_UNAVAILABLE;1902context = p_context;19031904String current_desk = OS::get_singleton()->get_environment("XDG_CURRENT_DESKTOP").to_lower();1905String session_desk = OS::get_singleton()->get_environment("XDG_SESSION_DESKTOP").to_lower();1906swap_cancel_ok = (current_desk.contains("kde") || session_desk.contains("kde") || current_desk.contains("lxqt") || session_desk.contains("lxqt"));19071908Error thread_err = wayland_thread.init();19091910if (thread_err != OK) {1911r_error = thread_err;1912ERR_FAIL_MSG("Could not initialize the Wayland thread.");1913}19141915// Input.1916Input::get_singleton()->set_event_dispatch_function(dispatch_input_events);19171918native_menu = memnew(NativeMenu);19191920#ifdef SPEECHD_ENABLED1921// Init TTS1922bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");1923if (tts_enabled) {1924initialize_tts();1925}1926#endif19271928#ifdef ACCESSKIT_ENABLED1929if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {1930accessibility_driver = memnew(AccessibilityDriverAccessKit);1931if (accessibility_driver->init() != OK) {1932memdelete(accessibility_driver);1933accessibility_driver = nullptr;1934}1935}1936#endif19371938rendering_driver = p_rendering_driver;19391940bool driver_found = false;1941String executable_name = OS::get_singleton()->get_executable_path().get_file();19421943if (rendering_driver == "dummy") {1944RasterizerDummy::make_current();1945driver_found = true;1946}19471948#ifdef RD_ENABLED1949#ifdef VULKAN_ENABLED1950if (rendering_driver == "vulkan") {1951rendering_context = memnew(RenderingContextDriverVulkanWayland);1952}1953#endif // VULKAN_ENABLED19541955if (rendering_context) {1956if (rendering_context->initialize() != OK) {1957memdelete(rendering_context);1958rendering_context = nullptr;1959#if defined(GLES3_ENABLED)1960bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");1961if (fallback_to_opengl3 && rendering_driver != "opengl3") {1962WARN_PRINT("Your video card drivers seem not to support the required Vulkan version, switching to OpenGL 3.");1963rendering_driver = "opengl3";1964OS::get_singleton()->set_current_rendering_method("gl_compatibility");1965OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);1966} else1967#endif // GLES3_ENABLED1968{1969r_error = ERR_CANT_CREATE;19701971if (p_rendering_driver == "vulkan") {1972OS::get_singleton()->alert(1973vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"1974"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"1975"You can enable the OpenGL 3 driver by starting the engine from the\n"1976"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"1977"If you recently updated your video card drivers, try rebooting.",1978executable_name),1979"Unable to initialize Vulkan video driver");1980}19811982ERR_FAIL_MSG(vformat("Could not initialize %s", rendering_driver));1983}1984}19851986driver_found = true;1987}1988#endif // RD_ENABLED19891990#ifdef GLES3_ENABLED1991if (rendering_driver == "opengl3" || rendering_driver == "opengl3_es") {1992#ifdef SOWRAP_ENABLED1993if (initialize_wayland_egl(dylibloader_verbose) != 0) {1994WARN_PRINT("Can't load the Wayland EGL library.");1995return;1996}1997#endif // SOWRAP_ENABLED19981999if (getenv("DRI_PRIME") == nullptr) {2000int prime_idx = -1;20012002if (getenv("PRIMUS_DISPLAY") ||2003getenv("PRIMUS_libGLd") ||2004getenv("PRIMUS_libGLa") ||2005getenv("PRIMUS_libGL") ||2006getenv("PRIMUS_LOAD_GLOBAL") ||2007getenv("BUMBLEBEE_SOCKET") ||2008getenv("__NV_PRIME_RENDER_OFFLOAD")) {2009print_verbose("Optirun/primusrun detected. Skipping GPU detection");2010prime_idx = 0;2011}20122013// Some tools use fake libGL libraries and have them override the real one using2014// LD_LIBRARY_PATH, so we skip them. *But* Steam also sets LD_LIBRARY_PATH for its2015// runtime and includes system `/lib` and `/lib64`... so ignore Steam.2016if (prime_idx == -1 && getenv("LD_LIBRARY_PATH") && !getenv("STEAM_RUNTIME_LIBRARY_PATH")) {2017String ld_library_path(getenv("LD_LIBRARY_PATH"));2018Vector<String> libraries = ld_library_path.split(":");20192020for (int i = 0; i < libraries.size(); ++i) {2021if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||2022FileAccess::exists(libraries[i] + "/libGL.so")) {2023print_verbose("Custom libGL override detected. Skipping GPU detection");2024prime_idx = 0;2025}2026}2027}20282029if (prime_idx == -1) {2030print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");2031prime_idx = DetectPrimeEGL::detect_prime(EGL_PLATFORM_WAYLAND_KHR);2032}20332034if (prime_idx) {2035print_line(vformat("Found discrete GPU, setting DRI_PRIME=%d to use it.", prime_idx));2036print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");2037setenv("DRI_PRIME", itos(prime_idx).utf8().ptr(), 1);2038}2039}20402041if (rendering_driver == "opengl3") {2042egl_manager = memnew(EGLManagerWayland);20432044if (egl_manager->initialize(wayland_thread.get_wl_display()) != OK || egl_manager->open_display(wayland_thread.get_wl_display()) != OK) {2045memdelete(egl_manager);2046egl_manager = nullptr;20472048bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_gles");2049if (fallback) {2050WARN_PRINT("Your video card drivers seem not to support the required OpenGL version, switching to OpenGLES.");2051rendering_driver = "opengl3_es";2052OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);2053} else {2054r_error = ERR_UNAVAILABLE;20552056OS::get_singleton()->alert(2057vformat("Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"2058"If possible, consider updating your video card drivers or using the Vulkan driver.\n\n"2059"You can enable the Vulkan driver by starting the engine from the\n"2060"command line with the command:\n\n \"%s\" --rendering-driver vulkan\n\n"2061"If you recently updated your video card drivers, try rebooting.",2062executable_name),2063"Unable to initialize OpenGL video driver");20642065ERR_FAIL_MSG("Could not initialize OpenGL.");2066}2067} else {2068RasterizerGLES3::make_current(true);2069driver_found = true;2070}2071}20722073if (rendering_driver == "opengl3_es") {2074egl_manager = memnew(EGLManagerWaylandGLES);20752076if (egl_manager->initialize(wayland_thread.get_wl_display()) != OK || egl_manager->open_display(wayland_thread.get_wl_display()) != OK) {2077memdelete(egl_manager);2078egl_manager = nullptr;2079r_error = ERR_CANT_CREATE;20802081OS::get_singleton()->alert(2082vformat("Your video card drivers seem not to support the required OpenGL ES 3.0 version.\n\n"2083"If possible, consider updating your video card drivers or using the Vulkan driver.\n\n"2084"You can enable the Vulkan driver by starting the engine from the\n"2085"command line with the command:\n\n \"%s\" --rendering-driver vulkan\n\n"2086"If you recently updated your video card drivers, try rebooting.",2087executable_name),2088"Unable to initialize OpenGL ES video driver");20892090ERR_FAIL_MSG("Could not initialize OpenGL ES.");2091}20922093RasterizerGLES3::make_current(false);2094driver_found = true;2095}2096}2097#endif // GLES3_ENABLED20982099if (!driver_found) {2100r_error = ERR_UNAVAILABLE;2101ERR_FAIL_MSG("Video driver not found.");2102}21032104cursor_set_shape(CURSOR_BUSY);21052106WindowData &wd = windows[MAIN_WINDOW_ID];21072108wd.id = MAIN_WINDOW_ID;2109wd.mode = p_mode;2110wd.flags = p_flags;2111wd.vsync_mode = p_vsync_mode;2112wd.rect.size = p_resolution;2113wd.title = "Godot";21142115#ifdef ACCESSKIT_ENABLED2116if (accessibility_driver && !accessibility_driver->window_create(wd.id, nullptr)) {2117if (OS::get_singleton()->is_stdout_verbose()) {2118ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");2119}2120memdelete(accessibility_driver);2121accessibility_driver = nullptr;2122}2123#endif21242125show_window(MAIN_WINDOW_ID);21262127#ifdef RD_ENABLED2128if (rendering_context) {2129rendering_device = memnew(RenderingDevice);2130if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {2131memdelete(rendering_device);2132rendering_device = nullptr;2133memdelete(rendering_context);2134rendering_context = nullptr;2135r_error = ERR_UNAVAILABLE;2136return;2137}2138rendering_device->screen_create(MAIN_WINDOW_ID);21392140RendererCompositorRD::make_current();2141}2142#endif // RD_ENABLED21432144#ifdef DBUS_ENABLED2145bool dbus_ok = true;2146#ifdef SOWRAP_ENABLED2147if (initialize_dbus(dylibloader_verbose) != 0) {2148print_verbose("Failed to load DBus library!");2149dbus_ok = false;2150}2151#endif2152if (dbus_ok) {2153bool ver_ok = false;2154int version_major = 0;2155int version_minor = 0;2156int version_rev = 0;2157dbus_get_version(&version_major, &version_minor, &version_rev);2158ver_ok = (version_major == 1 && version_minor >= 10) || (version_major > 1); // 1.10.02159print_verbose(vformat("DBus %d.%d.%d detected.", version_major, version_minor, version_rev));2160if (!ver_ok) {2161print_verbose("Unsupported DBus library version!");2162dbus_ok = false;2163}2164}2165if (dbus_ok) {2166screensaver = memnew(FreeDesktopScreenSaver);2167portal_desktop = memnew(FreeDesktopPortalDesktop);2168atspi_monitor = memnew(FreeDesktopAtSPIMonitor);2169}2170#endif // DBUS_ENABLED21712172screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));21732174r_error = OK;2175}21762177DisplayServerWayland::~DisplayServerWayland() {2178if (native_menu) {2179memdelete(native_menu);2180native_menu = nullptr;2181}21822183// Iterating on the window map while we delete stuff from it is a bit2184// uncomfortable, plus we can't even delete /all/ windows in an arbitrary order2185// (due to popups).2186List<WindowID> toplevels;21872188for (const KeyValue<WindowID, WindowData> &pair : windows) {2189WindowID id = pair.key;21902191if (!window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, id)) {2192toplevels.push_back(id);2193#ifdef ACCESSKIT_ENABLED2194} else if (accessibility_driver) {2195accessibility_driver->window_destroy(id);2196#endif2197}2198}21992200for (WindowID &id : toplevels) {2201delete_sub_window(id);2202}2203windows.clear();22042205wayland_thread.destroy();22062207// Destroy all drivers.2208#ifdef RD_ENABLED2209if (rendering_device) {2210memdelete(rendering_device);2211}22122213if (rendering_context) {2214memdelete(rendering_context);2215}2216#endif22172218#ifdef SPEECHD_ENABLED2219if (tts) {2220memdelete(tts);2221}2222#endif22232224#ifdef ACCESSKIT_ENABLED2225if (accessibility_driver) {2226memdelete(accessibility_driver);2227}2228#endif22292230#ifdef DBUS_ENABLED2231if (portal_desktop) {2232memdelete(portal_desktop);2233}2234if (screensaver) {2235memdelete(screensaver);2236}2237if (atspi_monitor) {2238memdelete(atspi_monitor);2239}2240#endif2241}22422243void DisplayServerWayland::register_wayland_driver() {2244register_create_function("wayland", create_func, get_rendering_drivers_func);2245}22462247#endif //WAYLAND_ENABLED224822492250