Path: blob/master/platform/linuxbsd/wayland/display_server_wayland.cpp
10278 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 "servers/rendering/dummy/rasterizer_dummy.h"4243#ifdef VULKAN_ENABLED44#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"45#endif4647#ifdef GLES3_ENABLED48#include "detect_prime_egl.h"49#include "drivers/gles3/rasterizer_gles3.h"50#include "wayland/egl_manager_wayland.h"51#include "wayland/egl_manager_wayland_gles.h"52#endif5354#ifdef ACCESSKIT_ENABLED55#include "drivers/accesskit/accessibility_driver_accesskit.h"56#endif5758#ifdef DBUS_ENABLED59#ifdef SOWRAP_ENABLED60#include "dbus-so_wrap.h"61#else62#include <dbus/dbus.h>63#endif64#endif6566#define WAYLAND_MAX_FRAME_TIME_US (1'000'000)6768String DisplayServerWayland::_get_app_id_from_context(Context p_context) {69String app_id;7071switch (p_context) {72case CONTEXT_EDITOR: {73app_id = "org.godotengine.Editor";74} break;7576case CONTEXT_PROJECTMAN: {77app_id = "org.godotengine.ProjectManager";78} break;7980case CONTEXT_ENGINE:81default: {82String config_name = GLOBAL_GET("application/config/name");83if (config_name.length() != 0) {84app_id = config_name;85} else {86app_id = "org.godotengine.Godot";87}88}89}9091return app_id;92}9394void DisplayServerWayland::_send_window_event(WindowEvent p_event, WindowID p_window_id) {95ERR_FAIL_COND(!windows.has(p_window_id));9697WindowData &wd = windows[p_window_id];9899if (wd.window_event_callback.is_valid()) {100Variant event = int(p_event);101wd.window_event_callback.call(event);102}103}104105void DisplayServerWayland::dispatch_input_events(const Ref<InputEvent> &p_event) {106static_cast<DisplayServerWayland *>(get_singleton())->_dispatch_input_event(p_event);107}108109void DisplayServerWayland::_dispatch_input_event(const Ref<InputEvent> &p_event) {110Ref<InputEventFromWindow> event_from_window = p_event;111112if (event_from_window.is_valid()) {113WindowID window_id = event_from_window->get_window_id();114115Ref<InputEventKey> key_event = p_event;116if (!popup_menu_list.is_empty() && key_event.is_valid()) {117// Redirect to the highest popup menu.118window_id = popup_menu_list.back()->get();119}120121// Send to a single window.122if (windows.has(window_id)) {123Callable callable = windows[window_id].input_event_callback;124if (callable.is_valid()) {125callable.call(p_event);126}127}128} else {129// Send to all windows. Copy all pending callbacks, since callback can erase window.130Vector<Callable> cbs;131for (KeyValue<WindowID, WindowData> &E : windows) {132Callable callable = E.value.input_event_callback;133if (callable.is_valid()) {134cbs.push_back(callable);135}136}137138for (const Callable &cb : cbs) {139cb.call(p_event);140}141}142}143144void DisplayServerWayland::_update_window_rect(const Rect2i &p_rect, WindowID p_window_id) {145ERR_FAIL_COND(!windows.has(p_window_id));146147WindowData &wd = windows[p_window_id];148149if (wd.rect == p_rect) {150return;151}152153wd.rect = p_rect;154155#ifdef RD_ENABLED156if (wd.visible && rendering_context) {157rendering_context->window_set_size(p_window_id, wd.rect.size.width, wd.rect.size.height);158}159#endif160161#ifdef GLES3_ENABLED162if (wd.visible && egl_manager) {163wl_egl_window_resize(wd.wl_egl_window, wd.rect.size.width, wd.rect.size.height, 0, 0);164}165#endif166167if (wd.rect_changed_callback.is_valid()) {168wd.rect_changed_callback.call(wd.rect);169}170}171172// Interface methods.173174bool DisplayServerWayland::has_feature(Feature p_feature) const {175switch (p_feature) {176#ifndef DISABLE_DEPRECATED177case FEATURE_GLOBAL_MENU: {178return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));179} break;180#endif181case FEATURE_MOUSE:182case FEATURE_MOUSE_WARP:183case FEATURE_CLIPBOARD:184case FEATURE_CURSOR_SHAPE:185case FEATURE_CUSTOM_CURSOR_SHAPE:186case FEATURE_WINDOW_TRANSPARENCY:187case FEATURE_HIDPI:188case FEATURE_SWAP_BUFFERS:189case FEATURE_KEEP_SCREEN_ON:190case FEATURE_IME:191case FEATURE_WINDOW_DRAG:192case FEATURE_CLIPBOARD_PRIMARY:193case FEATURE_SUBWINDOWS:194case FEATURE_SELF_FITTING_WINDOWS: {195return true;196} break;197198//case FEATURE_NATIVE_DIALOG:199//case FEATURE_NATIVE_DIALOG_INPUT:200#ifdef DBUS_ENABLED201case FEATURE_NATIVE_DIALOG_FILE:202case FEATURE_NATIVE_DIALOG_FILE_EXTRA:203case FEATURE_NATIVE_DIALOG_FILE_MIME: {204return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_file_chooser_supported());205} break;206case FEATURE_NATIVE_COLOR_PICKER: {207return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_screenshot_supported());208} break;209#endif210211#ifdef SPEECHD_ENABLED212case FEATURE_TEXT_TO_SPEECH: {213return true;214} break;215#endif216217#ifdef ACCESSKIT_ENABLED218case FEATURE_ACCESSIBILITY_SCREEN_READER: {219return (accessibility_driver != nullptr);220} break;221#endif222223default: {224return false;225}226}227}228229String DisplayServerWayland::get_name() const {230return "Wayland";231}232233#ifdef SPEECHD_ENABLED234235void DisplayServerWayland::initialize_tts() const {236const_cast<DisplayServerWayland *>(this)->tts = memnew(TTS_Linux);237}238239bool DisplayServerWayland::tts_is_speaking() const {240if (unlikely(!tts)) {241initialize_tts();242}243ERR_FAIL_NULL_V(tts, false);244return tts->is_speaking();245}246247bool DisplayServerWayland::tts_is_paused() const {248if (unlikely(!tts)) {249initialize_tts();250}251ERR_FAIL_NULL_V(tts, false);252return tts->is_paused();253}254255TypedArray<Dictionary> DisplayServerWayland::tts_get_voices() const {256if (unlikely(!tts)) {257initialize_tts();258}259ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());260return tts->get_voices();261}262263void 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) {264if (unlikely(!tts)) {265initialize_tts();266}267ERR_FAIL_NULL(tts);268tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);269}270271void DisplayServerWayland::tts_pause() {272if (unlikely(!tts)) {273initialize_tts();274}275ERR_FAIL_NULL(tts);276tts->pause();277}278279void DisplayServerWayland::tts_resume() {280if (unlikely(!tts)) {281initialize_tts();282}283ERR_FAIL_NULL(tts);284tts->resume();285}286287void DisplayServerWayland::tts_stop() {288if (unlikely(!tts)) {289initialize_tts();290}291ERR_FAIL_NULL(tts);292tts->stop();293}294295#endif296297#ifdef DBUS_ENABLED298299bool DisplayServerWayland::is_dark_mode_supported() const {300return portal_desktop && portal_desktop->is_supported() && portal_desktop->is_settings_supported();301}302303bool DisplayServerWayland::is_dark_mode() const {304if (!is_dark_mode_supported()) {305return false;306}307switch (portal_desktop->get_appearance_color_scheme()) {308case 1:309// Prefers dark theme.310return true;311case 2:312// Prefers light theme.313return false;314default:315// Preference unknown.316return false;317}318}319320Color DisplayServerWayland::get_accent_color() const {321if (!portal_desktop) {322return Color();323}324return portal_desktop->get_appearance_accent_color();325}326327void DisplayServerWayland::set_system_theme_change_callback(const Callable &p_callable) {328ERR_FAIL_COND(!portal_desktop);329portal_desktop->set_system_theme_change_callback(p_callable);330}331332Error 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) {333ERR_FAIL_COND_V(!portal_desktop, ERR_UNAVAILABLE);334MutexLock mutex_lock(wayland_thread.mutex);335336WindowID window_id = p_window_id;337if (!windows.has(window_id) || window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, window_id)) {338window_id = MAIN_WINDOW_ID;339}340341WaylandThread::WindowState *ws = wayland_thread.window_get_state(window_id);342ERR_FAIL_NULL_V(ws, ERR_BUG);343344return 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);345}346347Error 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) {348ERR_FAIL_COND_V(!portal_desktop, ERR_UNAVAILABLE);349MutexLock mutex_lock(wayland_thread.mutex);350351WindowID window_id = p_window_id;352if (!windows.has(window_id) || window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, window_id)) {353window_id = MAIN_WINDOW_ID;354}355356WaylandThread::WindowState *ws = wayland_thread.window_get_state(window_id);357ERR_FAIL_NULL_V(ws, ERR_BUG);358359return 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);360}361362#endif363364void DisplayServerWayland::beep() const {365wayland_thread.beep();366}367368void DisplayServerWayland::_mouse_update_mode() {369MouseMode wanted_mouse_mode = mouse_mode_override_enabled370? mouse_mode_override371: mouse_mode_base;372373if (wanted_mouse_mode == mouse_mode) {374return;375}376377MutexLock mutex_lock(wayland_thread.mutex);378379bool show_cursor = (wanted_mouse_mode == MOUSE_MODE_VISIBLE || wanted_mouse_mode == MOUSE_MODE_CONFINED);380381wayland_thread.cursor_set_visible(show_cursor);382383WaylandThread::PointerConstraint constraint = WaylandThread::PointerConstraint::NONE;384385switch (wanted_mouse_mode) {386case DisplayServer::MOUSE_MODE_CAPTURED: {387constraint = WaylandThread::PointerConstraint::LOCKED;388} break;389390case DisplayServer::MOUSE_MODE_CONFINED:391case DisplayServer::MOUSE_MODE_CONFINED_HIDDEN: {392constraint = WaylandThread::PointerConstraint::CONFINED;393} break;394395default: {396}397}398399wayland_thread.pointer_set_constraint(constraint);400401if (wanted_mouse_mode == DisplayServer::MOUSE_MODE_CAPTURED) {402WindowData *pointed_win = windows.getptr(wayland_thread.pointer_get_pointed_window_id());403ERR_FAIL_NULL(pointed_win);404wayland_thread.pointer_set_hint(pointed_win->rect.size / 2);405}406407mouse_mode = wanted_mouse_mode;408}409410void DisplayServerWayland::mouse_set_mode(MouseMode p_mode) {411ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);412if (p_mode == mouse_mode_base) {413return;414}415mouse_mode_base = p_mode;416_mouse_update_mode();417}418419DisplayServerWayland::MouseMode DisplayServerWayland::mouse_get_mode() const {420return mouse_mode;421}422423void DisplayServerWayland::mouse_set_mode_override(MouseMode p_mode) {424ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);425if (p_mode == mouse_mode_override) {426return;427}428mouse_mode_override = p_mode;429_mouse_update_mode();430}431432DisplayServerWayland::MouseMode DisplayServerWayland::mouse_get_mode_override() const {433return mouse_mode_override;434}435436void DisplayServerWayland::mouse_set_mode_override_enabled(bool p_override_enabled) {437if (p_override_enabled == mouse_mode_override_enabled) {438return;439}440mouse_mode_override_enabled = p_override_enabled;441_mouse_update_mode();442}443444bool DisplayServerWayland::mouse_is_mode_override_enabled() const {445return mouse_mode_override_enabled;446}447448// NOTE: This is hacked together (and not guaranteed to work in the first place)449// as for some reason the there's no proper way to ask the compositor to warp450// the pointer, although, at the time of writing, there's a proposal for a451// proper protocol for this. See:452// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/158453void DisplayServerWayland::warp_mouse(const Point2i &p_to) {454MutexLock mutex_lock(wayland_thread.mutex);455456WaylandThread::PointerConstraint old_constraint = wayland_thread.pointer_get_constraint();457458wayland_thread.pointer_set_constraint(WaylandThread::PointerConstraint::LOCKED);459wayland_thread.pointer_set_hint(p_to);460461wayland_thread.pointer_set_constraint(old_constraint);462}463464Point2i DisplayServerWayland::mouse_get_position() const {465MutexLock mutex_lock(wayland_thread.mutex);466467WindowID pointed_id = wayland_thread.pointer_get_pointed_window_id();468469if (pointed_id != INVALID_WINDOW_ID && windows.has(pointed_id)) {470return Input::get_singleton()->get_mouse_position() + windows[pointed_id].rect.position;471}472473// We can't properly implement this method by design.474// This is the best we can do unfortunately.475return Input::get_singleton()->get_mouse_position();476}477478BitField<MouseButtonMask> DisplayServerWayland::mouse_get_button_state() const {479MutexLock mutex_lock(wayland_thread.mutex);480481// Are we sure this is the only way? This seems sus.482// TODO: Handle tablets properly.483//mouse_button_mask.set_flag(MouseButtonMask((int64_t)wls.current_seat->tablet_tool_data.pressed_button_mask));484485return wayland_thread.pointer_get_button_mask();486}487488// NOTE: According to the Wayland specification, this method will only do489// anything if the user has interacted with the application by sending a490// "recent enough" input event.491// TODO: Add this limitation to the documentation.492void DisplayServerWayland::clipboard_set(const String &p_text) {493MutexLock mutex_lock(wayland_thread.mutex);494495wayland_thread.selection_set_text(p_text);496}497498String DisplayServerWayland::clipboard_get() const {499MutexLock mutex_lock(wayland_thread.mutex);500501Vector<uint8_t> data;502503const String text_mimes[] = {504"text/plain;charset=utf-8",505"text/plain",506};507508for (String mime : text_mimes) {509if (wayland_thread.selection_has_mime(mime)) {510print_verbose(vformat("Selecting media type \"%s\" from offered types.", mime));511data = wayland_thread.selection_get_mime(mime);512break;513}514}515516return String::utf8((const char *)data.ptr(), data.size());517}518519Ref<Image> DisplayServerWayland::clipboard_get_image() const {520MutexLock mutex_lock(wayland_thread.mutex);521522Ref<Image> image;523image.instantiate();524525Error err = OK;526527// TODO: Fallback to next media type on missing module or parse error.528if (wayland_thread.selection_has_mime("image/png")) {529err = image->load_png_from_buffer(wayland_thread.selection_get_mime("image/png"));530} else if (wayland_thread.selection_has_mime("image/jpeg")) {531err = image->load_jpg_from_buffer(wayland_thread.selection_get_mime("image/jpeg"));532} else if (wayland_thread.selection_has_mime("image/webp")) {533err = image->load_webp_from_buffer(wayland_thread.selection_get_mime("image/webp"));534} else if (wayland_thread.selection_has_mime("image/svg+xml")) {535err = image->load_svg_from_buffer(wayland_thread.selection_get_mime("image/svg+xml"));536} else if (wayland_thread.selection_has_mime("image/bmp")) {537err = image->load_bmp_from_buffer(wayland_thread.selection_get_mime("image/bmp"));538} else if (wayland_thread.selection_has_mime("image/x-tga")) {539err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-tga"));540} else if (wayland_thread.selection_has_mime("image/x-targa")) {541err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-targa"));542} else if (wayland_thread.selection_has_mime("image/ktx")) {543err = image->load_ktx_from_buffer(wayland_thread.selection_get_mime("image/ktx"));544}545546ERR_FAIL_COND_V(err != OK, Ref<Image>());547548return image;549}550551void DisplayServerWayland::clipboard_set_primary(const String &p_text) {552MutexLock mutex_lock(wayland_thread.mutex);553554wayland_thread.primary_set_text(p_text);555}556557String DisplayServerWayland::clipboard_get_primary() const {558MutexLock mutex_lock(wayland_thread.mutex);559560Vector<uint8_t> data;561562const String text_mimes[] = {563"text/plain;charset=utf-8",564"text/plain",565};566567for (String mime : text_mimes) {568if (wayland_thread.primary_has_mime(mime)) {569print_verbose(vformat("Selecting media type \"%s\" from offered types.", mime));570data = wayland_thread.primary_get_mime(mime);571break;572}573}574575return String::utf8((const char *)data.ptr(), data.size());576}577578int DisplayServerWayland::get_screen_count() const {579MutexLock mutex_lock(wayland_thread.mutex);580return wayland_thread.get_screen_count();581}582583int DisplayServerWayland::get_primary_screen() const {584// AFAIK Wayland doesn't allow knowing (nor we care) about which screen is585// primary.586return 0;587}588589Point2i DisplayServerWayland::screen_get_position(int p_screen) const {590MutexLock mutex_lock(wayland_thread.mutex);591592p_screen = _get_screen_index(p_screen);593int screen_count = get_screen_count();594ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());595596return wayland_thread.screen_get_data(p_screen).position;597}598599Size2i DisplayServerWayland::screen_get_size(int p_screen) const {600MutexLock mutex_lock(wayland_thread.mutex);601602p_screen = _get_screen_index(p_screen);603int screen_count = get_screen_count();604ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());605606return wayland_thread.screen_get_data(p_screen).size;607}608609Rect2i DisplayServerWayland::screen_get_usable_rect(int p_screen) const {610p_screen = _get_screen_index(p_screen);611int screen_count = get_screen_count();612ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());613614return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));615}616617int DisplayServerWayland::screen_get_dpi(int p_screen) const {618MutexLock mutex_lock(wayland_thread.mutex);619620p_screen = _get_screen_index(p_screen);621int screen_count = get_screen_count();622ERR_FAIL_INDEX_V(p_screen, screen_count, 96);623624const WaylandThread::ScreenData &data = wayland_thread.screen_get_data(p_screen);625626int width_mm = data.physical_size.width;627int height_mm = data.physical_size.height;628629double xdpi = (width_mm ? data.size.width / (double)width_mm * 25.4 : 0);630double ydpi = (height_mm ? data.size.height / (double)height_mm * 25.4 : 0);631632if (xdpi || ydpi) {633return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1);634}635636// Could not get DPI.637return 96;638}639640float DisplayServerWayland::screen_get_scale(int p_screen) const {641MutexLock mutex_lock(wayland_thread.mutex);642643if (p_screen == SCREEN_OF_MAIN_WINDOW) {644// Wayland does not expose fractional scale factors at the screen-level, but645// some code relies on it. Since this special screen is the default and a lot646// of code relies on it, we'll return the window's scale, which is what we647// really care about. After all, we have very little use of the actual screen648// enumeration APIs and we're (for now) in single-window mode anyways.649struct wl_surface *wl_surface = wayland_thread.window_get_wl_surface(MAIN_WINDOW_ID);650WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wl_surface);651652return wayland_thread.window_state_get_scale_factor(ws);653}654655p_screen = _get_screen_index(p_screen);656int screen_count = get_screen_count();657ERR_FAIL_INDEX_V(p_screen, screen_count, 1.0f);658659return wayland_thread.screen_get_data(p_screen).scale;660}661662float DisplayServerWayland::screen_get_refresh_rate(int p_screen) const {663MutexLock mutex_lock(wayland_thread.mutex);664665p_screen = _get_screen_index(p_screen);666int screen_count = get_screen_count();667ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);668669return wayland_thread.screen_get_data(p_screen).refresh_rate;670}671672void DisplayServerWayland::screen_set_keep_on(bool p_enable) {673MutexLock mutex_lock(wayland_thread.mutex);674675// FIXME: For some reason this does not also windows from the wayland thread.676677if (screen_is_kept_on() == p_enable) {678return;679}680681#ifdef DBUS_ENABLED682if (screensaver) {683if (p_enable) {684screensaver->inhibit();685} else {686screensaver->uninhibit();687}688689screensaver_inhibited = p_enable;690}691#endif692}693694bool DisplayServerWayland::screen_is_kept_on() const {695// FIXME: Multiwindow support.696#ifdef DBUS_ENABLED697return wayland_thread.window_get_idle_inhibition(MAIN_WINDOW_ID) || screensaver_inhibited;698#else699return wayland_thread.window_get_idle_inhibition(MAIN_WINDOW_ID);700#endif701}702703Vector<DisplayServer::WindowID> DisplayServerWayland::get_window_list() const {704MutexLock mutex_lock(wayland_thread.mutex);705706Vector<int> ret;707for (const KeyValue<WindowID, WindowData> &E : windows) {708ret.push_back(E.key);709}710return ret;711}712713DisplayServer::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) {714WindowID id = ++window_id_counter;715WindowData &wd = windows[id];716717wd.id = id;718wd.mode = p_mode;719wd.flags = p_flags;720wd.vsync_mode = p_vsync_mode;721722#ifdef ACCESSKIT_ENABLED723if (accessibility_driver && !accessibility_driver->window_create(wd.id, nullptr)) {724if (OS::get_singleton()->is_stdout_verbose()) {725ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");726}727memdelete(accessibility_driver);728accessibility_driver = nullptr;729}730#endif731732// NOTE: Remember to clear its position if this window will be a toplevel. We733// can only know once we show it.734wd.rect = p_rect;735736wd.title = "Godot";737wd.parent_id = p_transient_parent;738return id;739}740741void DisplayServerWayland::show_window(WindowID p_window_id) {742MutexLock mutex_lock(wayland_thread.mutex);743744ERR_FAIL_COND(!windows.has(p_window_id));745746WindowData &wd = windows[p_window_id];747748if (!wd.visible) {749DEBUG_LOG_WAYLAND(vformat("Showing window %d", p_window_id));750// Showing this window will reset its mode with whatever the compositor751// reports. We'll save the mode beforehand so that we can reapply it later.752// TODO: Fix/Port/Move/Whatever to `WaylandThread` APIs.753WindowMode setup_mode = wd.mode;754755// Let's determine the closest toplevel. For toplevels it will be themselves,756// for popups the first toplevel ancestor it finds.757WindowID root_id = wd.id;758while (root_id != INVALID_WINDOW_ID && window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, root_id)) {759root_id = windows[root_id].parent_id;760}761ERR_FAIL_COND(root_id == INVALID_WINDOW_ID);762763wd.root_id = root_id;764765if (!window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, p_window_id)) {766// NOTE: DO **NOT** KEEP THE POSITION SET FOR TOPLEVELS. Wayland does not767// track them and we're gonna get our events transformed in unexpected ways.768wd.rect.position = Point2i();769770DEBUG_LOG_WAYLAND(vformat("Creating regular window of size %s", wd.rect.size));771wayland_thread.window_create(p_window_id, wd.rect.size.width, wd.rect.size.height);772wayland_thread.window_set_min_size(p_window_id, wd.min_size);773wayland_thread.window_set_max_size(p_window_id, wd.max_size);774wayland_thread.window_set_app_id(p_window_id, _get_app_id_from_context(context));775wayland_thread.window_set_borderless(p_window_id, window_get_flag(WINDOW_FLAG_BORDERLESS, p_window_id));776777if (wd.parent_id != INVALID_WINDOW_ID) {778wayland_thread.window_set_parent(wd.id, wd.parent_id);779}780781// Since it can't have a position. Let's tell the window node the news by782// the actual rect to it.783if (wd.rect_changed_callback.is_valid()) {784wd.rect_changed_callback.call(wd.rect);785}786} else {787DEBUG_LOG_WAYLAND("!!!!! Making popup !!!!!");788789windows[root_id].popup_stack.push_back(p_window_id);790791if (window_get_flag(WINDOW_FLAG_POPUP, p_window_id)) {792// Reroutes all input to it.793popup_menu_list.push_back(p_window_id);794}795796wayland_thread.window_create_popup(p_window_id, wd.parent_id, wd.rect);797}798799// NOTE: The XDG shell protocol is built in a way that causes the window to800// be immediately shown as soon as a valid buffer is assigned to it. Hence,801// the only acceptable way of implementing window showing is to move the802// graphics context window creation logic here.803#ifdef RD_ENABLED804if (rendering_context) {805union {806#ifdef VULKAN_ENABLED807RenderingContextDriverVulkanWayland::WindowPlatformData vulkan;808#endif809} wpd;810#ifdef VULKAN_ENABLED811if (rendering_driver == "vulkan") {812wpd.vulkan.surface = wayland_thread.window_get_wl_surface(wd.id);813wpd.vulkan.display = wayland_thread.get_wl_display();814}815#endif816Error err = rendering_context->window_create(wd.id, &wpd);817ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s window", rendering_driver));818819rendering_context->window_set_size(wd.id, wd.rect.size.width, wd.rect.size.height);820821// NOTE: Looks like we have to set the vsync mode before creating the screen822// or it won't work. Resist any temptation.823window_set_vsync_mode(wd.vsync_mode, p_window_id);824}825826if (rendering_device) {827rendering_device->screen_create(wd.id);828}829#endif830831#ifdef GLES3_ENABLED832if (egl_manager) {833struct wl_surface *wl_surface = wayland_thread.window_get_wl_surface(wd.id);834wd.wl_egl_window = wl_egl_window_create(wl_surface, wd.rect.size.width, wd.rect.size.height);835836Error 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);837ERR_FAIL_COND_MSG(err == ERR_CANT_CREATE, "Can't show a GLES3 window.");838839window_set_vsync_mode(wd.vsync_mode, p_window_id);840}841#endif842843// NOTE: Some public window-handling methods might depend on this flag being844// set. Make sure the method you're calling does not depend on it before this845// assignment.846wd.visible = true;847848// Actually try to apply the window's mode now that it's visible.849window_set_mode(setup_mode, wd.id);850851wayland_thread.window_set_title(p_window_id, wd.title);852}853}854855void DisplayServerWayland::delete_sub_window(WindowID p_window_id) {856MutexLock mutex_lock(wayland_thread.mutex);857858ERR_FAIL_COND(!windows.has(p_window_id));859WindowData &wd = windows[p_window_id];860861ERR_FAIL_COND(!windows.has(wd.root_id));862WindowData &root_wd = windows[wd.root_id];863864// NOTE: By the time the Wayland thread will send a `WINDOW_EVENT_MOUSE_EXIT`865// the window will be gone and the message will be discarded, confusing the866// engine. We thus have to send it ourselves.867if (wayland_thread.pointer_get_pointed_window_id() == p_window_id) {868_send_window_event(WINDOW_EVENT_MOUSE_EXIT, p_window_id);869}870871// The XDG shell specification requires us to clear all popups in reverse order.872while (!root_wd.popup_stack.is_empty() && root_wd.popup_stack.back()->get() != p_window_id) {873_send_window_event(WINDOW_EVENT_FORCE_CLOSE, root_wd.popup_stack.back()->get());874}875876if (root_wd.popup_stack.back() && root_wd.popup_stack.back()->get() == p_window_id) {877root_wd.popup_stack.pop_back();878}879880if (popup_menu_list.back() && popup_menu_list.back()->get() == p_window_id) {881popup_menu_list.pop_back();882}883884#ifdef ACCESSKIT_ENABLED885if (accessibility_driver) {886accessibility_driver->window_destroy(p_window_id);887}888#endif889890if (wd.visible) {891#ifdef VULKAN_ENABLED892if (rendering_device) {893rendering_device->screen_free(p_window_id);894}895896if (rendering_context) {897rendering_context->window_destroy(p_window_id);898}899#endif900901#ifdef GLES3_ENABLED902if (egl_manager) {903egl_manager->window_destroy(p_window_id);904}905#endif906907wayland_thread.window_destroy(p_window_id);908}909910windows.erase(p_window_id);911912DEBUG_LOG_WAYLAND(vformat("Destroyed window %d", p_window_id));913}914915DisplayServer::WindowID DisplayServerWayland::window_get_active_popup() const {916MutexLock mutex_lock(wayland_thread.mutex);917918if (!popup_menu_list.is_empty()) {919return popup_menu_list.back()->get();920}921922return INVALID_WINDOW_ID;923}924925void DisplayServerWayland::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {926MutexLock mutex_lock(wayland_thread.mutex);927928ERR_FAIL_COND(!windows.has(p_window));929930windows[p_window].safe_rect = p_rect;931}932933Rect2i DisplayServerWayland::window_get_popup_safe_rect(WindowID p_window) const {934MutexLock mutex_lock(wayland_thread.mutex);935936ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());937938return windows[p_window].safe_rect;939}940941int64_t DisplayServerWayland::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {942MutexLock mutex_lock(wayland_thread.mutex);943944switch (p_handle_type) {945case DISPLAY_HANDLE: {946return (int64_t)wayland_thread.get_wl_display();947} break;948949case WINDOW_HANDLE: {950return (int64_t)wayland_thread.window_get_wl_surface(p_window);951} break;952953case WINDOW_VIEW: {954return 0; // Not supported.955} break;956957#ifdef GLES3_ENABLED958case OPENGL_CONTEXT: {959if (egl_manager) {960return (int64_t)egl_manager->get_context(p_window);961}962return 0;963} break;964case EGL_DISPLAY: {965if (egl_manager) {966return (int64_t)egl_manager->get_display(p_window);967}968return 0;969}970case EGL_CONFIG: {971if (egl_manager) {972return (int64_t)egl_manager->get_config(p_window);973}974return 0;975}976#endif // GLES3_ENABLED977978default: {979return 0;980} break;981}982}983984DisplayServer::WindowID DisplayServerWayland::get_window_at_screen_position(const Point2i &p_position) const {985// Standard Wayland APIs don't support this.986return MAIN_WINDOW_ID;987}988989void DisplayServerWayland::window_attach_instance_id(ObjectID p_instance, WindowID p_window_id) {990MutexLock mutex_lock(wayland_thread.mutex);991992ERR_FAIL_COND(!windows.has(p_window_id));993994windows[p_window_id].instance_id = p_instance;995}996997ObjectID DisplayServerWayland::window_get_attached_instance_id(WindowID p_window_id) const {998MutexLock mutex_lock(wayland_thread.mutex);9991000ERR_FAIL_COND_V(!windows.has(p_window_id), ObjectID());10011002return windows[p_window_id].instance_id;1003}10041005void DisplayServerWayland::window_set_title(const String &p_title, DisplayServer::WindowID p_window_id) {1006MutexLock mutex_lock(wayland_thread.mutex);10071008ERR_FAIL_COND(!windows.has(p_window_id));10091010WindowData &wd = windows[p_window_id];10111012wd.title = p_title;10131014if (wd.visible) {1015wayland_thread.window_set_title(p_window_id, wd.title);1016}1017}10181019void DisplayServerWayland::window_set_mouse_passthrough(const Vector<Vector2> &p_region, DisplayServer::WindowID p_window_id) {1020// TODO1021DEBUG_LOG_WAYLAND(vformat("wayland stub window_set_mouse_passthrough region %s", p_region));1022}10231024void DisplayServerWayland::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1025MutexLock mutex_lock(wayland_thread.mutex);10261027ERR_FAIL_COND(!windows.has(p_window_id));10281029windows[p_window_id].rect_changed_callback = p_callable;1030}10311032void DisplayServerWayland::window_set_window_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1033MutexLock mutex_lock(wayland_thread.mutex);10341035ERR_FAIL_COND(!windows.has(p_window_id));10361037windows[p_window_id].window_event_callback = p_callable;1038}10391040void DisplayServerWayland::window_set_input_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1041MutexLock mutex_lock(wayland_thread.mutex);10421043ERR_FAIL_COND(!windows.has(p_window_id));10441045windows[p_window_id].input_event_callback = p_callable;1046}10471048void DisplayServerWayland::window_set_input_text_callback(const Callable &p_callable, WindowID p_window_id) {1049MutexLock mutex_lock(wayland_thread.mutex);10501051ERR_FAIL_COND(!windows.has(p_window_id));10521053windows[p_window_id].input_text_callback = p_callable;1054}10551056void DisplayServerWayland::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {1057MutexLock mutex_lock(wayland_thread.mutex);10581059ERR_FAIL_COND(!windows.has(p_window_id));10601061windows[p_window_id].drop_files_callback = p_callable;1062}10631064int DisplayServerWayland::window_get_current_screen(DisplayServer::WindowID p_window_id) const {1065ERR_FAIL_COND_V(!windows.has(p_window_id), INVALID_SCREEN);1066// Standard Wayland APIs don't support getting the screen of a window.1067return 0;1068}10691070void DisplayServerWayland::window_set_current_screen(int p_screen, DisplayServer::WindowID p_window_id) {1071// Standard Wayland APIs don't support setting the screen of a window.1072}10731074Point2i DisplayServerWayland::window_get_position(DisplayServer::WindowID p_window_id) const {1075MutexLock mutex_lock(wayland_thread.mutex);10761077return windows[p_window_id].rect.position;1078}10791080Point2i DisplayServerWayland::window_get_position_with_decorations(DisplayServer::WindowID p_window_id) const {1081MutexLock mutex_lock(wayland_thread.mutex);10821083return windows[p_window_id].rect.position;1084}10851086void DisplayServerWayland::window_set_position(const Point2i &p_position, DisplayServer::WindowID p_window_id) {1087// Unsupported with toplevels.1088}10891090void DisplayServerWayland::window_set_max_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1091MutexLock mutex_lock(wayland_thread.mutex);10921093DEBUG_LOG_WAYLAND(vformat("window max size set to %s", p_size));10941095if (p_size.x < 0 || p_size.y < 0) {1096ERR_FAIL_MSG("Maximum window size can't be negative!");1097}10981099ERR_FAIL_COND(!windows.has(p_window_id));1100WindowData &wd = windows[p_window_id];11011102// FIXME: Is `p_size.x < wd.min_size.x || p_size.y < wd.min_size.y` == `p_size < wd.min_size`?1103if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {1104ERR_PRINT("Maximum window size can't be smaller than minimum window size!");1105return;1106}11071108wd.max_size = p_size;11091110if (wd.visible) {1111wayland_thread.window_set_max_size(p_window_id, p_size);1112}1113}11141115Size2i DisplayServerWayland::window_get_max_size(DisplayServer::WindowID p_window_id) const {1116MutexLock mutex_lock(wayland_thread.mutex);11171118ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1119return windows[p_window_id].max_size;1120}11211122void DisplayServerWayland::gl_window_make_current(DisplayServer::WindowID p_window_id) {1123#ifdef GLES3_ENABLED1124if (egl_manager) {1125egl_manager->window_make_current(p_window_id);1126}1127#endif1128}11291130void DisplayServerWayland::window_set_transient(WindowID p_window_id, WindowID p_parent) {1131MutexLock mutex_lock(wayland_thread.mutex);11321133ERR_FAIL_COND(!windows.has(p_window_id));1134WindowData &wd = windows[p_window_id];11351136ERR_FAIL_COND(wd.parent_id == p_parent);11371138if (p_parent != INVALID_WINDOW_ID) {1139ERR_FAIL_COND(!windows.has(p_parent));1140ERR_FAIL_COND_MSG(wd.parent_id != INVALID_WINDOW_ID, "Window already has a transient parent");1141wd.parent_id = p_parent;11421143// NOTE: Looks like live unparenting is not really practical unfortunately.1144// See WaylandThread::window_set_parent for more info.1145if (wd.visible) {1146wayland_thread.window_set_parent(p_window_id, p_parent);1147}1148}1149}11501151void DisplayServerWayland::window_set_min_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1152MutexLock mutex_lock(wayland_thread.mutex);11531154DEBUG_LOG_WAYLAND(vformat("window minsize set to %s", p_size));11551156ERR_FAIL_COND(!windows.has(p_window_id));1157WindowData &wd = windows[p_window_id];11581159if (p_size.x < 0 || p_size.y < 0) {1160ERR_FAIL_MSG("Minimum window size can't be negative!");1161}11621163// FIXME: Is `p_size.x > wd.max_size.x || p_size.y > wd.max_size.y` == `p_size > wd.max_size`?1164if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {1165ERR_PRINT("Minimum window size can't be larger than maximum window size!");1166return;1167}11681169wd.min_size = p_size;11701171if (wd.visible) {1172wayland_thread.window_set_min_size(p_window_id, p_size);1173}1174}11751176Size2i DisplayServerWayland::window_get_min_size(DisplayServer::WindowID p_window_id) const {1177MutexLock mutex_lock(wayland_thread.mutex);11781179ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1180return windows[p_window_id].min_size;1181}11821183void DisplayServerWayland::window_set_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {1184MutexLock mutex_lock(wayland_thread.mutex);11851186ERR_FAIL_COND(!windows.has(p_window_id));1187WindowData &wd = windows[p_window_id];11881189// The XDG spec doesn't allow non-interactive resizes. Let's update the1190// window's internal representation to account for that.1191if (wd.rect_changed_callback.is_valid()) {1192wd.rect_changed_callback.call(wd.rect);1193}1194}11951196Size2i DisplayServerWayland::window_get_size(DisplayServer::WindowID p_window_id) const {1197MutexLock mutex_lock(wayland_thread.mutex);11981199ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1200return windows[p_window_id].rect.size;1201}12021203Size2i DisplayServerWayland::window_get_size_with_decorations(DisplayServer::WindowID p_window_id) const {1204MutexLock mutex_lock(wayland_thread.mutex);12051206// I don't think there's a way of actually knowing the size of the window1207// decoration in Wayland, at least in the case of SSDs, nor that it would be1208// that useful in this case. We'll just return the main window's size.1209ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());1210return windows[p_window_id].rect.size;1211}12121213void DisplayServerWayland::window_set_mode(WindowMode p_mode, DisplayServer::WindowID p_window_id) {1214MutexLock mutex_lock(wayland_thread.mutex);12151216ERR_FAIL_COND(!windows.has(p_window_id));1217WindowData &wd = windows[p_window_id];12181219if (!wd.visible) {1220return;1221}12221223wayland_thread.window_try_set_mode(p_window_id, p_mode);1224}12251226DisplayServer::WindowMode DisplayServerWayland::window_get_mode(DisplayServer::WindowID p_window_id) const {1227MutexLock mutex_lock(wayland_thread.mutex);12281229ERR_FAIL_COND_V(!windows.has(p_window_id), WINDOW_MODE_WINDOWED);1230const WindowData &wd = windows[p_window_id];12311232if (!wd.visible) {1233return WINDOW_MODE_WINDOWED;1234}12351236return wayland_thread.window_get_mode(p_window_id);1237}12381239bool DisplayServerWayland::window_is_maximize_allowed(DisplayServer::WindowID p_window_id) const {1240MutexLock mutex_lock(wayland_thread.mutex);12411242return wayland_thread.window_can_set_mode(p_window_id, WINDOW_MODE_MAXIMIZED);1243}12441245void DisplayServerWayland::window_set_flag(WindowFlags p_flag, bool p_enabled, DisplayServer::WindowID p_window_id) {1246MutexLock mutex_lock(wayland_thread.mutex);12471248ERR_FAIL_COND(!windows.has(p_window_id));1249WindowData &wd = windows[p_window_id];12501251DEBUG_LOG_WAYLAND(vformat("Window set flag %d", p_flag));12521253switch (p_flag) {1254case WINDOW_FLAG_BORDERLESS: {1255wayland_thread.window_set_borderless(p_window_id, p_enabled);1256} break;12571258case WINDOW_FLAG_POPUP: {1259ERR_FAIL_COND_MSG(p_window_id == MAIN_WINDOW_ID, "Main window can't be popup.");1260ERR_FAIL_COND_MSG(wd.visible && (wd.flags & WINDOW_FLAG_POPUP_BIT) != p_enabled, "Popup flag can't changed while window is opened.");1261} break;12621263case WINDOW_FLAG_POPUP_WM_HINT: {1264ERR_FAIL_COND_MSG(p_window_id == MAIN_WINDOW_ID, "Main window can't have popup hint.");1265ERR_FAIL_COND_MSG(wd.visible && (wd.flags & WINDOW_FLAG_POPUP_WM_HINT_BIT) != p_enabled, "Popup hint can't changed while window is opened.");1266} break;12671268default: {1269}1270}12711272if (p_enabled) {1273wd.flags |= 1 << p_flag;1274} else {1275wd.flags &= ~(1 << p_flag);1276}1277}12781279bool DisplayServerWayland::window_get_flag(WindowFlags p_flag, DisplayServer::WindowID p_window_id) const {1280MutexLock mutex_lock(wayland_thread.mutex);12811282ERR_FAIL_COND_V(!windows.has(p_window_id), false);1283return windows[p_window_id].flags & (1 << p_flag);1284}12851286void DisplayServerWayland::window_request_attention(DisplayServer::WindowID p_window_id) {1287MutexLock mutex_lock(wayland_thread.mutex);12881289DEBUG_LOG_WAYLAND("Requested attention.");12901291wayland_thread.window_request_attention(p_window_id);1292}12931294void DisplayServerWayland::window_move_to_foreground(DisplayServer::WindowID p_window_id) {1295// Standard Wayland APIs don't support this.1296}12971298bool DisplayServerWayland::window_is_focused(WindowID p_window_id) const {1299return wayland_thread.pointer_get_pointed_window_id() == p_window_id;1300}13011302bool DisplayServerWayland::window_can_draw(DisplayServer::WindowID p_window_id) const {1303MutexLock mutex_lock(wayland_thread.mutex);13041305uint64_t last_frame_time = wayland_thread.window_get_last_frame_time(p_window_id);1306uint64_t time_since_frame = OS::get_singleton()->get_ticks_usec() - last_frame_time;13071308if (time_since_frame > WAYLAND_MAX_FRAME_TIME_US) {1309return false;1310}13111312if (wayland_thread.window_is_suspended(p_window_id)) {1313return false;1314}13151316return suspend_state == SuspendState::NONE;1317}13181319bool DisplayServerWayland::can_any_window_draw() const {1320return suspend_state == SuspendState::NONE;1321}13221323void DisplayServerWayland::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) {1324MutexLock mutex_lock(wayland_thread.mutex);13251326wayland_thread.window_set_ime_active(p_active, p_window_id);1327}13281329void DisplayServerWayland::window_set_ime_position(const Point2i &p_pos, DisplayServer::WindowID p_window_id) {1330MutexLock mutex_lock(wayland_thread.mutex);13311332wayland_thread.window_set_ime_position(p_pos, p_window_id);1333}13341335int DisplayServerWayland::accessibility_should_increase_contrast() const {1336#ifdef DBUS_ENABLED1337if (!portal_desktop) {1338return -1;1339}1340return portal_desktop->get_high_contrast();1341#endif1342return -1;1343}13441345int DisplayServerWayland::accessibility_screen_reader_active() const {1346#ifdef DBUS_ENABLED1347if (atspi_monitor && atspi_monitor->is_supported()) {1348return atspi_monitor->is_active();1349}1350#endif1351return -1;1352}13531354Point2i DisplayServerWayland::ime_get_selection() const {1355return ime_selection;1356}13571358String DisplayServerWayland::ime_get_text() const {1359return ime_text;1360}13611362// NOTE: While Wayland is supposed to be tear-free, wayland-protocols version1363// 1.30 added a protocol for allowing async flips which is supposed to be1364// handled by drivers such as Vulkan. We can then just ask to disable v-sync and1365// hope for the best. See: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/commit/6394f0b4f3be151076f10a845a2fb131eeb567061366void DisplayServerWayland::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, DisplayServer::WindowID p_window_id) {1367MutexLock mutex_lock(wayland_thread.mutex);13681369WindowData &wd = windows[p_window_id];13701371#ifdef RD_ENABLED1372if (rendering_context) {1373rendering_context->window_set_vsync_mode(p_window_id, p_vsync_mode);13741375wd.emulate_vsync = (!wayland_thread.is_fifo_available() && rendering_context->window_get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED);13761377if (wd.emulate_vsync) {1378print_verbose("VSYNC: manually throttling frames using MAILBOX.");1379rendering_context->window_set_vsync_mode(p_window_id, DisplayServer::VSYNC_MAILBOX);1380}1381}1382#endif // VULKAN_ENABLED13831384#ifdef GLES3_ENABLED1385if (egl_manager) {1386egl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);13871388// NOTE: Mesa's EGL implementation does not seem to make use of fifo_v1 so1389// we'll have to always emulate V-Sync.1390wd.emulate_vsync = egl_manager->is_using_vsync();13911392if (wd.emulate_vsync) {1393print_verbose("VSYNC: manually throttling frames with swap delay 0.");1394egl_manager->set_use_vsync(false);1395}1396}1397#endif // GLES3_ENABLED1398}13991400DisplayServer::VSyncMode DisplayServerWayland::window_get_vsync_mode(DisplayServer::WindowID p_window_id) const {1401const WindowData &wd = windows[p_window_id];1402if (wd.emulate_vsync) {1403return DisplayServer::VSYNC_ENABLED;1404}14051406#ifdef VULKAN_ENABLED1407if (rendering_context) {1408return rendering_context->window_get_vsync_mode(p_window_id);1409}1410#endif // VULKAN_ENABLED14111412#ifdef GLES3_ENABLED1413if (egl_manager) {1414return egl_manager->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;1415}1416#endif // GLES3_ENABLED14171418return DisplayServer::VSYNC_ENABLED;1419}14201421void DisplayServerWayland::window_start_drag(WindowID p_window) {1422MutexLock mutex_lock(wayland_thread.mutex);14231424wayland_thread.window_start_drag(p_window);1425}14261427void DisplayServerWayland::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {1428MutexLock mutex_lock(wayland_thread.mutex);14291430ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);1431wayland_thread.window_start_resize(p_edge, p_window);1432}14331434void DisplayServerWayland::cursor_set_shape(CursorShape p_shape) {1435ERR_FAIL_INDEX(p_shape, CURSOR_MAX);14361437MutexLock mutex_lock(wayland_thread.mutex);14381439if (p_shape == cursor_shape) {1440return;1441}14421443cursor_shape = p_shape;14441445if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {1446// Hidden.1447return;1448}14491450wayland_thread.cursor_set_shape(p_shape);1451}14521453DisplayServerWayland::CursorShape DisplayServerWayland::cursor_get_shape() const {1454MutexLock mutex_lock(wayland_thread.mutex);14551456return cursor_shape;1457}14581459void DisplayServerWayland::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {1460MutexLock mutex_lock(wayland_thread.mutex);14611462if (p_cursor.is_valid()) {1463HashMap<CursorShape, CustomCursor>::Iterator cursor_c = custom_cursors.find(p_shape);14641465if (cursor_c) {1466if (cursor_c->value.resource == p_cursor && cursor_c->value.hotspot == p_hotspot) {1467// We have a cached cursor. Nice.1468wayland_thread.cursor_set_shape(p_shape);1469return;1470}14711472// We're changing this cursor; we'll have to rebuild it.1473custom_cursors.erase(p_shape);1474wayland_thread.cursor_shape_clear_custom_image(p_shape);1475}14761477Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);1478ERR_FAIL_COND(image.is_null());14791480CustomCursor &cursor = custom_cursors[p_shape];14811482cursor.resource = p_cursor;1483cursor.hotspot = p_hotspot;14841485wayland_thread.cursor_shape_set_custom_image(p_shape, image, p_hotspot);14861487wayland_thread.cursor_set_shape(p_shape);1488} else {1489// Clear cache and reset to default system cursor.1490wayland_thread.cursor_shape_clear_custom_image(p_shape);14911492if (cursor_shape == p_shape) {1493wayland_thread.cursor_set_shape(p_shape);1494}14951496if (custom_cursors.has(p_shape)) {1497custom_cursors.erase(p_shape);1498}1499}1500}15011502bool DisplayServerWayland::get_swap_cancel_ok() {1503return swap_cancel_ok;1504}15051506int DisplayServerWayland::keyboard_get_layout_count() const {1507MutexLock mutex_lock(wayland_thread.mutex);15081509return wayland_thread.keyboard_get_layout_count();1510}15111512int DisplayServerWayland::keyboard_get_current_layout() const {1513MutexLock mutex_lock(wayland_thread.mutex);15141515return wayland_thread.keyboard_get_current_layout_index();1516}15171518void DisplayServerWayland::keyboard_set_current_layout(int p_index) {1519MutexLock mutex_lock(wayland_thread.mutex);15201521wayland_thread.keyboard_set_current_layout_index(p_index);1522}15231524String DisplayServerWayland::keyboard_get_layout_language(int p_index) const {1525MutexLock mutex_lock(wayland_thread.mutex);15261527// xkbcommon exposes only the layout's name, which looks like it overlaps with1528// its language.1529return wayland_thread.keyboard_get_layout_name(p_index);1530}15311532String DisplayServerWayland::keyboard_get_layout_name(int p_index) const {1533MutexLock mutex_lock(wayland_thread.mutex);15341535return wayland_thread.keyboard_get_layout_name(p_index);1536}15371538Key DisplayServerWayland::keyboard_get_keycode_from_physical(Key p_keycode) const {1539MutexLock mutex_lock(wayland_thread.mutex);15401541Key key = wayland_thread.keyboard_get_key_from_physical(p_keycode);15421543// If not found, fallback to QWERTY.1544// This should match the behavior of the event pump.1545if (key == Key::NONE) {1546return p_keycode;1547}15481549if (key >= Key::A + 32 && key <= Key::Z + 32) {1550key -= 'a' - 'A';1551}15521553// Make it consistent with the keys returned by `Input`.1554if (key == Key::BACKTAB) {1555key = Key::TAB;1556}15571558return key;1559}15601561bool DisplayServerWayland::color_picker(const Callable &p_callback) {1562#ifdef DBUS_ENABLED1563if (!portal_desktop) {1564return false;1565}1566MutexLock mutex_lock(wayland_thread.mutex);1567WindowID window_id = MAIN_WINDOW_ID;1568// TODO: Use window IDs for multiwindow support.1569WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wayland_thread.window_get_wl_surface(window_id));1570return portal_desktop->color_picker((ws ? ws->exported_handle : String()), p_callback);1571#else1572return false;1573#endif1574}15751576void DisplayServerWayland::try_suspend() {1577// Due to various reasons, we manually handle display synchronization by1578// waiting for a frame event (request to draw) or, if available, the actual1579// window's suspend status. When a window is suspended, we can avoid drawing1580// altogether, either because the compositor told us that we don't need to or1581// because the pace of the frame events became unreliable.1582bool frame = wayland_thread.wait_frame_suspend_ms(WAYLAND_MAX_FRAME_TIME_US / 1000);1583if (!frame) {1584suspend_state = SuspendState::TIMEOUT;1585}1586}15871588void DisplayServerWayland::process_events() {1589wayland_thread.mutex.lock();15901591while (wayland_thread.has_message()) {1592Ref<WaylandThread::Message> msg = wayland_thread.pop_message();15931594// Generic check. Not actual message handling.1595Ref<WaylandThread::WindowMessage> win_msg = msg;1596if (win_msg.is_valid()) {1597ERR_CONTINUE_MSG(win_msg->id == INVALID_WINDOW_ID, "Invalid window ID received from Wayland thread.");15981599if (!windows.has(win_msg->id)) {1600// Window got probably deleted.1601continue;1602}1603}16041605Ref<WaylandThread::WindowRectMessage> winrect_msg = msg;1606if (winrect_msg.is_valid()) {1607_update_window_rect(winrect_msg->rect, winrect_msg->id);1608continue;1609}16101611Ref<WaylandThread::WindowEventMessage> winev_msg = msg;1612if (winev_msg.is_valid() && windows.has(winev_msg->id)) {1613_send_window_event(winev_msg->event, winev_msg->id);16141615if (winev_msg->event == WINDOW_EVENT_FOCUS_IN) {1616#ifdef ACCESSKIT_ENABLED1617if (accessibility_driver) {1618accessibility_driver->accessibility_set_window_focused(winev_msg->id, true);1619}1620#endif1621if (OS::get_singleton()->get_main_loop()) {1622OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);1623}1624} else if (winev_msg->event == WINDOW_EVENT_FOCUS_OUT) {1625#ifdef ACCESSKIT_ENABLED1626if (accessibility_driver) {1627accessibility_driver->accessibility_set_window_focused(winev_msg->id, false);1628}1629#endif1630if (OS::get_singleton()->get_main_loop()) {1631OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);1632}1633Input::get_singleton()->release_pressed_events();1634}1635continue;1636}16371638Ref<WaylandThread::InputEventMessage> inputev_msg = msg;1639if (inputev_msg.is_valid()) {1640Ref<InputEventMouseButton> mb = inputev_msg->event;16411642bool handled = false;1643if (!popup_menu_list.is_empty() && mb.is_valid()) {1644// Popup menu handling.16451646BitField<MouseButtonMask> mouse_mask = mb->get_button_mask();1647if (mouse_mask != last_mouse_monitor_mask && mb->is_pressed()) {1648List<WindowID>::Element *E = popup_menu_list.back();1649List<WindowID>::Element *C = nullptr;16501651// Looking for the oldest popup to close.1652while (E) {1653WindowData &wd = windows[E->get()];1654Point2 global_pos = mb->get_position() + window_get_position(mb->get_window_id());1655if (wd.rect.has_point(global_pos)) {1656break;1657} else if (wd.safe_rect.has_point(global_pos)) {1658break;1659}16601661C = E;1662E = E->prev();1663}16641665if (C) {1666handled = true;1667_send_window_event(WINDOW_EVENT_CLOSE_REQUEST, C->get());1668}1669}16701671last_mouse_monitor_mask = mouse_mask;1672}16731674if (!handled) {1675Input::get_singleton()->parse_input_event(inputev_msg->event);1676}1677continue;1678}16791680Ref<WaylandThread::DropFilesEventMessage> dropfiles_msg = msg;1681if (dropfiles_msg.is_valid()) {1682WindowData wd = windows[dropfiles_msg->id];16831684if (wd.drop_files_callback.is_valid()) {1685Variant v_files = dropfiles_msg->files;1686const Variant *v_args[1] = { &v_files };1687Variant ret;1688Callable::CallError ce;1689wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);1690if (ce.error != Callable::CallError::CALL_OK) {1691ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce)));1692}1693}1694continue;1695}16961697Ref<WaylandThread::IMECommitEventMessage> ime_commit_msg = msg;1698if (ime_commit_msg.is_valid()) {1699for (int i = 0; i < ime_commit_msg->text.length(); i++) {1700const char32_t codepoint = ime_commit_msg->text[i];17011702Ref<InputEventKey> ke;1703ke.instantiate();1704ke->set_window_id(ime_commit_msg->id);1705ke->set_pressed(true);1706ke->set_echo(false);1707ke->set_keycode(Key::NONE);1708ke->set_physical_keycode(Key::NONE);1709ke->set_key_label(Key::NONE);1710ke->set_unicode(codepoint);17111712Input::get_singleton()->parse_input_event(ke);1713}1714ime_text = String();1715ime_selection = Vector2i();17161717OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);1718continue;1719}17201721Ref<WaylandThread::IMEUpdateEventMessage> ime_update_msg = msg;1722if (ime_update_msg.is_valid()) {1723if (ime_text != ime_update_msg->text || ime_selection != ime_update_msg->selection) {1724ime_text = ime_update_msg->text;1725ime_selection = ime_update_msg->selection;17261727OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);1728}1729continue;1730}1731}17321733wayland_thread.keyboard_echo_keys();17341735switch (suspend_state) {1736case SuspendState::NONE: {1737bool emulate_vsync = false;1738for (KeyValue<DisplayServer::WindowID, WindowData> &pair : windows) {1739if (pair.value.emulate_vsync) {1740emulate_vsync = true;1741break;1742}1743}17441745if (emulate_vsync) {1746// Due to the way legacy suspension works, we have to treat low processor1747// usage mode very differently than the regular one.1748if (OS::get_singleton()->is_in_low_processor_usage_mode()) {1749// NOTE: We must avoid committing a surface if we expect a new frame, as we1750// might otherwise commit some inconsistent data (e.g. buffer scale). Note1751// that if a new frame is expected it's going to be committed by the renderer1752// soon anyways.1753if (!RenderingServer::get_singleton()->has_changed()) {1754// We _can't_ commit in a different thread (such as in the frame callback1755// itself) because we would risk to step on the renderer's feet, which would1756// cause subtle but severe issues, such as crashes on setups with explicit1757// sync. This isn't normally a problem, as the renderer commits at every1758// frame (which is what we need for atomic surface updates anyways), but in1759// low processor usage mode that expectation is broken. When it's on, our1760// frame rate stops being constant. This also reflects in the frame1761// information we use for legacy suspension. In order to avoid issues, let's1762// manually commit all surfaces, so that we can get fresh frame data.1763wayland_thread.commit_surfaces();1764try_suspend();1765}1766} else {1767try_suspend();1768}1769}17701771if (wayland_thread.is_suspended()) {1772suspend_state = SuspendState::CAPABILITY;1773}17741775if (suspend_state == SuspendState::TIMEOUT) {1776DEBUG_LOG_WAYLAND("Suspending. Reason: timeout.");1777} else if (suspend_state == SuspendState::CAPABILITY) {1778DEBUG_LOG_WAYLAND("Suspending. Reason: capability.");1779}1780} break;17811782case SuspendState::TIMEOUT: {1783// Certain compositors might not report the "suspended" wm_capability flag.1784// Because of this we'll wake up at the next frame event, indicating the1785// desire for the compositor to let us repaint.1786if (wayland_thread.get_reset_frame()) {1787suspend_state = SuspendState::NONE;1788DEBUG_LOG_WAYLAND("Unsuspending from timeout.");1789}17901791// Since we're not rendering, nothing is committing the windows'1792// surfaces. We have to do it ourselves.1793wayland_thread.commit_surfaces();1794} break;17951796case SuspendState::CAPABILITY: {1797// If we suspended by capability we can assume that it will be reset when1798// the compositor wants us to repaint.1799if (!wayland_thread.is_suspended()) {1800suspend_state = SuspendState::NONE;1801DEBUG_LOG_WAYLAND("Unsuspending from capability.");1802}1803} break;1804}18051806#ifdef DBUS_ENABLED1807if (portal_desktop) {1808portal_desktop->process_callbacks();1809}1810#endif18111812wayland_thread.mutex.unlock();18131814Input::get_singleton()->flush_buffered_events();1815}18161817void DisplayServerWayland::release_rendering_thread() {1818#ifdef GLES3_ENABLED1819if (egl_manager) {1820egl_manager->release_current();1821}1822#endif1823}18241825void DisplayServerWayland::swap_buffers() {1826#ifdef GLES3_ENABLED1827if (egl_manager) {1828egl_manager->swap_buffers();1829}1830#endif1831}18321833void DisplayServerWayland::set_context(Context p_context) {1834MutexLock mutex_lock(wayland_thread.mutex);18351836DEBUG_LOG_WAYLAND(vformat("Setting context %d.", p_context));18371838context = p_context;18391840String app_id = _get_app_id_from_context(p_context);1841wayland_thread.window_set_app_id(MAIN_WINDOW_ID, app_id);1842}18431844bool DisplayServerWayland::is_window_transparency_available() const {1845#if defined(RD_ENABLED)1846if (rendering_device && !rendering_device->is_composite_alpha_supported()) {1847return false;1848}1849#endif1850return OS::get_singleton()->is_layered_allowed();1851}18521853Vector<String> DisplayServerWayland::get_rendering_drivers_func() {1854Vector<String> drivers;18551856#ifdef VULKAN_ENABLED1857drivers.push_back("vulkan");1858#endif18591860#ifdef GLES3_ENABLED1861drivers.push_back("opengl3");1862drivers.push_back("opengl3_es");1863#endif1864drivers.push_back("dummy");18651866return drivers;1867}18681869DisplayServer *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) {1870DisplayServer *ds = memnew(DisplayServerWayland(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, p_context, p_parent_window, r_error));1871if (r_error != OK) {1872ERR_PRINT("Can't create the Wayland display server.");1873memdelete(ds);18741875return nullptr;1876}1877return ds;1878}18791880DisplayServerWayland::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) {1881#if defined(GLES3_ENABLED) || defined(DBUS_ENABLED)1882#ifdef SOWRAP_ENABLED1883#ifdef DEBUG_ENABLED1884int dylibloader_verbose = 1;1885#else1886int dylibloader_verbose = 0;1887#endif // DEBUG_ENABLED1888#endif // SOWRAP_ENABLED1889#endif // defined(GLES3_ENABLED) || defined(DBUS_ENABLED)18901891r_error = ERR_UNAVAILABLE;1892context = p_context;18931894String current_desk = OS::get_singleton()->get_environment("XDG_CURRENT_DESKTOP").to_lower();1895String session_desk = OS::get_singleton()->get_environment("XDG_SESSION_DESKTOP").to_lower();1896swap_cancel_ok = (current_desk.contains("kde") || session_desk.contains("kde") || current_desk.contains("lxqt") || session_desk.contains("lxqt"));18971898Error thread_err = wayland_thread.init();18991900if (thread_err != OK) {1901r_error = thread_err;1902ERR_FAIL_MSG("Could not initialize the Wayland thread.");1903}19041905// Input.1906Input::get_singleton()->set_event_dispatch_function(dispatch_input_events);19071908native_menu = memnew(NativeMenu);19091910#ifdef SPEECHD_ENABLED1911// Init TTS1912bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");1913if (tts_enabled) {1914initialize_tts();1915}1916#endif19171918#ifdef ACCESSKIT_ENABLED1919if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {1920accessibility_driver = memnew(AccessibilityDriverAccessKit);1921if (accessibility_driver->init() != OK) {1922memdelete(accessibility_driver);1923accessibility_driver = nullptr;1924}1925}1926#endif19271928rendering_driver = p_rendering_driver;19291930bool driver_found = false;1931String executable_name = OS::get_singleton()->get_executable_path().get_file();19321933if (rendering_driver == "dummy") {1934RasterizerDummy::make_current();1935driver_found = true;1936}19371938#ifdef RD_ENABLED1939#ifdef VULKAN_ENABLED1940if (rendering_driver == "vulkan") {1941rendering_context = memnew(RenderingContextDriverVulkanWayland);1942}1943#endif // VULKAN_ENABLED19441945if (rendering_context) {1946if (rendering_context->initialize() != OK) {1947memdelete(rendering_context);1948rendering_context = nullptr;1949#if defined(GLES3_ENABLED)1950bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");1951if (fallback_to_opengl3 && rendering_driver != "opengl3") {1952WARN_PRINT("Your video card drivers seem not to support the required Vulkan version, switching to OpenGL 3.");1953rendering_driver = "opengl3";1954OS::get_singleton()->set_current_rendering_method("gl_compatibility");1955OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);1956} else1957#endif // GLES3_ENABLED1958{1959r_error = ERR_CANT_CREATE;19601961if (p_rendering_driver == "vulkan") {1962OS::get_singleton()->alert(1963vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"1964"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"1965"You can enable the OpenGL 3 driver by starting the engine from the\n"1966"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"1967"If you recently updated your video card drivers, try rebooting.",1968executable_name),1969"Unable to initialize Vulkan video driver");1970}19711972ERR_FAIL_MSG(vformat("Could not initialize %s", rendering_driver));1973}1974}19751976driver_found = true;1977}1978#endif // RD_ENABLED19791980#ifdef GLES3_ENABLED1981if (rendering_driver == "opengl3" || rendering_driver == "opengl3_es") {1982#ifdef SOWRAP_ENABLED1983if (initialize_wayland_egl(dylibloader_verbose) != 0) {1984WARN_PRINT("Can't load the Wayland EGL library.");1985return;1986}1987#endif // SOWRAP_ENABLED19881989if (getenv("DRI_PRIME") == nullptr) {1990int prime_idx = -1;19911992if (getenv("PRIMUS_DISPLAY") ||1993getenv("PRIMUS_libGLd") ||1994getenv("PRIMUS_libGLa") ||1995getenv("PRIMUS_libGL") ||1996getenv("PRIMUS_LOAD_GLOBAL") ||1997getenv("BUMBLEBEE_SOCKET") ||1998getenv("__NV_PRIME_RENDER_OFFLOAD")) {1999print_verbose("Optirun/primusrun detected. Skipping GPU detection");2000prime_idx = 0;2001}20022003// Some tools use fake libGL libraries and have them override the real one using2004// LD_LIBRARY_PATH, so we skip them. *But* Steam also sets LD_LIBRARY_PATH for its2005// runtime and includes system `/lib` and `/lib64`... so ignore Steam.2006if (prime_idx == -1 && getenv("LD_LIBRARY_PATH") && !getenv("STEAM_RUNTIME_LIBRARY_PATH")) {2007String ld_library_path(getenv("LD_LIBRARY_PATH"));2008Vector<String> libraries = ld_library_path.split(":");20092010for (int i = 0; i < libraries.size(); ++i) {2011if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||2012FileAccess::exists(libraries[i] + "/libGL.so")) {2013print_verbose("Custom libGL override detected. Skipping GPU detection");2014prime_idx = 0;2015}2016}2017}20182019if (prime_idx == -1) {2020print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");2021prime_idx = DetectPrimeEGL::detect_prime(EGL_PLATFORM_WAYLAND_KHR);2022}20232024if (prime_idx) {2025print_line(vformat("Found discrete GPU, setting DRI_PRIME=%d to use it.", prime_idx));2026print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");2027setenv("DRI_PRIME", itos(prime_idx).utf8().ptr(), 1);2028}2029}20302031if (rendering_driver == "opengl3") {2032egl_manager = memnew(EGLManagerWayland);20332034if (egl_manager->initialize(wayland_thread.get_wl_display()) != OK || egl_manager->open_display(wayland_thread.get_wl_display()) != OK) {2035memdelete(egl_manager);2036egl_manager = nullptr;20372038bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_gles");2039if (fallback) {2040WARN_PRINT("Your video card drivers seem not to support the required OpenGL version, switching to OpenGLES.");2041rendering_driver = "opengl3_es";2042OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);2043} else {2044r_error = ERR_UNAVAILABLE;20452046OS::get_singleton()->alert(2047vformat("Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"2048"If possible, consider updating your video card drivers or using the Vulkan driver.\n\n"2049"You can enable the Vulkan driver by starting the engine from the\n"2050"command line with the command:\n\n \"%s\" --rendering-driver vulkan\n\n"2051"If you recently updated your video card drivers, try rebooting.",2052executable_name),2053"Unable to initialize OpenGL video driver");20542055ERR_FAIL_MSG("Could not initialize OpenGL.");2056}2057} else {2058RasterizerGLES3::make_current(true);2059driver_found = true;2060}2061}20622063if (rendering_driver == "opengl3_es") {2064egl_manager = memnew(EGLManagerWaylandGLES);20652066if (egl_manager->initialize(wayland_thread.get_wl_display()) != OK || egl_manager->open_display(wayland_thread.get_wl_display()) != OK) {2067memdelete(egl_manager);2068egl_manager = nullptr;2069r_error = ERR_CANT_CREATE;20702071OS::get_singleton()->alert(2072vformat("Your video card drivers seem not to support the required OpenGL ES 3.0 version.\n\n"2073"If possible, consider updating your video card drivers or using the Vulkan driver.\n\n"2074"You can enable the Vulkan driver by starting the engine from the\n"2075"command line with the command:\n\n \"%s\" --rendering-driver vulkan\n\n"2076"If you recently updated your video card drivers, try rebooting.",2077executable_name),2078"Unable to initialize OpenGL ES video driver");20792080ERR_FAIL_MSG("Could not initialize OpenGL ES.");2081}20822083RasterizerGLES3::make_current(false);2084driver_found = true;2085}2086}2087#endif // GLES3_ENABLED20882089if (!driver_found) {2090r_error = ERR_UNAVAILABLE;2091ERR_FAIL_MSG("Video driver not found.");2092}20932094cursor_set_shape(CURSOR_BUSY);20952096WindowData &wd = windows[MAIN_WINDOW_ID];20972098wd.id = MAIN_WINDOW_ID;2099wd.mode = p_mode;2100wd.flags = p_flags;2101wd.vsync_mode = p_vsync_mode;2102wd.rect.size = p_resolution;2103wd.title = "Godot";21042105#ifdef ACCESSKIT_ENABLED2106if (accessibility_driver && !accessibility_driver->window_create(wd.id, nullptr)) {2107if (OS::get_singleton()->is_stdout_verbose()) {2108ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");2109}2110memdelete(accessibility_driver);2111accessibility_driver = nullptr;2112}2113#endif21142115show_window(MAIN_WINDOW_ID);21162117#ifdef RD_ENABLED2118if (rendering_context) {2119rendering_device = memnew(RenderingDevice);2120if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {2121memdelete(rendering_device);2122rendering_device = nullptr;2123memdelete(rendering_context);2124rendering_context = nullptr;2125r_error = ERR_UNAVAILABLE;2126return;2127}2128rendering_device->screen_create(MAIN_WINDOW_ID);21292130RendererCompositorRD::make_current();2131}2132#endif // RD_ENABLED21332134#ifdef DBUS_ENABLED2135bool dbus_ok = true;2136#ifdef SOWRAP_ENABLED2137if (initialize_dbus(dylibloader_verbose) != 0) {2138print_verbose("Failed to load DBus library!");2139dbus_ok = false;2140}2141#endif2142if (dbus_ok) {2143bool ver_ok = false;2144int version_major = 0;2145int version_minor = 0;2146int version_rev = 0;2147dbus_get_version(&version_major, &version_minor, &version_rev);2148ver_ok = (version_major == 1 && version_minor >= 10) || (version_major > 1); // 1.10.02149print_verbose(vformat("DBus %d.%d.%d detected.", version_major, version_minor, version_rev));2150if (!ver_ok) {2151print_verbose("Unsupported DBus library version!");2152dbus_ok = false;2153}2154}2155if (dbus_ok) {2156screensaver = memnew(FreeDesktopScreenSaver);2157portal_desktop = memnew(FreeDesktopPortalDesktop);2158atspi_monitor = memnew(FreeDesktopAtSPIMonitor);2159}2160#endif // DBUS_ENABLED21612162screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));21632164r_error = OK;2165}21662167DisplayServerWayland::~DisplayServerWayland() {2168if (native_menu) {2169memdelete(native_menu);2170native_menu = nullptr;2171}21722173// Iterating on the window map while we delete stuff from it is a bit2174// uncomfortable, plus we can't even delete /all/ windows in an arbitrary order2175// (due to popups).2176List<WindowID> toplevels;21772178for (const KeyValue<WindowID, WindowData> &pair : windows) {2179WindowID id = pair.key;21802181if (!window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, id)) {2182toplevels.push_back(id);2183#ifdef ACCESSKIT_ENABLED2184} else if (accessibility_driver) {2185accessibility_driver->window_destroy(id);2186#endif2187}2188}21892190for (WindowID &id : toplevels) {2191delete_sub_window(id);2192}2193windows.clear();21942195wayland_thread.destroy();21962197// Destroy all drivers.2198#ifdef RD_ENABLED2199if (rendering_device) {2200memdelete(rendering_device);2201}22022203if (rendering_context) {2204memdelete(rendering_context);2205}2206#endif22072208#ifdef SPEECHD_ENABLED2209if (tts) {2210memdelete(tts);2211}2212#endif22132214#ifdef ACCESSKIT_ENABLED2215if (accessibility_driver) {2216memdelete(accessibility_driver);2217}2218#endif22192220#ifdef DBUS_ENABLED2221if (portal_desktop) {2222memdelete(portal_desktop);2223}2224if (screensaver) {2225memdelete(screensaver);2226}2227if (atspi_monitor) {2228memdelete(atspi_monitor);2229}2230#endif2231}22322233void DisplayServerWayland::register_wayland_driver() {2234register_create_function("wayland", create_func, get_rendering_drivers_func);2235}22362237#endif //WAYLAND_ENABLED223822392240