Path: blob/master/platform/windows/display_server_windows.cpp
20941 views
/**************************************************************************/1/* display_server_windows.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_windows.h"3132#include "drop_target_windows.h"33#include "os_windows.h"34#include "scene/main/window.h"35#include "wgl_detect_version.h"3637#include "core/config/project_settings.h"38#include "core/input/input.h"39#include "core/io/file_access.h"40#include "core/io/marshalls.h"41#include "core/io/xml_parser.h"42#include "core/os/main_loop.h"43#include "core/version.h"44#include "drivers/png/png_driver_common.h"45#include "main/main.h"46#include "scene/resources/texture.h"4748#ifdef SDL_ENABLED49#include "drivers/sdl/joypad_sdl.h"50#endif5152#include "servers/rendering/dummy/rasterizer_dummy.h"5354#if defined(VULKAN_ENABLED)55#include "rendering_context_driver_vulkan_windows.h"56#endif57#if defined(D3D12_ENABLED)58#include "drivers/d3d12/rendering_context_driver_d3d12.h"59#endif60#if defined(GLES3_ENABLED)61#include "drivers/gles3/rasterizer_gles3.h"62#endif6364#if defined(ACCESSKIT_ENABLED)65#include "drivers/accesskit/accessibility_driver_accesskit.h"66#endif6768#include <avrt.h>69#include <dwmapi.h>70#include <propkey.h>71#include <propvarutil.h>72#include <shellapi.h>73#include <shellscalingapi.h>74#include <shlwapi.h>75#include <shobjidl.h>76#include <wbemcli.h>7778#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE79#define DWMWA_USE_IMMERSIVE_DARK_MODE 2080#endif8182#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H183#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 1984#endif8586#ifndef DWMWA_WINDOW_CORNER_PREFERENCE87#define DWMWA_WINDOW_CORNER_PREFERENCE 3388#endif8990#ifndef DWMWCP_DEFAULT91#define DWMWCP_DEFAULT 092#endif9394#ifndef DWMWCP_DONOTROUND95#define DWMWCP_DONOTROUND 196#endif9798#define WM_INDICATOR_CALLBACK_MESSAGE (WM_USER + 1)99100static String format_error_message(DWORD id) {101LPWSTR messageBuffer = nullptr;102size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,103nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);104105String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size);106107LocalFree(messageBuffer);108109return msg;110}111112static void track_mouse_leave_event(HWND hWnd) {113TRACKMOUSEEVENT tme;114tme.cbSize = sizeof(TRACKMOUSEEVENT);115tme.dwFlags = TME_LEAVE;116tme.hwndTrack = hWnd;117tme.dwHoverTime = HOVER_DEFAULT;118TrackMouseEvent(&tme);119}120121bool DisplayServerWindows::has_feature(Feature p_feature) const {122switch (p_feature) {123#ifndef DISABLE_DEPRECATED124case FEATURE_GLOBAL_MENU: {125return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));126} break;127#endif128case FEATURE_SUBWINDOWS:129case FEATURE_TOUCHSCREEN:130case FEATURE_MOUSE:131case FEATURE_MOUSE_WARP:132case FEATURE_CLIPBOARD:133case FEATURE_CURSOR_SHAPE:134case FEATURE_CUSTOM_CURSOR_SHAPE:135case FEATURE_IME:136case FEATURE_WINDOW_TRANSPARENCY:137case FEATURE_HIDPI:138case FEATURE_ICON:139case FEATURE_NATIVE_ICON:140case FEATURE_NATIVE_DIALOG:141case FEATURE_NATIVE_DIALOG_INPUT:142case FEATURE_NATIVE_DIALOG_FILE:143case FEATURE_NATIVE_DIALOG_FILE_EXTRA:144//case FEATURE_NATIVE_DIALOG_FILE_MIME:145case FEATURE_SWAP_BUFFERS:146case FEATURE_KEEP_SCREEN_ON:147case FEATURE_TEXT_TO_SPEECH:148case FEATURE_SCREEN_CAPTURE:149case FEATURE_STATUS_INDICATOR:150case FEATURE_WINDOW_EMBEDDING:151case FEATURE_WINDOW_DRAG:152return true;153case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:154return (os_ver.dwBuildNumber >= 19041); // Fully supported on Windows 10 Vibranium R1 (2004)+ only, captured as black rect on older versions.155case FEATURE_EMOJI_AND_SYMBOL_PICKER:156return (os_ver.dwBuildNumber >= 17134); // Windows 10 Redstone 4 (1803)+ only.157#ifdef ACCESSKIT_ENABLED158case FEATURE_ACCESSIBILITY_SCREEN_READER: {159return (accessibility_driver != nullptr);160} break;161#endif162default:163return false;164}165}166167String DisplayServerWindows::get_name() const {168return "Windows";169}170171Vector2i _logical_to_physical(const Vector2i &p_point) {172POINT p1;173p1.x = p_point.x;174p1.y = p_point.y;175LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p1);176return Vector2i(p1.x, p1.y);177}178179Vector2i DisplayServerWindows::_get_screen_expand_offset(int p_screen) const {180Vector2i p1 = _logical_to_physical(screen_get_position(p_screen));181Vector2i p2 = _logical_to_physical(screen_get_position(p_screen) + screen_get_size(p_screen));182183int screen_down = -1;184int screen_right = -1;185for (int i = 0; i < get_screen_count(); i++) {186if (i == p_screen) {187continue;188}189Vector2i sp1 = _logical_to_physical(screen_get_position(i));190Vector2i sp2 = _logical_to_physical(screen_get_position(i) + screen_get_size(i));191if (sp1.y >= p2.y - 5 && sp1.y <= p2.y + 5 && sp1.x <= p2.x && p1.x <= sp2.x) {192screen_down = i;193}194if (sp1.x >= p2.x - 5 && sp1.x <= p2.x + 5 && sp1.y <= p2.y && p1.y <= sp2.y) {195screen_right = i;196}197}198199if (screen_down == -1) {200return Vector2i(0, 2);201} else if (screen_right == -1) {202return Vector2i(2, 0);203} else {204int diff_d = screen_get_refresh_rate(p_screen) - screen_get_refresh_rate(screen_down);205int diff_r = screen_get_refresh_rate(p_screen) - screen_get_refresh_rate(screen_right);206if (diff_d < diff_r) {207return Vector2i(0, 2);208} else {209return Vector2i(2, 0);210}211}212}213214void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {215if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {216// Hide cursor before moving.217if (hCursor == nullptr) {218hCursor = SetCursor(nullptr);219} else {220SetCursor(nullptr);221}222}223224if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {225// Mouse is grabbed (captured or confined).226WindowID window_id = _get_focused_window_or_popup();227if (!windows.has(window_id)) {228window_id = MAIN_WINDOW_ID;229}230231WindowData &wd = windows[window_id];232233Vector2i off = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? _get_screen_expand_offset(window_get_current_screen(window_id)) : Vector2i();234235RECT clipRect;236GetClientRect(wd.hWnd, &clipRect);237clipRect.right -= off.x;238clipRect.bottom -= off.y;239ClientToScreen(wd.hWnd, (POINT *)&clipRect.left);240ClientToScreen(wd.hWnd, (POINT *)&clipRect.right);241ClipCursor(&clipRect);242if (p_mode == MOUSE_MODE_CAPTURED) {243center = window_get_size() / 2;244POINT pos = { (int)center.x, (int)center.y };245ClientToScreen(wd.hWnd, &pos);246SetCursorPos(pos.x, pos.y);247SetCapture(wd.hWnd);248249_register_raw_input_devices(window_id);250}251} else {252// Mouse is free to move around (not captured or confined).253// When the user is moving a window, it's important to not ReleaseCapture because it will cause254// the window movement to stop and if the user tries to move the Windows when it's not activated,255// it will prevent the window movement. It's probably impossible to move the Window while it's captured anyway.256if (!_has_moving_window()) {257ReleaseCapture();258}259ClipCursor(nullptr);260261_register_raw_input_devices(INVALID_WINDOW_ID);262}263264if (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED) {265// Show cursor.266CursorShape c = cursor_shape;267cursor_shape = CURSOR_MAX;268cursor_set_shape(c);269}270}271272DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() const {273const List<WindowID>::Element *E = popup_list.back();274if (E) {275return E->get();276}277278return last_focused_window;279}280281bool DisplayServerWindows::_has_moving_window() const {282for (const KeyValue<WindowID, WindowData> &E : windows) {283if (E.value.move_timer_id) {284return true;285}286}287return false;288}289290void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) {291use_raw_input = true;292293RAWINPUTDEVICE rid[2] = {};294rid[0].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC295rid[0].usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE296rid[0].dwFlags = 0;297298rid[1].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC299rid[1].usUsage = 0x06; // HID_USAGE_GENERIC_KEYBOARD300rid[1].dwFlags = 0;301302if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) {303// Follow the defined window304rid[0].hwndTarget = windows[p_target_window].hWnd;305rid[1].hwndTarget = windows[p_target_window].hWnd;306} else {307// Follow the keyboard focus308rid[0].hwndTarget = nullptr;309rid[1].hwndTarget = nullptr;310}311312if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) {313// Registration failed.314use_raw_input = false;315}316}317318void DisplayServerWindows::initialize_tts() const {319const_cast<DisplayServerWindows *>(this)->tts = memnew(TTS_Windows);320}321322bool DisplayServerWindows::tts_is_speaking() const {323if (unlikely(!tts)) {324initialize_tts();325}326ERR_FAIL_NULL_V(tts, false);327return tts->is_speaking();328}329330bool DisplayServerWindows::tts_is_paused() const {331if (unlikely(!tts)) {332initialize_tts();333}334ERR_FAIL_NULL_V(tts, false);335return tts->is_paused();336}337338TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const {339if (unlikely(!tts)) {340initialize_tts();341}342ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());343return tts->get_voices();344}345346void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int64_t p_utterance_id, bool p_interrupt) {347if (unlikely(!tts)) {348initialize_tts();349}350ERR_FAIL_NULL(tts);351tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);352}353354void DisplayServerWindows::tts_pause() {355if (unlikely(!tts)) {356initialize_tts();357}358ERR_FAIL_NULL(tts);359tts->pause();360}361362void DisplayServerWindows::tts_resume() {363if (unlikely(!tts)) {364initialize_tts();365}366ERR_FAIL_NULL(tts);367tts->resume();368}369370void DisplayServerWindows::tts_stop() {371if (unlikely(!tts)) {372initialize_tts();373}374ERR_FAIL_NULL(tts);375tts->stop();376}377378Error DisplayServerWindows::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) {379return _file_dialog_with_options_show(p_title, p_current_directory, String(), p_filename, p_show_hidden, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false, p_window_id);380}381382Error DisplayServerWindows::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) {383return _file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, true, p_window_id);384}385386GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Wnon-virtual-dtor") // Silence warning due to a COM API weirdness.387388class FileDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents {389LONG ref_count = 1;390int ctl_id = 1;391392HashMap<int, String> ctls;393Dictionary selected;394String root;395396public:397// IUnknown methods398HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) {399static const QITAB qit[] = {400#ifdef __MINGW32__401{ &__uuidof(IFileDialogEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogEvents, FileDialogEventHandler)) },402{ &__uuidof(IFileDialogControlEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogControlEvents, FileDialogEventHandler)) },403#else404QITABENT(FileDialogEventHandler, IFileDialogEvents),405QITABENT(FileDialogEventHandler, IFileDialogControlEvents),406#endif407{ nullptr, 0 },408};409return QISearch(this, qit, riid, ppv);410}411412ULONG STDMETHODCALLTYPE AddRef() {413return InterlockedIncrement(&ref_count);414}415416ULONG STDMETHODCALLTYPE Release() {417long ref = InterlockedDecrement(&ref_count);418if (!ref) {419delete this;420}421return ref;422}423424// IFileDialogEvents methods425HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *) { return S_OK; }426HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialog *) { return S_OK; }427428HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog *p_pfd, IShellItem *p_item) {429if (root.is_empty()) {430return S_OK;431}432433LPWSTR lpw_path = nullptr;434p_item->GetDisplayName(SIGDN_FILESYSPATH, &lpw_path);435if (!lpw_path) {436return S_FALSE;437}438String path = String::utf16((const char16_t *)lpw_path).replace_char('\\', '/').trim_prefix(R"(\\?\)").simplify_path();439if (!path.begins_with(root.simplify_path())) {440return S_FALSE;441}442return S_OK;443}444445HRESULT STDMETHODCALLTYPE OnHelp(IFileDialog *) { return S_OK; }446HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialog *) { return S_OK; }447HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; }448HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog *pfd) { return S_OK; }449HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; }450451// IFileDialogControlEvents methods452HRESULT STDMETHODCALLTYPE OnItemSelected(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, DWORD p_item_idx) {453if (ctls.has(p_ctl_id)) {454selected[ctls[p_ctl_id]] = (int)p_item_idx;455}456return S_OK;457}458459HRESULT STDMETHODCALLTYPE OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; }460HRESULT STDMETHODCALLTYPE OnCheckButtonToggled(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, BOOL p_checked) {461if (ctls.has(p_ctl_id)) {462selected[ctls[p_ctl_id]] = (bool)p_checked;463}464return S_OK;465}466HRESULT STDMETHODCALLTYPE OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; }467468Dictionary get_selected() {469return selected;470}471472void set_root(const String &p_root) {473root = p_root;474}475476void add_option(IFileDialogCustomize *p_pfdc, const String &p_name, const Vector<String> &p_options, int p_default) {477int gid = ctl_id++;478int cid = ctl_id++;479480if (p_options.is_empty()) {481// Add check box.482p_pfdc->StartVisualGroup(gid, L"");483p_pfdc->AddCheckButton(cid, (LPCWSTR)p_name.utf16().get_data(), p_default);484p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);485p_pfdc->EndVisualGroup();486selected[p_name] = (bool)p_default;487} else {488// Add combo box.489p_pfdc->StartVisualGroup(gid, (LPCWSTR)p_name.utf16().get_data());490p_pfdc->AddComboBox(cid);491p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);492for (int i = 0; i < p_options.size(); i++) {493p_pfdc->AddControlItem(cid, i, (LPCWSTR)p_options[i].utf16().get_data());494}495p_pfdc->SetSelectedControlItem(cid, p_default);496p_pfdc->EndVisualGroup();497selected[p_name] = p_default;498}499ctls[cid] = p_name;500}501502virtual ~FileDialogEventHandler() {}503};504505GODOT_GCC_WARNING_POP506507LRESULT CALLBACK WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {508DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());509if (ds_win) {510return ds_win->WndProcFileDialog(hWnd, uMsg, wParam, lParam);511} else {512return DefWindowProcW(hWnd, uMsg, wParam, lParam);513}514}515516LRESULT DisplayServerWindows::WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {517MutexLock lock(file_dialog_mutex);518if (file_dialog_wnd.has(hWnd)) {519if (file_dialog_wnd[hWnd]->close_requested.is_set()) {520IPropertyStore *prop_store;521HRESULT hr = SHGetPropertyStoreForWindow(hWnd, IID_IPropertyStore, (void **)&prop_store);522if (hr == S_OK) {523PROPVARIANT val;524PropVariantInit(&val);525prop_store->SetValue(PKEY_AppUserModel_ID, val);526prop_store->Release();527}528DestroyWindow(hWnd);529file_dialog_wnd.erase(hWnd);530}531}532return DefWindowProcW(hWnd, uMsg, wParam, lParam);533}534535void DisplayServerWindows::_thread_fd_monitor(void *p_ud) {536DisplayServerWindows *ds = static_cast<DisplayServerWindows *>(get_singleton());537FileDialogData *fd = (FileDialogData *)p_ud;538539if (fd->mode < 0 && fd->mode >= DisplayServer::FILE_DIALOG_MODE_SAVE_MAX) {540fd->finished.set();541return;542}543CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);544545int64_t x = fd->wrect.position.x;546int64_t y = fd->wrect.position.y;547int64_t w = fd->wrect.size.x;548int64_t h = fd->wrect.size.y;549550WNDCLASSW wc = {};551wc.lpfnWndProc = (WNDPROC)::WndProcFileDialog;552wc.hInstance = GetModuleHandle(nullptr);553wc.lpszClassName = L"Engine File Dialog";554RegisterClassW(&wc);555556HWND hwnd_dialog = CreateWindowExW(WS_EX_APPWINDOW, L"Engine File Dialog", L"", WS_OVERLAPPEDWINDOW, x, y, w, h, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);557if (hwnd_dialog) {558{559MutexLock lock(ds->file_dialog_mutex);560ds->file_dialog_wnd[hwnd_dialog] = fd;561}562563HICON mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_SMALL, 0);564if (mainwindow_icon) {565SendMessage(hwnd_dialog, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);566}567mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_BIG, 0);568if (mainwindow_icon) {569SendMessage(hwnd_dialog, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);570}571IPropertyStore *prop_store;572HRESULT hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);573if (hr == S_OK) {574PROPVARIANT val;575InitPropVariantFromString((PCWSTR)fd->appid.utf16().get_data(), &val);576prop_store->SetValue(PKEY_AppUserModel_ID, val);577prop_store->Release();578}579}580581SetCurrentProcessExplicitAppUserModelID((PCWSTR)fd->appid.utf16().get_data());582583Vector<Char16String> filter_names;584Vector<Char16String> filter_exts;585for (const String &E : fd->filters) {586Vector<String> tokens = E.split(";");587if (tokens.size() >= 1) {588String flt = tokens[0].strip_edges();589int filter_slice_count = flt.get_slice_count(",");590Vector<String> exts;591for (int j = 0; j < filter_slice_count; j++) {592String str = (flt.get_slicec(',', j).strip_edges());593if (!str.is_empty()) {594exts.push_back(str);595}596}597if (!exts.is_empty()) {598String str = String(";").join(exts);599filter_exts.push_back(str.utf16());600if (tokens.size() >= 2) {601filter_names.push_back(tokens[1].strip_edges().utf16());602} else {603filter_names.push_back(str.utf16());604}605}606}607}608if (filter_names.is_empty()) {609filter_exts.push_back(String("*.*").utf16());610filter_names.push_back((RTR("All Files") + " (*.*)").utf16());611}612613Vector<COMDLG_FILTERSPEC> filters;614for (int i = 0; i < filter_names.size(); i++) {615filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });616}617618HRESULT hr = S_OK;619IFileDialog *pfd = nullptr;620if (fd->mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {621hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd);622} else {623hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd);624}625if (SUCCEEDED(hr)) {626IFileDialogEvents *pfde = nullptr;627FileDialogEventHandler *event_handler = new FileDialogEventHandler();628hr = event_handler->QueryInterface(IID_PPV_ARGS(&pfde));629630DWORD cookie = 0;631hr = pfd->Advise(pfde, &cookie);632633IFileDialogCustomize *pfdc = nullptr;634hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));635636for (int i = 0; i < fd->options.size(); i++) {637const Dictionary &item = fd->options[i];638if (!item.has("name") || !item.has("values") || !item.has("default")) {639continue;640}641event_handler->add_option(pfdc, item["name"], item["values"], item["default"]);642}643event_handler->set_root(fd->root);644645pfdc->Release();646647DWORD flags;648pfd->GetOptions(&flags);649if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {650flags |= FOS_ALLOWMULTISELECT;651}652if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR) {653flags |= FOS_PICKFOLDERS;654}655if (fd->show_hidden) {656flags |= FOS_FORCESHOWHIDDEN;657}658pfd->SetOptions(flags | FOS_FORCEFILESYSTEM);659pfd->SetTitle((LPCWSTR)fd->title.utf16().get_data());660661String dir = ProjectSettings::get_singleton()->globalize_path(fd->current_directory);662if (dir == ".") {663dir = OS::get_singleton()->get_executable_path().get_base_dir();664}665if (dir.is_relative_path() || dir == ".") {666Char16String current_dir_name;667size_t str_len = GetCurrentDirectoryW(0, nullptr);668current_dir_name.resize_uninitialized(str_len + 1);669GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw());670if (dir == ".") {671dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/');672} else {673dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/').path_join(dir);674}675}676dir = dir.simplify_path();677dir = dir.trim_prefix(R"(\\?\)").replace_char('/', '\\');678679IShellItem *shellitem = nullptr;680hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem);681if (SUCCEEDED(hr)) {682pfd->SetDefaultFolder(shellitem);683pfd->SetFolder(shellitem);684}685686pfd->SetFileName((LPCWSTR)fd->filename.utf16().get_data());687pfd->SetFileTypes(filters.size(), filters.ptr());688pfd->SetFileTypeIndex(0);689690hr = pfd->Show(hwnd_dialog);691pfd->Unadvise(cookie);692693Dictionary options = event_handler->get_selected();694695pfde->Release();696event_handler->Release();697698UINT index = 0;699pfd->GetFileTypeIndex(&index);700if (index > 0) {701index = index - 1;702}703704if (SUCCEEDED(hr)) {705Vector<String> file_names;706707if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {708IShellItemArray *results;709hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results);710if (SUCCEEDED(hr)) {711DWORD count = 0;712results->GetCount(&count);713for (DWORD i = 0; i < count; i++) {714IShellItem *result;715results->GetItemAt(i, &result);716717PWSTR file_path = nullptr;718hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);719if (SUCCEEDED(hr)) {720file_names.push_back(String::utf16((const char16_t *)file_path).replace_char('\\', '/').trim_prefix(R"(\\?\)"));721CoTaskMemFree(file_path);722}723result->Release();724}725results->Release();726}727} else {728IShellItem *result;729hr = pfd->GetResult(&result);730if (SUCCEEDED(hr)) {731PWSTR file_path = nullptr;732hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);733if (SUCCEEDED(hr)) {734file_names.push_back(String::utf16((const char16_t *)file_path).replace_char('\\', '/').trim_prefix(R"(\\?\)"));735CoTaskMemFree(file_path);736}737result->Release();738}739}740if (fd->callback.is_valid()) {741MutexLock lock(ds->file_dialog_mutex);742FileDialogCallback cb;743cb.callback = fd->callback;744cb.status = true;745cb.files = file_names;746cb.index = index;747cb.options = options;748cb.opt_in_cb = fd->options_in_cb;749ds->pending_cbs.push_back(cb);750}751} else {752if (fd->callback.is_valid()) {753MutexLock lock(ds->file_dialog_mutex);754FileDialogCallback cb;755cb.callback = fd->callback;756cb.status = false;757cb.files = Vector<String>();758cb.index = index;759cb.options = options;760cb.opt_in_cb = fd->options_in_cb;761ds->pending_cbs.push_back(cb);762}763}764pfd->Release();765} else {766if (fd->callback.is_valid()) {767MutexLock lock(ds->file_dialog_mutex);768FileDialogCallback cb;769cb.callback = fd->callback;770cb.status = false;771cb.files = Vector<String>();772cb.index = 0;773cb.options = Dictionary();774cb.opt_in_cb = fd->options_in_cb;775ds->pending_cbs.push_back(cb);776}777}778{779MutexLock lock(ds->file_dialog_mutex);780if (hwnd_dialog && ds->file_dialog_wnd.has(hwnd_dialog)) {781IPropertyStore *prop_store;782hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);783if (hr == S_OK) {784PROPVARIANT val;785PropVariantInit(&val);786prop_store->SetValue(PKEY_AppUserModel_ID, val);787prop_store->Release();788}789DestroyWindow(hwnd_dialog);790ds->file_dialog_wnd.erase(hwnd_dialog);791}792}793UnregisterClassW(L"Engine File Dialog", GetModuleHandle(nullptr));794CoUninitialize();795796fd->finished.set();797798if (fd->window_id != INVALID_WINDOW_ID) {799callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd->window_id);800}801}802803Error DisplayServerWindows::_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, bool p_options_in_cb, WindowID p_window_id) {804_THREAD_SAFE_METHOD_805806ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);807808String appname;809if (Engine::get_singleton()->is_editor_hint()) {810appname = "Godot.GodotEditor." + String(GODOT_VERSION_BRANCH);811} else {812String name = GLOBAL_GET("application/config/name");813String version = GLOBAL_GET("application/config/version");814if (version.is_empty()) {815version = "0";816}817String clean_app_name = name.to_pascal_case();818for (int i = 0; i < clean_app_name.length(); i++) {819if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {820clean_app_name[i] = '_';821}822}823clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");824appname = "Godot." + clean_app_name + "." + version;825}826827FileDialogData *fd = memnew(FileDialogData);828if (windows.has(p_window_id) && !windows[p_window_id].is_popup) {829fd->hwnd_owner = windows[p_window_id].hWnd;830RECT crect;831GetWindowRect(fd->hwnd_owner, &crect);832fd->wrect = Rect2i(crect.left, crect.top, crect.right - crect.left, crect.bottom - crect.top);833} else {834fd->hwnd_owner = nullptr;835fd->wrect = Rect2i(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT);836}837fd->appid = appname;838fd->title = p_title;839fd->current_directory = p_current_directory;840fd->root = p_root;841fd->filename = p_filename;842fd->show_hidden = p_show_hidden;843fd->mode = p_mode;844fd->window_id = p_window_id;845fd->filters = p_filters;846fd->options = p_options;847fd->callback = p_callback;848fd->options_in_cb = p_options_in_cb;849fd->finished.clear();850fd->close_requested.clear();851852fd->listener_thread.start(DisplayServerWindows::_thread_fd_monitor, fd);853854file_dialogs.push_back(fd);855856return OK;857}858859void DisplayServerWindows::process_file_dialog_callbacks() {860MutexLock lock(file_dialog_mutex);861while (!pending_cbs.is_empty()) {862FileDialogCallback cb = pending_cbs.front()->get();863pending_cbs.pop_front();864865if (cb.opt_in_cb) {866Variant ret;867Callable::CallError ce;868const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options };869870cb.callback.callp(args, 4, ret, ce);871if (ce.error != Callable::CallError::CALL_OK) {872ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce)));873}874} else {875Variant ret;876Callable::CallError ce;877const Variant *args[3] = { &cb.status, &cb.files, &cb.index };878879cb.callback.callp(args, 3, ret, ce);880if (ce.error != Callable::CallError::CALL_OK) {881ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce)));882}883}884}885}886887void DisplayServerWindows::beep() const {888MessageBeep(MB_OK);889}890891void DisplayServerWindows::_mouse_update_mode() {892_THREAD_SAFE_METHOD_893894MouseMode wanted_mouse_mode = mouse_mode_override_enabled895? mouse_mode_override896: mouse_mode_base;897898if (mouse_mode == wanted_mouse_mode) {899// Already in the same mode; do nothing.900return;901}902903mouse_mode = wanted_mouse_mode;904905_set_mouse_mode_impl(wanted_mouse_mode);906}907908void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {909ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);910if (p_mode == mouse_mode_base) {911return;912}913mouse_mode_base = p_mode;914_mouse_update_mode();915}916917DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {918return mouse_mode;919}920921void DisplayServerWindows::mouse_set_mode_override(MouseMode p_mode) {922ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);923if (p_mode == mouse_mode_override) {924return;925}926mouse_mode_override = p_mode;927_mouse_update_mode();928}929930DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode_override() const {931return mouse_mode_override;932}933934void DisplayServerWindows::mouse_set_mode_override_enabled(bool p_override_enabled) {935if (p_override_enabled == mouse_mode_override_enabled) {936return;937}938mouse_mode_override_enabled = p_override_enabled;939_mouse_update_mode();940}941942bool DisplayServerWindows::mouse_is_mode_override_enabled() const {943return mouse_mode_override_enabled;944}945946void DisplayServerWindows::warp_mouse(const Point2i &p_position) {947_THREAD_SAFE_METHOD_948949WindowID window_id = _get_focused_window_or_popup();950951if (!windows.has(window_id)) {952return; // No focused window?953}954955if (mouse_mode == MOUSE_MODE_CAPTURED) {956old_x = p_position.x;957old_y = p_position.y;958} else {959POINT p;960p.x = p_position.x;961p.y = p_position.y;962ClientToScreen(windows[window_id].hWnd, &p);963964SetCursorPos(p.x, p.y);965}966}967968Point2i DisplayServerWindows::mouse_get_position() const {969POINT p;970GetCursorPos(&p);971return Point2i(p.x, p.y) - _get_screens_origin();972}973974BitField<MouseButtonMask> DisplayServerWindows::mouse_get_button_state() const {975BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;976977if (GetKeyState(VK_LBUTTON) & (1 << 15)) {978last_button_state.set_flag(MouseButtonMask::LEFT);979}980if (GetKeyState(VK_RBUTTON) & (1 << 15)) {981last_button_state.set_flag(MouseButtonMask::RIGHT);982}983if (GetKeyState(VK_MBUTTON) & (1 << 15)) {984last_button_state.set_flag(MouseButtonMask::MIDDLE);985}986if (GetKeyState(VK_XBUTTON1) & (1 << 15)) {987last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);988}989if (GetKeyState(VK_XBUTTON2) & (1 << 15)) {990last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);991}992993return last_button_state;994}995996void DisplayServerWindows::clipboard_set(const String &p_text) {997_THREAD_SAFE_METHOD_998999if (!windows.has(MAIN_WINDOW_ID)) {1000return;1001}10021003// Convert LF line endings to CRLF in clipboard content.1004// Otherwise, line endings won't be visible when pasted in other software.1005String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // Avoid \r\r\n.10061007if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {1008ERR_FAIL_MSG("Unable to open clipboard.");1009}1010EmptyClipboard();10111012Char16String utf16 = text.utf16();1013HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR));1014ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");10151016LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);1017memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR));1018GlobalUnlock(mem);10191020SetClipboardData(CF_UNICODETEXT, mem);10211022// Set the CF_TEXT version (not needed?).1023CharString utf8 = text.utf8();1024mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);1025ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");10261027LPTSTR ptr = (LPTSTR)GlobalLock(mem);1028memcpy(ptr, utf8.get_data(), utf8.length());1029ptr[utf8.length()] = 0;1030GlobalUnlock(mem);10311032SetClipboardData(CF_TEXT, mem);10331034CloseClipboard();1035}10361037String DisplayServerWindows::clipboard_get() const {1038_THREAD_SAFE_METHOD_10391040if (!windows.has(MAIN_WINDOW_ID)) {1041return String();1042}10431044String ret;1045if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {1046ERR_FAIL_V_MSG("", "Unable to open clipboard.");1047}10481049if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {1050HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);1051if (mem != nullptr) {1052LPWSTR ptr = (LPWSTR)GlobalLock(mem);1053if (ptr != nullptr) {1054ret = String::utf16((const char16_t *)ptr);1055GlobalUnlock(mem);1056}1057}10581059} else if (IsClipboardFormatAvailable(CF_TEXT)) {1060HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);1061if (mem != nullptr) {1062LPTSTR ptr = (LPTSTR)GlobalLock(mem);1063if (ptr != nullptr) {1064ret.append_utf8((const char *)ptr);1065GlobalUnlock(mem);1066}1067}1068}10691070CloseClipboard();10711072return ret;1073}10741075Ref<Image> DisplayServerWindows::clipboard_get_image() const {1076Ref<Image> image;1077if (!windows.has(last_focused_window)) {1078return image; // No focused window?1079}1080if (!OpenClipboard(windows[last_focused_window].hWnd)) {1081ERR_FAIL_V_MSG(image, "Unable to open clipboard.");1082}1083UINT png_format = RegisterClipboardFormatA("PNG");1084if (png_format && IsClipboardFormatAvailable(png_format)) {1085HANDLE png_handle = GetClipboardData(png_format);1086if (png_handle) {1087size_t png_size = GlobalSize(png_handle);1088uint8_t *png_data = (uint8_t *)GlobalLock(png_handle);1089image.instantiate();10901091PNGDriverCommon::png_to_image(png_data, png_size, false, image);10921093GlobalUnlock(png_handle);1094}1095} else if (IsClipboardFormatAvailable(CF_DIB)) {1096HGLOBAL mem = GetClipboardData(CF_DIB);1097if (mem != nullptr) {1098BITMAPINFO *ptr = static_cast<BITMAPINFO *>(GlobalLock(mem));10991100if (ptr != nullptr) {1101BITMAPINFOHEADER *info = &ptr->bmiHeader;1102void *dib_bits = (void *)(ptr->bmiColors);11031104// Draw DIB image to temporary DC surface and read it back as BGRA8.1105HDC dc = GetDC(nullptr);1106if (dc) {1107HDC hdc = CreateCompatibleDC(dc);1108if (hdc) {1109HBITMAP hbm = CreateCompatibleBitmap(dc, info->biWidth, std::abs(info->biHeight));1110if (hbm) {1111SelectObject(hdc, hbm);1112SetDIBitsToDevice(hdc, 0, 0, info->biWidth, std::abs(info->biHeight), 0, 0, 0, std::abs(info->biHeight), dib_bits, ptr, DIB_RGB_COLORS);11131114BITMAPINFO bmp_info = {};1115bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1116bmp_info.bmiHeader.biWidth = info->biWidth;1117bmp_info.bmiHeader.biHeight = -std::abs(info->biHeight);1118bmp_info.bmiHeader.biPlanes = 1;1119bmp_info.bmiHeader.biBitCount = 32;1120bmp_info.bmiHeader.biCompression = BI_RGB;11211122Vector<uint8_t> img_data;1123img_data.resize(info->biWidth * std::abs(info->biHeight) * 4);1124GetDIBits(hdc, hbm, 0, std::abs(info->biHeight), img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);11251126uint8_t *wr = (uint8_t *)img_data.ptrw();1127for (int i = 0; i < info->biWidth * std::abs(info->biHeight); i++) {1128SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1129if (info->biBitCount != 32) {1130wr[i * 4 + 3] = 255; // Set A to solid if it's not in the source image.1131}1132}1133image = Image::create_from_data(info->biWidth, std::abs(info->biHeight), false, Image::Format::FORMAT_RGBA8, img_data);11341135DeleteObject(hbm);1136}1137DeleteDC(hdc);1138}1139ReleaseDC(nullptr, dc);1140}1141GlobalUnlock(mem);1142}1143}1144}1145CloseClipboard();11461147return image;1148}11491150bool DisplayServerWindows::clipboard_has() const {1151return (IsClipboardFormatAvailable(CF_TEXT) ||1152IsClipboardFormatAvailable(CF_UNICODETEXT) ||1153IsClipboardFormatAvailable(CF_OEMTEXT));1154}11551156bool DisplayServerWindows::clipboard_has_image() const {1157UINT png_format = RegisterClipboardFormatA("PNG");1158return ((png_format && IsClipboardFormatAvailable(png_format)) || IsClipboardFormatAvailable(CF_DIB));1159}11601161typedef struct {1162int count;1163int screen;1164HMONITOR monitor;1165} EnumScreenData;11661167static BOOL CALLBACK _MonitorEnumProcPrim(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1168EnumScreenData *data = (EnumScreenData *)dwData;1169if ((lprcMonitor->left == 0) && (lprcMonitor->top == 0)) {1170data->screen = data->count;1171return FALSE;1172}11731174data->count++;1175return TRUE;1176}11771178static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1179EnumScreenData *data = (EnumScreenData *)dwData;1180if (data->monitor == hMonitor) {1181data->screen = data->count;1182}11831184data->count++;1185return TRUE;1186}11871188static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1189int *data = (int *)dwData;1190(*data)++;1191return TRUE;1192}11931194int DisplayServerWindows::get_screen_count() const {1195_THREAD_SAFE_METHOD_11961197int data = 0;1198EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcCount, (LPARAM)&data);1199return data;1200}12011202int DisplayServerWindows::get_primary_screen() const {1203EnumScreenData data = { 0, 0, nullptr };1204EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPrim, (LPARAM)&data);1205return data.screen;1206}12071208int DisplayServerWindows::get_keyboard_focus_screen() const {1209HWND hwnd = GetForegroundWindow();1210if (hwnd) {1211EnumScreenData data = { 0, 0, MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) };1212EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);1213return data.screen;1214} else {1215return get_primary_screen();1216}1217}12181219typedef struct {1220int count;1221int screen;1222Point2 pos;1223} EnumPosData;12241225static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1226EnumPosData *data = (EnumPosData *)dwData;1227if (data->count == data->screen) {1228data->pos.x = lprcMonitor->left;1229data->pos.y = lprcMonitor->top;1230}12311232data->count++;1233return TRUE;1234}12351236static BOOL CALLBACK _MonitorEnumProcOrigin(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1237EnumPosData *data = (EnumPosData *)dwData;1238data->pos = data->pos.min(Point2(lprcMonitor->left, lprcMonitor->top));12391240return TRUE;1241}12421243Point2i DisplayServerWindows::_get_screens_origin() const {1244_THREAD_SAFE_METHOD_12451246EnumPosData data = { 0, 0, Point2() };1247EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcOrigin, (LPARAM)&data);1248return data.pos;1249}12501251Point2i DisplayServerWindows::screen_get_position(int p_screen) const {1252_THREAD_SAFE_METHOD_12531254p_screen = _get_screen_index(p_screen);1255int screen_count = get_screen_count();1256ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());12571258EnumPosData data = { 0, p_screen, Point2() };1259EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPos, (LPARAM)&data);1260return data.pos - _get_screens_origin();1261}12621263typedef struct {1264int count;1265int screen;1266Size2 size;1267} EnumSizeData;12681269typedef struct {1270int count;1271int screen;1272Rect2i rect;1273} EnumRectData;12741275typedef struct {1276Vector<DISPLAYCONFIG_PATH_INFO> paths;1277Vector<DISPLAYCONFIG_MODE_INFO> modes;1278int count;1279int screen;1280float rate;1281} EnumRefreshRateData;12821283static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1284EnumSizeData *data = (EnumSizeData *)dwData;1285if (data->count == data->screen) {1286data->size.x = lprcMonitor->right - lprcMonitor->left;1287data->size.y = lprcMonitor->bottom - lprcMonitor->top;1288}12891290data->count++;1291return TRUE;1292}12931294Size2i DisplayServerWindows::screen_get_size(int p_screen) const {1295_THREAD_SAFE_METHOD_12961297p_screen = _get_screen_index(p_screen);1298int screen_count = get_screen_count();1299ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());13001301EnumSizeData data = { 0, p_screen, Size2() };1302EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcSize, (LPARAM)&data);1303return data.size;1304}13051306static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1307EnumRectData *data = (EnumRectData *)dwData;1308if (data->count == data->screen) {1309MONITORINFO minfo;1310memset(&minfo, 0, sizeof(MONITORINFO));1311minfo.cbSize = sizeof(MONITORINFO);1312GetMonitorInfoA(hMonitor, &minfo);13131314data->rect.position.x = minfo.rcWork.left;1315data->rect.position.y = minfo.rcWork.top;1316data->rect.size.x = minfo.rcWork.right - minfo.rcWork.left;1317data->rect.size.y = minfo.rcWork.bottom - minfo.rcWork.top;1318}13191320data->count++;1321return TRUE;1322}13231324static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1325EnumRefreshRateData *data = (EnumRefreshRateData *)dwData;1326if (data->count == data->screen) {1327MONITORINFOEXW minfo;1328memset(&minfo, 0, sizeof(minfo));1329minfo.cbSize = sizeof(minfo);1330GetMonitorInfoW(hMonitor, &minfo);13311332bool found = false;1333for (const DISPLAYCONFIG_PATH_INFO &path : data->paths) {1334DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name;1335memset(&source_name, 0, sizeof(source_name));1336source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;1337source_name.header.size = sizeof(source_name);1338source_name.header.adapterId = path.sourceInfo.adapterId;1339source_name.header.id = path.sourceInfo.id;1340if (DisplayConfigGetDeviceInfo(&source_name.header) == ERROR_SUCCESS) {1341if (wcscmp(minfo.szDevice, source_name.viewGdiDeviceName) == 0 && path.targetInfo.refreshRate.Numerator != 0 && path.targetInfo.refreshRate.Denominator != 0) {1342data->rate = (double)path.targetInfo.refreshRate.Numerator / (double)path.targetInfo.refreshRate.Denominator;1343found = true;1344break;1345}1346}1347}1348if (!found) {1349DEVMODEW dm;1350memset(&dm, 0, sizeof(dm));1351dm.dmSize = sizeof(dm);1352EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);13531354data->rate = dm.dmDisplayFrequency;1355}1356}13571358data->count++;1359return TRUE;1360}13611362Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const {1363_THREAD_SAFE_METHOD_13641365p_screen = _get_screen_index(p_screen);1366int screen_count = get_screen_count();1367ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());13681369EnumRectData data = { 0, p_screen, Rect2i() };1370EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcUsableSize, (LPARAM)&data);1371data.rect.position -= _get_screens_origin();1372return data.rect;1373}13741375typedef struct {1376int current_index;1377int screen;1378int dpi;1379} EnumDpiData;13801381static int QueryDpiForMonitor(HMONITOR hmon) {1382int dpiX = 96, dpiY = 96;13831384UINT x = 0, y = 0;1385if (hmon) {1386HRESULT hr = GetDpiForMonitor(hmon, MDT_DEFAULT, &x, &y);1387if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {1388dpiX = (int)x;1389dpiY = (int)y;1390}1391} else {1392static int overallX = 0, overallY = 0;1393if (overallX <= 0 || overallY <= 0) {1394HDC hdc = GetDC(nullptr);1395if (hdc) {1396overallX = GetDeviceCaps(hdc, LOGPIXELSX);1397overallY = GetDeviceCaps(hdc, LOGPIXELSY);1398ReleaseDC(nullptr, hdc);1399}1400}1401if (overallX > 0 && overallY > 0) {1402dpiX = overallX;1403dpiY = overallY;1404}1405}14061407return (dpiX + dpiY) / 2;1408}14091410static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1411EnumDpiData *data = (EnumDpiData *)dwData;14121413data->current_index++;14141415if (data->current_index == data->screen) {1416data->dpi = QueryDpiForMonitor(hMonitor);1417return FALSE;1418}1419return TRUE;1420}14211422int DisplayServerWindows::screen_get_dpi(int p_screen) const {1423_THREAD_SAFE_METHOD_14241425p_screen = _get_screen_index(p_screen);14261427EnumDpiData data = { -1, p_screen, 96 };1428EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);14291430ERR_FAIL_COND_V_MSG(data.current_index < p_screen, 96, vformat("Screen index %d out of range [0, %d].", p_screen, data.current_index));1431return data.dpi;1432}14331434Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const {1435Point2i pos = p_position + _get_screens_origin();14361437POINT p;1438p.x = pos.x;1439p.y = pos.y;1440LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p);14411442HDC dc = GetDC(nullptr);1443if (dc) {1444COLORREF col = GetPixel(dc, p.x, p.y);1445if (col != CLR_INVALID) {1446ReleaseDC(nullptr, dc);1447return Color(float(col & 0x000000FF) / 255.0f, float((col & 0x0000FF00) >> 8) / 255.0f, float((col & 0x00FF0000) >> 16) / 255.0f, 1.0f);1448}1449ReleaseDC(nullptr, dc);1450}14511452return Color();1453}14541455Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {1456p_screen = _get_screen_index(p_screen);1457int screen_count = get_screen_count();1458ERR_FAIL_INDEX_V(p_screen, screen_count, Ref<Image>());14591460Point2i pos = screen_get_position(p_screen) + _get_screens_origin();1461Size2i size = screen_get_size(p_screen);14621463POINT p1;1464p1.x = pos.x;1465p1.y = pos.y;14661467POINT p2;1468p2.x = pos.x + size.x;1469p2.y = pos.y + size.y;1470LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p1);1471LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p2);14721473Ref<Image> img;1474HDC dc = GetDC(nullptr);1475if (dc) {1476HDC hdc = CreateCompatibleDC(dc);1477int width = p2.x - p1.x;1478int height = p2.y - p1.y;1479if (hdc) {1480HBITMAP hbm = CreateCompatibleBitmap(dc, width, height);1481if (hbm) {1482SelectObject(hdc, hbm);1483BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY);14841485BITMAPINFO bmp_info = {};1486bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1487bmp_info.bmiHeader.biWidth = width;1488bmp_info.bmiHeader.biHeight = -height;1489bmp_info.bmiHeader.biPlanes = 1;1490bmp_info.bmiHeader.biBitCount = 32;1491bmp_info.bmiHeader.biCompression = BI_RGB;14921493Vector<uint8_t> img_data;1494img_data.resize(width * height * 4);1495GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);14961497uint8_t *wr = (uint8_t *)img_data.ptrw();1498for (int i = 0; i < width * height; i++) {1499SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1500}1501img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);15021503DeleteObject(hbm);1504}1505DeleteDC(hdc);1506}1507ReleaseDC(nullptr, dc);1508}15091510return img;1511}15121513Ref<Image> DisplayServerWindows::screen_get_image_rect(const Rect2i &p_rect) const {1514Point2i pos = p_rect.position + _get_screens_origin();1515Size2i size = p_rect.size;15161517POINT p1;1518p1.x = pos.x;1519p1.y = pos.y;15201521POINT p2;1522p2.x = pos.x + size.x;1523p2.y = pos.y + size.y;1524LogicalToPhysicalPointForPerMonitorDPI(0, &p1);1525LogicalToPhysicalPointForPerMonitorDPI(0, &p2);15261527Ref<Image> img;1528HDC dc = GetDC(0);1529if (dc) {1530HDC hdc = CreateCompatibleDC(dc);1531int width = p2.x - p1.x;1532int height = p2.y - p1.y;1533if (hdc) {1534HBITMAP hbm = CreateCompatibleBitmap(dc, width, height);1535if (hbm) {1536SelectObject(hdc, hbm);1537BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY);15381539BITMAPINFO bmp_info = {};1540bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1541bmp_info.bmiHeader.biWidth = width;1542bmp_info.bmiHeader.biHeight = -height;1543bmp_info.bmiHeader.biPlanes = 1;1544bmp_info.bmiHeader.biBitCount = 32;1545bmp_info.bmiHeader.biCompression = BI_RGB;15461547Vector<uint8_t> img_data;1548img_data.resize(width * height * 4);1549GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);15501551uint8_t *wr = (uint8_t *)img_data.ptrw();1552for (int i = 0; i < width * height; i++) {1553SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1554}1555img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);15561557DeleteObject(hbm);1558}1559DeleteDC(hdc);1560}1561ReleaseDC(NULL, dc);1562}15631564return img;1565}15661567float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {1568_THREAD_SAFE_METHOD_15691570p_screen = _get_screen_index(p_screen);1571int screen_count = get_screen_count();1572ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);15731574EnumRefreshRateData data = { Vector<DISPLAYCONFIG_PATH_INFO>(), Vector<DISPLAYCONFIG_MODE_INFO>(), 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK };15751576uint32_t path_count = 0;1577uint32_t mode_count = 0;1578if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) {1579data.paths.resize(path_count);1580data.modes.resize(mode_count);1581if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, data.paths.ptrw(), &mode_count, data.modes.ptrw(), nullptr) != ERROR_SUCCESS) {1582data.paths.clear();1583data.modes.clear();1584}1585}15861587EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data);1588return data.rate;1589}15901591void DisplayServerWindows::screen_set_keep_on(bool p_enable) {1592if (keep_screen_on == p_enable) {1593return;1594}15951596if (p_enable) {1597const String reason = "Godot Engine running with display/window/energy_saving/keep_screen_on = true";1598Char16String reason_utf16 = reason.utf16();1599REASON_CONTEXT context;1600context.Version = POWER_REQUEST_CONTEXT_VERSION;1601context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;1602context.Reason.SimpleReasonString = (LPWSTR)(reason_utf16.ptrw());1603power_request = PowerCreateRequest(&context);1604if (power_request == INVALID_HANDLE_VALUE) {1605print_error("Failed to enable screen_keep_on.");1606return;1607}1608if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired) == 0) {1609print_error("Failed to request system sleep override.");1610return;1611}1612if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired) == 0) {1613print_error("Failed to request display timeout override.");1614return;1615}1616} else {1617PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired);1618PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired);1619CloseHandle(power_request);1620power_request = nullptr;1621}16221623keep_screen_on = p_enable;1624}16251626bool DisplayServerWindows::screen_is_kept_on() const {1627return keep_screen_on;1628}16291630Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {1631_THREAD_SAFE_METHOD_16321633Vector<DisplayServer::WindowID> ret;1634for (const KeyValue<WindowID, WindowData> &E : windows) {1635ret.push_back(E.key);1636}1637return ret;1638}16391640DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(const Point2i &p_position) const {1641Point2i offset = _get_screens_origin();1642POINT p;1643p.x = p_position.x + offset.x;1644p.y = p_position.y + offset.y;1645HWND hwnd = WindowFromPoint(p);1646for (const KeyValue<WindowID, WindowData> &E : windows) {1647if (E.value.hWnd == hwnd) {1648return E.key;1649}1650}16511652return INVALID_WINDOW_ID;1653}16541655DisplayServer::WindowID DisplayServerWindows::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) {1656_THREAD_SAFE_METHOD_16571658bool no_redirection_bitmap = false;1659#ifdef DCOMP_ENABLED1660no_redirection_bitmap = OS::get_singleton()->is_layered_allowed() && rendering_driver == "d3d12";1661#endif16621663WindowID window_id = window_id_counter;1664Error err = _create_window(window_id, p_mode, p_flags, p_rect, p_exclusive, p_transient_parent, NULL, no_redirection_bitmap);1665ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Failed to create sub window.");1666++window_id_counter;16671668#ifdef RD_ENABLED1669if (rendering_context != nullptr) {1670_create_rendering_context_window(window_id, rendering_driver);1671}1672#endif1673#ifdef GLES3_ENABLED1674_create_gl_window(window_id);1675#endif16761677window_set_vsync_mode(p_vsync_mode, window_id);16781679WindowData &wd = windows[window_id];16801681if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {1682wd.resizable = false;1683}1684if (p_flags & WINDOW_FLAG_MINIMIZE_DISABLED_BIT) {1685wd.no_min_btn = true;1686}1687if (p_flags & WINDOW_FLAG_MAXIMIZE_DISABLED_BIT) {1688wd.no_max_btn = true;1689}1690if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {1691wd.borderless = true;1692}1693if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {1694wd.always_on_top = true;1695}1696if (p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT) {1697wd.sharp_corners = true;1698}1699if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {1700wd.no_focus = true;1701}1702if (p_flags & WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT) {1703wd.mpass = true;1704}1705if (p_flags & WINDOW_FLAG_EXCLUDE_FROM_CAPTURE_BIT) {1706wd.hide_from_capture = true;1707if (os_ver.dwBuildNumber >= 19041) {1708SetWindowDisplayAffinity(wd.hWnd, WDA_EXCLUDEFROMCAPTURE);1709} else {1710SetWindowDisplayAffinity(wd.hWnd, WDA_MONITOR);1711}1712}1713if (p_flags & WINDOW_FLAG_POPUP_BIT) {1714wd.is_popup = true;1715}1716if (p_flags & WINDOW_FLAG_TRANSPARENT_BIT) {1717if (OS::get_singleton()->is_layered_allowed()) {1718DWM_BLURBEHIND bb;1719ZeroMemory(&bb, sizeof(bb));1720HRGN hRgn = CreateRectRgn(0, 0, -1, -1);1721bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;1722bb.hRgnBlur = hRgn;1723bb.fEnable = TRUE;1724DwmEnableBlurBehindWindow(wd.hWnd, &bb);1725}17261727wd.layered_window = true;1728}17291730// Inherit icons from MAIN_WINDOW for all sub windows.1731HICON mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_SMALL, 0);1732if (mainwindow_icon) {1733SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);1734}1735mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_BIG, 0);1736if (mainwindow_icon) {1737SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);1738}1739#ifdef RD_ENABLED1740if (rendering_device) {1741rendering_device->screen_create(window_id);1742}1743#endif1744return window_id;1745}17461747bool DisplayServerWindows::_is_always_on_top_recursive(WindowID p_window) const {1748ERR_FAIL_COND_V(!windows.has(p_window), false);17491750const WindowData &wd = windows[p_window];1751if (wd.always_on_top) {1752return true;1753}17541755if (wd.transient_parent != INVALID_WINDOW_ID) {1756return _is_always_on_top_recursive(wd.transient_parent);1757}17581759return false;1760}17611762void DisplayServerWindows::show_window(WindowID p_id) {1763ERR_FAIL_COND(!windows.has(p_id));17641765WindowData &wd = windows[p_id];1766popup_open(p_id);17671768if (p_id != MAIN_WINDOW_ID) {1769_update_window_style(p_id);1770}1771wd.initialized = true;17721773if (wd.maximized) {1774ShowWindow(wd.hWnd, SW_SHOWMAXIMIZED);1775SetForegroundWindow(wd.hWnd); // Slightly higher priority.1776SetFocus(wd.hWnd); // Set keyboard focus.1777} else if (wd.minimized) {1778ShowWindow(wd.hWnd, SW_SHOWMINIMIZED);1779} else if (wd.no_focus) {1780// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow1781ShowWindow(wd.hWnd, SW_SHOWNA);1782} else if (wd.is_popup) {1783ShowWindow(wd.hWnd, SW_SHOWNA);1784SetFocus(wd.hWnd); // Set keyboard focus.1785} else {1786ShowWindow(wd.hWnd, SW_SHOW);1787SetForegroundWindow(wd.hWnd); // Slightly higher priority.1788SetFocus(wd.hWnd); // Set keyboard focus.1789}1790if (_is_always_on_top_recursive(p_id)) {1791SetWindowPos(wd.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));1792}1793}17941795void DisplayServerWindows::delete_sub_window(WindowID p_window) {1796_THREAD_SAFE_METHOD_17971798ERR_FAIL_COND(!windows.has(p_window));1799ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted.");18001801popup_close(p_window);18021803WindowData &wd = windows[p_window];18041805while (wd.transient_children.size()) {1806window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);1807}18081809if (wd.transient_parent != INVALID_WINDOW_ID) {1810window_set_transient(p_window, INVALID_WINDOW_ID);1811}18121813#ifdef RD_ENABLED1814if (rendering_device) {1815rendering_device->screen_free(p_window);1816}18171818if (rendering_context) {1819rendering_context->window_destroy(p_window);1820}1821#endif1822#ifdef GLES3_ENABLED1823if (gl_manager_angle) {1824gl_manager_angle->window_destroy(p_window);1825}1826if (gl_manager_native) {1827gl_manager_native->window_destroy(p_window);1828}1829#endif18301831_destroy_window(p_window);18321833if (last_focused_window == p_window) {1834last_focused_window = INVALID_WINDOW_ID;1835}1836}18371838void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) {1839#if defined(GLES3_ENABLED)1840if (gl_manager_angle) {1841gl_manager_angle->window_make_current(p_window_id);1842}1843if (gl_manager_native) {1844gl_manager_native->window_make_current(p_window_id);1845}1846#endif1847}18481849int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {1850ERR_FAIL_COND_V(!windows.has(p_window), 0);1851switch (p_handle_type) {1852case DISPLAY_HANDLE: {1853return 0; // Not supported.1854}1855case WINDOW_HANDLE: {1856return (int64_t)windows[p_window].hWnd;1857}1858#if defined(GLES3_ENABLED)1859case WINDOW_VIEW: {1860if (gl_manager_native) {1861return (int64_t)gl_manager_native->get_hdc(p_window);1862} else {1863return (int64_t)GetDC(windows[p_window].hWnd);1864}1865}1866case OPENGL_CONTEXT: {1867if (gl_manager_native) {1868return (int64_t)gl_manager_native->get_hglrc(p_window);1869}1870if (gl_manager_angle) {1871return (int64_t)gl_manager_angle->get_context(p_window);1872}1873return 0;1874}1875case EGL_DISPLAY: {1876if (gl_manager_angle) {1877return (int64_t)gl_manager_angle->get_display(p_window);1878}1879return 0;1880}1881case EGL_CONFIG: {1882if (gl_manager_angle) {1883return (int64_t)gl_manager_angle->get_config(p_window);1884}1885return 0;1886}1887#endif1888default: {1889return 0;1890}1891}1892}18931894void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {1895_THREAD_SAFE_METHOD_18961897ERR_FAIL_COND(!windows.has(p_window));1898windows[p_window].instance_id = p_instance;1899}19001901ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window) const {1902_THREAD_SAFE_METHOD_19031904ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());1905return windows[p_window].instance_id;1906}19071908void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {1909_THREAD_SAFE_METHOD_19101911ERR_FAIL_COND(!windows.has(p_window));1912windows[p_window].rect_changed_callback = p_callable;1913}19141915void DisplayServerWindows::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {1916_THREAD_SAFE_METHOD_19171918ERR_FAIL_COND(!windows.has(p_window));1919windows[p_window].event_callback = p_callable;1920}19211922void DisplayServerWindows::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {1923_THREAD_SAFE_METHOD_19241925ERR_FAIL_COND(!windows.has(p_window));1926windows[p_window].input_event_callback = p_callable;1927}19281929void DisplayServerWindows::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {1930_THREAD_SAFE_METHOD_19311932ERR_FAIL_COND(!windows.has(p_window));1933windows[p_window].input_text_callback = p_callable;1934}19351936void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {1937_THREAD_SAFE_METHOD_19381939ERR_FAIL_COND(!windows.has(p_window));1940WindowData &window_data = windows[p_window];19411942window_data.drop_files_callback = p_callable;19431944if (window_data.drop_target == nullptr) {1945window_data.drop_target = memnew(DropTargetWindows(&window_data));1946ERR_FAIL_COND(RegisterDragDrop(window_data.hWnd, window_data.drop_target) != S_OK);1947}1948}19491950void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) {1951_THREAD_SAFE_METHOD_19521953ERR_FAIL_COND(!windows.has(p_window));1954SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data()));1955}19561957Size2i DisplayServerWindows::window_get_title_size(const String &p_title, WindowID p_window) const {1958_THREAD_SAFE_METHOD_19591960Size2i size;1961ERR_FAIL_COND_V(!windows.has(p_window), size);19621963const WindowData &wd = windows[p_window];1964if (wd.fullscreen || wd.minimized || wd.borderless) {1965return size;1966}19671968HDC hdc = GetDCEx(wd.hWnd, nullptr, DCX_WINDOW);1969if (hdc) {1970Char16String s = p_title.utf16();1971SIZE text_size;1972if (GetTextExtentPoint32W(hdc, (LPCWSTR)(s.get_data()), s.length(), &text_size)) {1973size.x = text_size.cx;1974size.y = text_size.cy;1975}19761977ReleaseDC(wd.hWnd, hdc);1978}1979RECT rect;1980if (DwmGetWindowAttribute(wd.hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rect, sizeof(RECT)) == S_OK) {1981if (rect.right - rect.left > 0) {1982ClientToScreen(wd.hWnd, (POINT *)&rect.left);1983ClientToScreen(wd.hWnd, (POINT *)&rect.right);19841985PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.left);1986PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.right);19871988size.x += (rect.right - rect.left);1989size.y = MAX(size.y, rect.bottom - rect.top);1990}1991}1992if (icon_big) {1993size.x += 32;1994} else {1995size.x += 16;1996}1997return size;1998}19992000void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {2001_THREAD_SAFE_METHOD_20022003ERR_FAIL_COND(!windows.has(p_window));2004windows[p_window].mpath = p_region;2005_update_window_mouse_passthrough(p_window);2006}20072008void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {2009ERR_FAIL_COND(!windows.has(p_window));20102011const WindowData &wd = windows[p_window];2012bool clip_pixel = (wd.multiwindow_fs || (wd.borderless && wd.maximized));2013bool pass_set = (wd.mpath.size() > 0);2014if (!clip_pixel && !pass_set) {2015SetWindowRgn(wd.hWnd, nullptr, TRUE);2016} else {2017HRGN region = nullptr;2018if (pass_set) {2019Vector<POINT> points;2020points.resize(wd.mpath.size());2021POINT *points_ptr = points.ptrw();2022for (int i = 0; i < wd.mpath.size(); i++) {2023if (wd.borderless) {2024points_ptr[i].x = wd.mpath[i].x;2025points_ptr[i].y = wd.mpath[i].y;2026} else {2027points_ptr[i].x = wd.mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME);2028points_ptr[i].y = wd.mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);2029}2030}2031region = CreatePolygonRgn(points.ptr(), points.size(), ALTERNATE);2032} else {2033region = CreateRectRgn(0, 0, wd.width, wd.height);2034}2035if (clip_pixel) {2036HRGN region_clip = CreateRectRgn(0, 0, wd.width, wd.height);2037CombineRgn(region, region, region_clip, RGN_AND);2038DeleteObject(region_clip);2039}2040SetWindowRgn(wd.hWnd, region, FALSE);2041}2042}20432044int DisplayServerWindows::window_get_current_screen(WindowID p_window) const {2045_THREAD_SAFE_METHOD_20462047ERR_FAIL_COND_V(!windows.has(p_window), INVALID_SCREEN);20482049EnumScreenData data = { 0, 0, MonitorFromWindow(windows[p_window].hWnd, MONITOR_DEFAULTTONEAREST) };2050EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);2051return data.screen;2052}20532054void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) {2055_THREAD_SAFE_METHOD_20562057ERR_FAIL_COND(!windows.has(p_window));20582059p_screen = _get_screen_index(p_screen);2060int screen_count = get_screen_count();2061ERR_FAIL_INDEX(p_screen, screen_count);20622063if (window_get_current_screen(p_window) == p_screen) {2064return;2065}2066const WindowData &wd = windows[p_window];20672068if (wd.parent_hwnd) {2069print_line("Embedded window can't be moved to another screen.");2070return;2071}2072if (wd.fullscreen) {2073Point2 pos = screen_get_position(p_screen) + _get_screens_origin();2074Size2 size = screen_get_size(p_screen);2075Vector2i off = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? _get_screen_expand_offset(p_screen) : Vector2i();20762077MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off.x, size.height + off.y, TRUE);2078} else if (wd.maximized) {2079Point2 pos = screen_get_position(p_screen) + _get_screens_origin();2080Size2 size = screen_get_size(p_screen);2081Vector2i off = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? _get_screen_expand_offset(p_screen) : Vector2i();20822083ShowWindow(wd.hWnd, SW_RESTORE);2084MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off.x, size.height + off.y, TRUE);2085ShowWindow(wd.hWnd, SW_MAXIMIZE);2086} else {2087Rect2i srect = screen_get_usable_rect(p_screen);2088Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));2089Size2i wsize = window_get_size(p_window);2090wpos += srect.position;20912092wpos = wpos.clamp(srect.position, srect.position + srect.size - wsize / 3);2093window_set_position(wpos, p_window);2094}2095}20962097Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {2098_THREAD_SAFE_METHOD_20992100ERR_FAIL_COND_V(!windows.has(p_window), Point2i());2101const WindowData &wd = windows[p_window];21022103if (wd.minimized) {2104return wd.last_pos;2105}21062107POINT point;2108point.x = 0;2109point.y = 0;21102111ClientToScreen(wd.hWnd, &point);21122113return Point2i(point.x, point.y) - _get_screens_origin();2114}21152116Point2i DisplayServerWindows::window_get_position_with_decorations(WindowID p_window) const {2117_THREAD_SAFE_METHOD_21182119ERR_FAIL_COND_V(!windows.has(p_window), Point2i());2120const WindowData &wd = windows[p_window];21212122if (wd.minimized) {2123return wd.last_pos;2124}21252126RECT r;2127if (GetWindowRect(wd.hWnd, &r)) {2128return Point2i(r.left, r.top) - _get_screens_origin();2129}21302131return Point2i();2132}21332134void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) {2135ERR_FAIL_COND(!windows.has(p_window));21362137POINT mouse_pos;2138if (GetCursorPos(&mouse_pos) && ScreenToClient(windows[p_window].hWnd, &mouse_pos)) {2139if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= windows[p_window].width && mouse_pos.y <= windows[p_window].height) {2140old_x = mouse_pos.x;2141old_y = mouse_pos.y;2142old_invalid = false;2143Input::get_singleton()->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y));2144}2145}2146}21472148void DisplayServerWindows::window_set_position(const Point2i &p_position, WindowID p_window) {2149_THREAD_SAFE_METHOD_21502151ERR_FAIL_COND(!windows.has(p_window));2152WindowData &wd = windows[p_window];21532154if (wd.parent_hwnd) {2155print_line("Embedded window can't be moved.");2156return;2157}21582159if (wd.fullscreen || wd.maximized) {2160return;2161}21622163Point2i offset = _get_screens_origin();21642165RECT rc;2166rc.left = p_position.x + offset.x;2167rc.right = p_position.x + wd.width + offset.x;2168rc.bottom = p_position.y + wd.height + offset.y;2169rc.top = p_position.y + offset.y;21702171const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);2172const DWORD exStyle = GetWindowLongPtr(wd.hWnd, GWL_EXSTYLE);21732174AdjustWindowRectEx(&rc, style, false, exStyle);2175MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);21762177wd.last_pos = p_position;2178_update_real_mouse_position(p_window);2179}21802181void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclusive) {2182_THREAD_SAFE_METHOD_2183ERR_FAIL_COND(!windows.has(p_window));2184WindowData &wd = windows[p_window];2185if (wd.exclusive != p_exclusive) {2186wd.exclusive = p_exclusive;2187if (wd.transient_parent != INVALID_WINDOW_ID) {2188if (wd.exclusive) {2189WindowData &wd_parent = windows[wd.transient_parent];2190SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);2191} else {2192SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);2193}2194}2195}2196}21972198void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) {2199_THREAD_SAFE_METHOD_22002201ERR_FAIL_COND(p_window == p_parent);2202ERR_FAIL_COND(!windows.has(p_window));22032204WindowData &wd_window = windows[p_window];22052206ERR_FAIL_COND(wd_window.transient_parent == p_parent);2207ERR_FAIL_COND_MSG(wd_window.always_on_top, "Windows with the 'on top' can't become transient.");22082209if (p_parent == INVALID_WINDOW_ID) {2210// Remove transient.22112212ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);2213ERR_FAIL_COND(!windows.has(wd_window.transient_parent));22142215WindowData &wd_parent = windows[wd_window.transient_parent];22162217wd_window.transient_parent = INVALID_WINDOW_ID;2218wd_parent.transient_children.erase(p_window);22192220if (wd_window.exclusive) {2221SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);2222}2223} else {2224ERR_FAIL_COND(!windows.has(p_parent));2225ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");2226WindowData &wd_parent = windows[p_parent];22272228wd_window.transient_parent = p_parent;2229wd_parent.transient_children.insert(p_window);22302231if (wd_window.exclusive) {2232SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);2233}2234}2235}22362237void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_window) {2238_THREAD_SAFE_METHOD_22392240ERR_FAIL_COND(!windows.has(p_window));2241WindowData &wd = windows[p_window];22422243if (wd.parent_hwnd) {2244print_line("Embedded windows can't have a maximum size.");2245return;2246}22472248if ((p_size != Size2()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {2249ERR_PRINT("Maximum window size can't be smaller than minimum window size!");2250return;2251}2252wd.max_size = p_size;2253}22542255Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const {2256_THREAD_SAFE_METHOD_22572258ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2259const WindowData &wd = windows[p_window];2260return wd.max_size;2261}22622263void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_window) {2264_THREAD_SAFE_METHOD_22652266ERR_FAIL_COND(!windows.has(p_window));2267WindowData &wd = windows[p_window];22682269if (wd.parent_hwnd) {2270print_line("Embedded windows can't have a minimum size.");2271return;2272}22732274if ((p_size != Size2()) && (wd.max_size != Size2()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {2275ERR_PRINT("Minimum window size can't be larger than maximum window size!");2276return;2277}2278wd.min_size = p_size;2279}22802281Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const {2282_THREAD_SAFE_METHOD_22832284ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2285const WindowData &wd = windows[p_window];2286return wd.min_size;2287}22882289void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_window) {2290_THREAD_SAFE_METHOD_22912292ERR_FAIL_COND(!windows.has(p_window));2293WindowData &wd = windows[p_window];22942295if (wd.parent_hwnd) {2296print_line("Embedded window can't be resized.");2297return;2298}22992300if (wd.fullscreen || wd.maximized) {2301return;2302}23032304int w = p_size.width;2305int h = p_size.height;2306RECT rect;2307GetWindowRect(wd.hWnd, &rect);23082309if (!wd.borderless) {2310RECT crect;2311GetClientRect(wd.hWnd, &crect);23122313w += (rect.right - rect.left) - (crect.right - crect.left);2314h += (rect.bottom - rect.top) - (crect.bottom - crect.top);2315}23162317MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE);2318}23192320Size2i DisplayServerWindows::window_get_size(WindowID p_window) const {2321_THREAD_SAFE_METHOD_23222323ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2324const WindowData &wd = windows[p_window];23252326// GetClientRect() returns a zero rect for a minimized window, so we need to get the size in another way.2327if (wd.minimized) {2328return Size2(wd.width, wd.height);2329}23302331RECT r;2332if (GetClientRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.2333Vector2i off = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? _get_screen_expand_offset(window_get_current_screen(p_window)) : Vector2i();2334return Size2(r.right - r.left - off.x, r.bottom - r.top - off.y);2335}2336return Size2();2337}23382339Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window) const {2340_THREAD_SAFE_METHOD_23412342ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2343const WindowData &wd = windows[p_window];23442345// GetWindowRect() returns a zero rect for a minimized window, so we need to get the size in another way.2346if (wd.minimized) {2347return Size2(wd.width_with_decorations, wd.height_with_decorations);2348}23492350RECT r;2351if (GetWindowRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.2352Vector2i off = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? _get_screen_expand_offset(window_get_current_screen(p_window)) : Vector2i();2353return Size2(r.right - r.left - off.x, r.bottom - r.top - off.y);2354}2355return Size2();2356}23572358void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_no_min_btn, bool p_no_max_btn, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, bool p_embed_child, bool p_no_redirection_bitmap, DWORD &r_style, DWORD &r_style_ex) {2359// Windows docs for window styles:2360// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles2361// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles23622363r_style = 0;2364r_style_ex = WS_EX_WINDOWEDGE;2365if (p_main_window) {2366// When embedded, we don't want the window to have WS_EX_APPWINDOW because it will2367// show the embedded process in the taskbar and Alt-Tab.2368if (!p_embed_child) {2369r_style_ex |= WS_EX_APPWINDOW;2370}2371if (p_initialized) {2372r_style |= WS_VISIBLE;2373}2374}23752376if (p_embed_child) {2377r_style |= WS_POPUP;2378} else if (p_fullscreen || p_borderless) {2379r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past.2380if (p_minimized) {2381r_style |= WS_MINIMIZE;2382} else if (p_maximized) {2383r_style |= WS_MAXIMIZE;2384}2385if (!p_fullscreen) {2386r_style |= WS_SYSMENU;2387if (!p_no_min_btn) {2388r_style |= WS_MINIMIZEBOX;2389}2390if (!p_no_max_btn) {2391r_style |= WS_MAXIMIZEBOX;2392}2393}2394} else {2395if (p_resizable) {2396if (p_minimized) {2397r_style = WS_OVERLAPPEDWINDOW | WS_MINIMIZE;2398} else if (p_maximized) {2399r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE;2400} else {2401r_style = WS_OVERLAPPEDWINDOW;2402}2403if (p_no_min_btn) {2404r_style &= ~WS_MINIMIZEBOX;2405}2406if (p_no_max_btn) {2407r_style &= ~WS_MAXIMIZEBOX;2408}2409} else {2410if (p_minimized) {2411r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZE;2412} else {2413r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;2414}2415if (!p_no_min_btn) {2416r_style |= WS_MINIMIZEBOX;2417}2418if (!p_no_max_btn) {2419r_style |= WS_MAXIMIZEBOX;2420}2421}2422}24232424if (p_no_activate_focus && !p_embed_child) {2425r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;2426}24272428if (!p_borderless && !p_no_activate_focus && p_initialized) {2429r_style |= WS_VISIBLE;2430}24312432r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;2433r_style_ex |= WS_EX_ACCEPTFILES;24342435if (p_no_redirection_bitmap) {2436r_style_ex |= WS_EX_NOREDIRECTIONBITMAP;2437}2438}24392440void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint) {2441_THREAD_SAFE_METHOD_24422443ERR_FAIL_COND(!windows.has(p_window));2444WindowData &wd = windows[p_window];24452446DWORD style = 0;2447DWORD style_ex = 0;24482449_get_window_style(p_window == MAIN_WINDOW_ID, wd.initialized, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.no_min_btn, wd.no_max_btn, wd.minimized, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, wd.parent_hwnd, wd.no_redirection_bitmap, style, style_ex);24502451SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);2452SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);24532454if (icon_big && !icon_small) {2455SendMessage(wd.hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);2456SendMessage(wd.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_big);2457} else {2458if (icon_big) {2459SendMessage(wd.hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);2460}2461if (icon_small) {2462SendMessage(wd.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);2463}2464}24652466SetWindowPos(wd.hWnd, _is_always_on_top_recursive(p_window) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));24672468if (p_repaint) {2469RECT rect;2470GetWindowRect(wd.hWnd, &rect);2471Vector2i off = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? _get_screen_expand_offset(window_get_current_screen(p_window)) : Vector2i();2472MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left + off.x, rect.bottom - rect.top - off.y, TRUE);2473}2474}24752476void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) {2477_THREAD_SAFE_METHOD_24782479ERR_FAIL_COND(!windows.has(p_window));2480WindowData &wd = windows[p_window];24812482if (p_mode != WINDOW_MODE_WINDOWED && wd.parent_hwnd) {2483print_line("Embedded window only supports Windowed mode.");2484return;2485}24862487bool was_fullscreen = wd.fullscreen;2488wd.was_fullscreen_pre_min = false;24892490if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {2491int cs = window_get_current_screen(p_window);2492Rect2i full = Rect2i(screen_get_position(cs), screen_get_size(cs));2493Rect2i usable = screen_get_usable_rect(cs);2494if (full == usable) {2495p_mode = WINDOW_MODE_FULLSCREEN;2496}2497}24982499if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {2500RECT rect;25012502wd.fullscreen = false;2503wd.multiwindow_fs = false;25042505// Restore previous maximized state.2506wd.maximized = wd.was_maximized_pre_fs;25072508_update_window_style(p_window, false);25092510// Restore window rect after exiting fullscreen.2511if (wd.pre_fs_valid) {2512rect = wd.pre_fs_rect;2513} else {2514rect.left = 0;2515rect.right = wd.width;2516rect.top = 0;2517rect.bottom = wd.height;2518}25192520ShowWindow(wd.hWnd, SW_RESTORE);2521MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);25222523if (restore_mouse_trails > 1) {2524SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);2525restore_mouse_trails = 0;2526}2527}25282529if ((wd.maximized || wd.was_maximized_pre_fs) && wd.borderless && p_mode != WINDOW_MODE_MINIMIZED && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {2530RECT rect;2531if (wd.pre_fs_valid) {2532rect = wd.pre_fs_rect;2533} else {2534rect.left = 0;2535rect.right = wd.width;2536rect.top = 0;2537rect.bottom = wd.height;2538}25392540ShowWindow(wd.hWnd, SW_RESTORE);2541MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);2542}25432544if (p_mode == WINDOW_MODE_WINDOWED) {2545ShowWindow(wd.hWnd, SW_NORMAL);2546wd.maximized = false;2547wd.minimized = false;2548}25492550if (p_mode == WINDOW_MODE_MAXIMIZED && !wd.borderless) {2551ShowWindow(wd.hWnd, SW_MAXIMIZE);2552wd.maximized = true;2553wd.minimized = false;2554}25552556if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {2557if (!was_fullscreen && !(wd.maximized && wd.borderless)) {2558// Save non-fullscreen rect before entering fullscreen.2559GetWindowRect(wd.hWnd, &wd.pre_fs_rect);2560wd.pre_fs_valid = true;2561}2562ShowWindow(wd.hWnd, SW_NORMAL);2563wd.maximized = true;2564wd.minimized = false;25652566int cs = window_get_current_screen(p_window);2567Rect2i usable = screen_get_usable_rect(cs);2568Point2 pos = usable.position + _get_screens_origin();2569Size2 size = usable.size;2570MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);2571}25722573if (p_mode == WINDOW_MODE_MINIMIZED) {2574ShowWindow(wd.hWnd, SW_MINIMIZE);2575wd.maximized = false;2576wd.minimized = true;2577wd.was_fullscreen_pre_min = was_fullscreen;2578}25792580if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {2581wd.multiwindow_fs = false;2582} else if (p_mode == WINDOW_MODE_FULLSCREEN) {2583wd.multiwindow_fs = true;2584}2585_update_window_style(p_window, false);25862587if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) {2588if (wd.minimized || wd.maximized) {2589ShowWindow(wd.hWnd, SW_RESTORE);2590}25912592// Save previous maximized stare.2593wd.was_maximized_pre_fs = wd.maximized;25942595if (!was_fullscreen && !(wd.maximized && wd.borderless)) {2596// Save non-fullscreen rect before entering fullscreen.2597GetWindowRect(wd.hWnd, &wd.pre_fs_rect);2598wd.pre_fs_valid = true;2599}26002601int cs = window_get_current_screen(p_window);2602Point2 pos = screen_get_position(cs) + _get_screens_origin();2603Size2 size = screen_get_size(cs);26042605wd.fullscreen = true;2606wd.maximized = false;2607wd.minimized = false;26082609_update_window_style(p_window, false);26102611Vector2i off = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? _get_screen_expand_offset(cs) : Vector2i();2612MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off.x, size.height + off.y, TRUE);26132614// If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode.2615// Save number of trails so we can restore when exiting, then turn off mouse trails2616SystemParametersInfoA(SPI_GETMOUSETRAILS, 0, &restore_mouse_trails, 0);2617if (restore_mouse_trails > 1) {2618SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, nullptr, 0);2619}2620}2621_update_window_mouse_passthrough(p_window);2622}26232624DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {2625_THREAD_SAFE_METHOD_26262627ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);2628const WindowData &wd = windows[p_window];26292630if (wd.fullscreen) {2631if (wd.multiwindow_fs) {2632return WINDOW_MODE_FULLSCREEN;2633} else {2634return WINDOW_MODE_EXCLUSIVE_FULLSCREEN;2635}2636} else if (wd.minimized) {2637return WINDOW_MODE_MINIMIZED;2638} else if (wd.maximized) {2639return WINDOW_MODE_MAXIMIZED;2640} else {2641return WINDOW_MODE_WINDOWED;2642}2643}26442645bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const {2646_THREAD_SAFE_METHOD_26472648ERR_FAIL_COND_V(!windows.has(p_window), false);2649const WindowData &wd = windows[p_window];26502651const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);2652return (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX;2653}26542655void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {2656_THREAD_SAFE_METHOD_26572658ERR_FAIL_COND(!windows.has(p_window));2659WindowData &wd = windows[p_window];2660switch (p_flag) {2661case WINDOW_FLAG_MINIMIZE_DISABLED: {2662wd.no_min_btn = p_enabled;2663_update_window_style(p_window);2664} break;2665case WINDOW_FLAG_MAXIMIZE_DISABLED: {2666wd.no_max_btn = p_enabled;2667_update_window_style(p_window);2668} break;2669case WINDOW_FLAG_RESIZE_DISABLED: {2670if (p_enabled && wd.parent_hwnd) {2671print_line("Embedded window resize can't be disabled.");2672return;2673}2674wd.resizable = !p_enabled;2675_update_window_style(p_window);2676} break;2677case WINDOW_FLAG_BORDERLESS: {2678wd.borderless = p_enabled;2679if (wd.fullscreen) {2680return;2681}2682_update_window_mouse_passthrough(p_window);2683_update_window_style(p_window);2684ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.2685} break;2686case WINDOW_FLAG_ALWAYS_ON_TOP: {2687ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top.");2688if (p_enabled && wd.parent_hwnd) {2689print_line("Embedded window can't become on top.");2690return;2691}2692wd.always_on_top = p_enabled;2693_update_window_style(p_window);2694} break;2695case WINDOW_FLAG_SHARP_CORNERS: {2696wd.sharp_corners = p_enabled;2697DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;2698::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));2699_update_window_style(p_window);2700} break;2701case WINDOW_FLAG_TRANSPARENT: {2702if (p_enabled) {2703// Enable per-pixel alpha.2704if (OS::get_singleton()->is_layered_allowed()) {2705DWM_BLURBEHIND bb;2706ZeroMemory(&bb, sizeof(bb));2707HRGN hRgn = CreateRectRgn(0, 0, -1, -1);2708bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;2709bb.hRgnBlur = hRgn;2710bb.fEnable = TRUE;2711DwmEnableBlurBehindWindow(wd.hWnd, &bb);2712}2713wd.layered_window = true;2714} else {2715// Disable per-pixel alpha.2716wd.layered_window = false;2717if (OS::get_singleton()->is_layered_allowed()) {2718DWM_BLURBEHIND bb;2719ZeroMemory(&bb, sizeof(bb));2720HRGN hRgn = CreateRectRgn(0, 0, -1, -1);2721bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;2722bb.hRgnBlur = hRgn;2723bb.fEnable = FALSE;2724DwmEnableBlurBehindWindow(wd.hWnd, &bb);2725}2726}2727} break;2728case WINDOW_FLAG_NO_FOCUS: {2729wd.no_focus = p_enabled;2730_update_window_style(p_window);2731} break;2732case WINDOW_FLAG_MOUSE_PASSTHROUGH: {2733wd.mpass = p_enabled;2734} break;2735case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {2736wd.hide_from_capture = p_enabled;2737if (p_enabled) {2738if (os_ver.dwBuildNumber >= 19041) {2739SetWindowDisplayAffinity(wd.hWnd, WDA_EXCLUDEFROMCAPTURE);2740} else {2741SetWindowDisplayAffinity(wd.hWnd, WDA_MONITOR);2742}2743} else {2744SetWindowDisplayAffinity(wd.hWnd, WDA_NONE);2745}2746} break;2747case WINDOW_FLAG_POPUP: {2748ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");2749ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");2750if (p_enabled && wd.parent_hwnd) {2751print_line("Embedded window can't be popup.");2752return;2753}2754wd.is_popup = p_enabled;2755} break;2756default:2757break;2758}2759}27602761bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window) const {2762_THREAD_SAFE_METHOD_27632764ERR_FAIL_COND_V(!windows.has(p_window), false);2765const WindowData &wd = windows[p_window];2766switch (p_flag) {2767case WINDOW_FLAG_MAXIMIZE_DISABLED: {2768return wd.no_max_btn;2769} break;2770case WINDOW_FLAG_MINIMIZE_DISABLED: {2771return wd.no_min_btn;2772} break;2773case WINDOW_FLAG_RESIZE_DISABLED: {2774return !wd.resizable;2775} break;2776case WINDOW_FLAG_BORDERLESS: {2777return wd.borderless;2778} break;2779case WINDOW_FLAG_ALWAYS_ON_TOP: {2780return wd.always_on_top;2781} break;2782case WINDOW_FLAG_SHARP_CORNERS: {2783return wd.sharp_corners;2784} break;2785case WINDOW_FLAG_TRANSPARENT: {2786return wd.layered_window;2787} break;2788case WINDOW_FLAG_NO_FOCUS: {2789return wd.no_focus;2790} break;2791case WINDOW_FLAG_MOUSE_PASSTHROUGH: {2792return wd.mpass;2793} break;2794case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {2795return wd.hide_from_capture;2796} break;2797case WINDOW_FLAG_POPUP: {2798return wd.is_popup;2799} break;2800default:2801break;2802}28032804return false;2805}28062807void DisplayServerWindows::window_request_attention(WindowID p_window) {2808_THREAD_SAFE_METHOD_28092810ERR_FAIL_COND(!windows.has(p_window));2811const WindowData &wd = windows[p_window];28122813FLASHWINFO info;2814info.cbSize = sizeof(FLASHWINFO);2815info.hwnd = wd.hWnd;2816info.dwFlags = FLASHW_ALL;2817info.dwTimeout = 0;2818info.uCount = 2;2819FlashWindowEx(&info);2820}28212822void DisplayServerWindows::window_move_to_foreground(WindowID p_window) {2823_THREAD_SAFE_METHOD_28242825ERR_FAIL_COND(!windows.has(p_window));2826WindowData &wd = windows[p_window];28272828if (!wd.no_focus && !wd.is_popup) {2829SetForegroundWindow(wd.hWnd);2830}2831}28322833bool DisplayServerWindows::window_is_focused(WindowID p_window) const {2834_THREAD_SAFE_METHOD_28352836ERR_FAIL_COND_V(!windows.has(p_window), false);2837const WindowData &wd = windows[p_window];28382839return wd.window_focused;2840}28412842DisplayServerWindows::WindowID DisplayServerWindows::get_focused_window() const {2843return last_focused_window;2844}28452846bool DisplayServerWindows::window_can_draw(WindowID p_window) const {2847_THREAD_SAFE_METHOD_28482849ERR_FAIL_COND_V(!windows.has(p_window), false);2850const WindowData &wd = windows[p_window];2851return !wd.minimized;2852}28532854bool DisplayServerWindows::can_any_window_draw() const {2855_THREAD_SAFE_METHOD_28562857for (const KeyValue<WindowID, WindowData> &E : windows) {2858if (!E.value.minimized) {2859return true;2860}2861}28622863return false;2864}28652866int DisplayServerWindows::accessibility_should_increase_contrast() const {2867HIGHCONTRASTA hc;2868hc.cbSize = sizeof(HIGHCONTRAST);2869if (!SystemParametersInfoA(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &hc, 0)) {2870return -1;2871}2872return (hc.dwFlags & HCF_HIGHCONTRASTON);2873}28742875int DisplayServerWindows::accessibility_should_reduce_animation() const {2876BOOL anim_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2877if (!SystemParametersInfoA(SPI_GETCLIENTAREAANIMATION, 0, &anim_enabled, 0)) {2878return -1;2879}2880return (!anim_enabled);2881}28822883int DisplayServerWindows::accessibility_should_reduce_transparency() const {2884BOOL tr_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2885if (!SystemParametersInfoA(SPI_GETDISABLEOVERLAPPEDCONTENT, 0, &tr_enabled, 0)) {2886return -1;2887}2888return tr_enabled;2889}28902891int DisplayServerWindows::accessibility_screen_reader_active() const {2892BOOL sr_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2893if (SystemParametersInfoA(SPI_GETSCREENREADER, 0, &sr_enabled, 0) && sr_enabled) {2894return true;2895}28962897static const WCHAR *narrator_mutex_name = L"NarratorRunning";2898HANDLE narrator_mutex = OpenMutexW(MUTEX_ALL_ACCESS, false, narrator_mutex_name);2899if (narrator_mutex) {2900CloseHandle(narrator_mutex);2901return true;2902}2903return false;2904}29052906Vector2i DisplayServerWindows::ime_get_selection() const {2907_THREAD_SAFE_METHOD_29082909DisplayServer::WindowID window_id = _get_focused_window_or_popup();2910const WindowData &wd = windows[window_id];2911if (!wd.ime_active) {2912return Vector2i();2913}29142915int cursor = ImmGetCompositionStringW(wd.im_himc, GCS_CURSORPOS, nullptr, 0);29162917int32_t length = ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, nullptr, 0);2918wchar_t *string = reinterpret_cast<wchar_t *>(memalloc(length));2919ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);29202921int32_t utf32_cursor = 0;2922for (int32_t i = 0; i < length / int32_t(sizeof(wchar_t)); i++) {2923if ((string[i] & 0xfffffc00) == 0xd800) {2924i++;2925}2926if (i < cursor) {2927utf32_cursor++;2928} else {2929break;2930}2931}29322933memdelete(string);29342935return Vector2i(utf32_cursor, 0);2936}29372938String DisplayServerWindows::ime_get_text() const {2939_THREAD_SAFE_METHOD_29402941DisplayServer::WindowID window_id = _get_focused_window_or_popup();2942const WindowData &wd = windows[window_id];2943if (!wd.ime_active) {2944return String();2945}29462947String ret;2948int32_t length = ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, nullptr, 0);2949wchar_t *string = reinterpret_cast<wchar_t *>(memalloc(length));2950ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);2951ret.append_utf16((char16_t *)string, length / sizeof(wchar_t));29522953memdelete(string);29542955return ret;2956}29572958void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p_window) {2959_THREAD_SAFE_METHOD_29602961ERR_FAIL_COND(!windows.has(p_window));2962WindowData &wd = windows[p_window];29632964if (p_active) {2965wd.ime_active = true;2966ImmAssociateContext(wd.hWnd, wd.im_himc);2967CreateCaret(wd.hWnd, nullptr, 1, 1);2968window_set_ime_position(wd.im_position, p_window);2969} else {2970ImmAssociateContext(wd.hWnd, (HIMC) nullptr);2971DestroyCaret();2972wd.ime_active = false;2973}2974}29752976void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {2977_THREAD_SAFE_METHOD_29782979ERR_FAIL_COND(!windows.has(p_window));2980WindowData &wd = windows[p_window];29812982wd.im_position = p_pos;29832984HIMC himc = ImmGetContext(wd.hWnd);2985if (himc == (HIMC) nullptr) {2986return;2987}29882989COMPOSITIONFORM cps;2990cps.dwStyle = CFS_POINT;2991cps.ptCurrentPos.x = wd.im_position.x;2992cps.ptCurrentPos.y = wd.im_position.y;2993ImmSetCompositionWindow(himc, &cps);2994ImmReleaseContext(wd.hWnd, himc);2995}29962997void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {2998_THREAD_SAFE_METHOD_29993000ERR_FAIL_INDEX(p_shape, CURSOR_MAX);30013002if (cursor_shape == p_shape) {3003return;3004}30053006if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {3007cursor_shape = p_shape;3008return;3009}30103011static const LPCTSTR win_cursors[CURSOR_MAX] = {3012IDC_ARROW,3013IDC_IBEAM,3014IDC_HAND, // Finger.3015IDC_CROSS,3016IDC_WAIT,3017IDC_APPSTARTING,3018IDC_SIZEALL,3019IDC_ARROW,3020IDC_NO,3021IDC_SIZENS,3022IDC_SIZEWE,3023IDC_SIZENESW,3024IDC_SIZENWSE,3025IDC_SIZEALL,3026IDC_SIZENS,3027IDC_SIZEWE,3028IDC_HELP3029};30303031if (cursors_cache.has(p_shape)) {3032SetCursor(cursors[p_shape]);3033} else {3034SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));3035}30363037cursor_shape = p_shape;3038}30393040DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {3041return cursor_shape;3042}30433044void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {3045_THREAD_SAFE_METHOD_30463047ERR_FAIL_INDEX(p_shape, CURSOR_MAX);30483049if (p_cursor.is_valid()) {3050RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);30513052if (cursor_c) {3053if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {3054cursor_set_shape(p_shape);3055return;3056}30573058cursors_cache.erase(p_shape);3059}30603061Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);3062ERR_FAIL_COND(image.is_null());3063Vector2i texture_size = image->get_size();30643065UINT image_size = texture_size.width * texture_size.height;30663067// Create the BITMAP with alpha channel.3068COLORREF *buffer = nullptr;30693070BITMAPV5HEADER bi;3071ZeroMemory(&bi, sizeof(bi));3072bi.bV5Size = sizeof(bi);3073bi.bV5Width = texture_size.width;3074bi.bV5Height = -texture_size.height;3075bi.bV5Planes = 1;3076bi.bV5BitCount = 32;3077bi.bV5Compression = BI_BITFIELDS;3078bi.bV5RedMask = 0x00ff0000;3079bi.bV5GreenMask = 0x0000ff00;3080bi.bV5BlueMask = 0x000000ff;3081bi.bV5AlphaMask = 0xff000000;30823083HDC dc = GetDC(nullptr);3084HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);3085HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr);30863087bool fully_transparent = true;3088for (UINT index = 0; index < image_size; index++) {3089int row_index = std::floor(index / texture_size.width);3090int column_index = index % int(texture_size.width);30913092const Color &c = image->get_pixel(column_index, row_index);3093fully_transparent = fully_transparent && (c.a == 0.f);30943095*(buffer + index) = c.to_argb32();3096}30973098// Finally, create the icon.3099if (cursors[p_shape]) {3100DestroyIcon(cursors[p_shape]);3101}31023103if (fully_transparent) {3104cursors[p_shape] = nullptr;3105} else {3106ICONINFO iconinfo;3107iconinfo.fIcon = FALSE;3108iconinfo.xHotspot = p_hotspot.x;3109iconinfo.yHotspot = p_hotspot.y;3110iconinfo.hbmMask = mask;3111iconinfo.hbmColor = bitmap;3112cursors[p_shape] = CreateIconIndirect(&iconinfo);3113}31143115Vector<Variant> params;3116params.push_back(p_cursor);3117params.push_back(p_hotspot);3118cursors_cache.insert(p_shape, params);31193120if (p_shape == cursor_shape) {3121if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {3122SetCursor(cursors[p_shape]);3123}3124}31253126DeleteObject(mask);3127DeleteObject(bitmap);3128ReleaseDC(nullptr, dc);3129} else {3130// Reset to default system cursor.3131if (cursors[p_shape]) {3132DestroyIcon(cursors[p_shape]);3133}3134cursors[p_shape] = nullptr;31353136cursors_cache.erase(p_shape);31373138CursorShape c = cursor_shape;3139cursor_shape = CURSOR_MAX;3140cursor_set_shape(c);3141}3142}31433144bool DisplayServerWindows::get_swap_cancel_ok() {3145return true;3146}31473148void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {3149_THREAD_SAFE_METHOD_31503151AllowSetForegroundWindow(pid);3152}31533154struct WindowEnumData {3155DWORD process_id;3156HWND parent_hWnd;3157HWND hWnd;3158};31593160static BOOL CALLBACK _enum_proc_find_window_from_process_id_callback(HWND hWnd, LPARAM lParam) {3161WindowEnumData &ed = *(WindowEnumData *)lParam;3162DWORD process_id = 0x0;31633164GetWindowThreadProcessId(hWnd, &process_id);3165if (ed.process_id == process_id) {3166if (GetParent(hWnd) != ed.parent_hWnd) {3167return TRUE;3168}31693170// Found it.3171ed.hWnd = hWnd;3172SetLastError(ERROR_SUCCESS);3173return FALSE;3174}3175// Continue enumeration.3176return TRUE;3177}31783179HWND DisplayServerWindows::_find_window_from_process_id(OS::ProcessID p_pid, HWND p_current_hwnd) {3180DWORD pid = p_pid;3181WindowEnumData ed = { pid, p_current_hwnd, NULL };31823183// First, check our own child, maybe it's already embedded.3184if (!EnumChildWindows(p_current_hwnd, _enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {3185if (ed.hWnd) {3186return ed.hWnd;3187}3188}31893190// Then check all the opened windows on the computer.3191if (!EnumWindows(_enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {3192return ed.hWnd;3193}31943195return NULL;3196}31973198Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {3199_THREAD_SAFE_METHOD_32003201ERR_FAIL_COND_V(!windows.has(p_window), FAILED);32023203const WindowData &wd = windows[p_window];32043205EmbeddedProcessData *ep = nullptr;3206if (embedded_processes.has(p_pid)) {3207ep = embedded_processes.get(p_pid);3208} else {3209// New process, trying to find the window.3210HWND handle_to_embed = _find_window_from_process_id(p_pid, wd.hWnd);3211if (!handle_to_embed) {3212return ERR_DOES_NOT_EXIST;3213}32143215const DWORD style = GetWindowLongPtr(handle_to_embed, GWL_STYLE);32163217ep = memnew(EmbeddedProcessData);3218ep->window_handle = handle_to_embed;3219ep->parent_window_handle = wd.hWnd;3220ep->is_visible = (style & WS_VISIBLE) == WS_VISIBLE;32213222embedded_processes.insert(p_pid, ep);3223}32243225if (p_rect.size.x <= 100 || p_rect.size.y <= 100) {3226p_visible = false;3227}32283229// In Godot, the window position is offset by the screen's origin coordinates.3230// We need to adjust for this when a screen is positioned in the negative space3231// (e.g., a screen to the left of the main screen).3232const Rect2i adjusted_rect = Rect2i(p_rect.position + _get_screens_origin(), p_rect.size);32333234// Use HWND_BOTTOM to prevent reordering of the embedded window over another popup.3235SetWindowPos(ep->window_handle, HWND_BOTTOM, adjusted_rect.position.x, adjusted_rect.position.y, adjusted_rect.size.x, adjusted_rect.size.y, SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS);32363237if (ep->is_visible != p_visible) {3238if (p_visible) {3239ShowWindow(ep->window_handle, SW_SHOWNA);3240} else {3241ShowWindow(ep->window_handle, SW_HIDE);3242}3243ep->is_visible = p_visible;3244}32453246if (p_grab_focus) {3247SetForegroundWindow(ep->window_handle);3248SetFocus(ep->window_handle);3249}32503251return OK;3252}32533254Error DisplayServerWindows::request_close_embedded_process(OS::ProcessID p_pid) {3255_THREAD_SAFE_METHOD_32563257if (!embedded_processes.has(p_pid)) {3258return ERR_DOES_NOT_EXIST;3259}32603261EmbeddedProcessData *ep = embedded_processes.get(p_pid);32623263// Send a close message to gracefully close the process.3264PostMessage(ep->window_handle, WM_CLOSE, 0, 0);32653266return OK;3267}32683269Error DisplayServerWindows::remove_embedded_process(OS::ProcessID p_pid) {3270_THREAD_SAFE_METHOD_32713272if (!embedded_processes.has(p_pid)) {3273return ERR_DOES_NOT_EXIST;3274}32753276EmbeddedProcessData *ep = embedded_processes.get(p_pid);32773278request_close_embedded_process(p_pid);32793280// This is a workaround to ensure the parent window correctly regains focus after the3281// embedded window is closed. When the embedded window is closed while it has focus,3282// the parent window (the editor) does not become active. It appears focused but is not truly activated.3283// Opening a new window and closing it forces Windows to set the focus and activation correctly.3284DWORD style = WS_POPUP | WS_VISIBLE;3285DWORD style_ex = WS_EX_TOPMOST;32863287WNDCLASSW wcTemp = {};3288wcTemp.lpfnWndProc = DefWindowProcW;3289wcTemp.hInstance = GetModuleHandle(nullptr);3290wcTemp.lpszClassName = L"Engine temp window";3291RegisterClassW(&wcTemp);32923293HWND hWnd = CreateWindowExW(3294style_ex,3295L"Engine temp window", L"",3296style,32970,32980,32991,33001,3301ep->parent_window_handle,3302nullptr,3303GetModuleHandle(nullptr),3304nullptr);33053306SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);33073308DestroyWindow(hWnd);3309UnregisterClassW(L"Engine temp window", GetModuleHandle(nullptr));33103311SetForegroundWindow(ep->parent_window_handle);33123313embedded_processes.erase(p_pid);3314memdelete(ep);33153316return OK;3317}33183319OS::ProcessID DisplayServerWindows::get_focused_process_id() {3320HWND hwnd = GetForegroundWindow();3321if (!hwnd) {3322return 0;3323}33243325// Get the process ID of the window.3326DWORD processID;3327GetWindowThreadProcessId(hwnd, &processID);33283329return processID;3330}33313332static HRESULT CALLBACK win32_task_dialog_callback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) {3333if (msg == TDN_CREATED) {3334// To match the input text dialog.3335SendMessageW(hwnd, WM_SETICON, ICON_BIG, 0);3336SendMessageW(hwnd, WM_SETICON, ICON_SMALL, 0);3337}33383339return 0;3340}33413342Error DisplayServerWindows::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {3343_THREAD_SAFE_METHOD_33443345TASKDIALOGCONFIG config;3346ZeroMemory(&config, sizeof(TASKDIALOGCONFIG));3347config.cbSize = sizeof(TASKDIALOGCONFIG);33483349Char16String title = p_title.utf16();3350Char16String message = p_description.utf16();3351LocalVector<Char16String> buttons;3352for (String s : p_buttons) {3353buttons.push_back(s.utf16());3354}33553356WindowID window_id = _get_focused_window_or_popup();3357if (!windows.has(window_id)) {3358window_id = MAIN_WINDOW_ID;3359}33603361config.pszWindowTitle = (LPCWSTR)(title.get_data());3362config.pszContent = (LPCWSTR)(message.get_data());3363config.hwndParent = windows[window_id].hWnd;33643365const int button_count = buttons.size();3366config.cButtons = button_count;33673368// No dynamic stack array size :(3369TASKDIALOG_BUTTON *tbuttons = button_count != 0 ? (TASKDIALOG_BUTTON *)alloca(sizeof(TASKDIALOG_BUTTON) * button_count) : nullptr;3370if (tbuttons) {3371for (int i = 0; i < button_count; i++) {3372tbuttons[i].nButtonID = i + 100;3373tbuttons[i].pszButtonText = (LPCWSTR)(buttons[i].get_data());3374}3375}3376config.pButtons = tbuttons;3377config.pfCallback = win32_task_dialog_callback;33783379Error result = FAILED;3380HMODULE comctl = LoadLibraryW(L"comctl32.dll");3381if (comctl) {3382typedef HRESULT(WINAPI * TaskDialogIndirectPtr)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);33833384TaskDialogIndirectPtr task_dialog_indirect = (TaskDialogIndirectPtr)(void *)GetProcAddress(comctl, "TaskDialogIndirect");3385int button_pressed;33863387if (task_dialog_indirect && SUCCEEDED(task_dialog_indirect(&config, &button_pressed, nullptr, nullptr))) {3388if (p_callback.is_valid()) {3389Variant button = button_pressed - 100;3390const Variant *args[1] = { &button };3391Variant ret;3392Callable::CallError ce;3393p_callback.callp(args, 1, ret, ce);3394if (ce.error != Callable::CallError::CALL_OK) {3395ERR_PRINT(vformat("Failed to execute dialog callback: %s.", Variant::get_callable_error_text(p_callback, args, 1, ce)));3396}3397}33983399result = OK;3400}3401FreeLibrary(comctl);3402} else {3403ERR_PRINT("Unable to create native dialog.");3404}34053406return result;3407}34083409struct Win32InputTextDialogInit {3410const char16_t *title;3411const char16_t *description;3412const char16_t *partial;3413const Callable &callback;3414};34153416static int scale_with_dpi(int p_pos, int p_dpi) {3417return IsProcessDPIAware() ? (p_pos * p_dpi / 96) : p_pos;3418}34193420static INT_PTR input_text_dialog_init(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3421Win32InputTextDialogInit init = *(Win32InputTextDialogInit *)lParam;3422SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)&init.callback); // Set dialog callback.34233424SetWindowTextW(hWnd, (LPCWSTR)init.title);34253426const int dpi = DisplayServerWindows::get_singleton()->screen_get_dpi();34273428const int margin = scale_with_dpi(7, dpi);3429const SIZE dlg_size = { scale_with_dpi(300, dpi), scale_with_dpi(50, dpi) };34303431int str_len = lstrlenW((LPCWSTR)init.description);3432SIZE str_size = { dlg_size.cx, 0 };3433if (str_len > 0) {3434HDC hdc = GetDC(nullptr);3435RECT trect = { margin, margin, margin + dlg_size.cx, margin + dlg_size.cy };3436SelectObject(hdc, (HFONT)SendMessageW(hWnd, WM_GETFONT, 0, 0));34373438// `+ margin` adds some space between the static text and the edit field.3439// Don't scale this with DPI because DPI is already handled by DrawText.3440str_size.cy = DrawTextW(hdc, (LPCWSTR)init.description, str_len, &trect, DT_LEFT | DT_WORDBREAK | DT_CALCRECT) + margin;34413442ReleaseDC(nullptr, hdc);3443}34443445RECT crect, wrect;3446GetClientRect(hWnd, &crect);3447GetWindowRect(hWnd, &wrect);3448int sw = GetSystemMetrics(SM_CXSCREEN);3449int sh = GetSystemMetrics(SM_CYSCREEN);3450int new_width = dlg_size.cx + margin * 2 + wrect.right - wrect.left - crect.right;3451int new_height = dlg_size.cy + margin * 2 + wrect.bottom - wrect.top - crect.bottom + str_size.cy;34523453MoveWindow(hWnd, (sw - new_width) / 2, (sh - new_height) / 2, new_width, new_height, true);34543455HWND ok_button = GetDlgItem(hWnd, 1);3456MoveWindow(ok_button,3457dlg_size.cx + margin - scale_with_dpi(65, dpi),3458dlg_size.cy + str_size.cy + margin - scale_with_dpi(20, dpi),3459scale_with_dpi(65, dpi), scale_with_dpi(20, dpi), true);34603461HWND description = GetDlgItem(hWnd, 3);3462MoveWindow(description, margin, margin, dlg_size.cx, str_size.cy, true);3463SetWindowTextW(description, (LPCWSTR)init.description);34643465HWND text_edit = GetDlgItem(hWnd, 2);3466MoveWindow(text_edit, margin, str_size.cy + margin, dlg_size.cx, scale_with_dpi(20, dpi), true);3467SetWindowTextW(text_edit, (LPCWSTR)init.partial);34683469return TRUE;3470}34713472static INT_PTR input_text_dialog_cmd_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3473if (LOWORD(wParam) == 1) {3474HWND text_edit = GetDlgItem(hWnd, 2);3475ERR_FAIL_NULL_V(text_edit, false);34763477Char16String text;3478text.resize_uninitialized(GetWindowTextLengthW(text_edit) + 1);3479GetWindowTextW(text_edit, (LPWSTR)text.get_data(), text.size());34803481const Callable *callback = (const Callable *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);3482if (callback && callback->is_valid()) {3483Variant v_result = String((const wchar_t *)text.get_data());3484Variant ret;3485Callable::CallError ce;3486const Variant *args[1] = { &v_result };34873488callback->callp(args, 1, ret, ce);3489if (ce.error != Callable::CallError::CALL_OK) {3490ERR_PRINT(vformat("Failed to execute input dialog callback: %s.", Variant::get_callable_error_text(*callback, args, 1, ce)));3491}3492}34933494return EndDialog(hWnd, 0);3495}34963497return false;3498}34993500static INT_PTR CALLBACK input_text_dialog_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3501switch (code) {3502case WM_INITDIALOG:3503return input_text_dialog_init(hWnd, code, wParam, lParam);35043505case WM_COMMAND:3506return input_text_dialog_cmd_proc(hWnd, code, wParam, lParam);35073508default:3509return FALSE;3510}3511}35123513Error DisplayServerWindows::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {3514#pragma pack(push, 1)35153516// NOTE: Use default/placeholder coordinates here. Windows uses its own coordinate system3517// specifically for dialogs which relies on font sizes instead of pixels.3518const struct {3519WORD dlgVer; // must be 13520WORD signature; // must be 0xFFFF3521DWORD helpID;3522DWORD exStyle;3523DWORD style;3524WORD cDlgItems;3525short x;3526short y;3527short cx;3528short cy;3529WCHAR menu[1]; // must be 03530WCHAR windowClass[7]; // must be "#32770" -- the default window class for dialogs3531WCHAR title[1]; // must be 03532WORD pointsize;3533WORD weight;3534BYTE italic;3535BYTE charset;3536WCHAR font[13]; // must be "MS Shell Dlg"3537} template_base = {35381, 0xFFFF, 0, 0,3539DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION,35403, 0, 0, 20, 20, L"", L"#32770", L"", 8, FW_NORMAL, 0, DEFAULT_CHARSET, L"MS Shell Dlg"3541};35423543const struct {3544DWORD helpID;3545DWORD exStyle;3546DWORD style;3547short x;3548short y;3549short cx;3550short cy;3551DWORD id;3552WCHAR windowClass[7]; // must be "Button"3553WCHAR title[3]; // must be "OK"3554WORD extraCount;3555} ok_button = {35560, 0, WS_VISIBLE | BS_DEFPUSHBUTTON, 0, 0, 50, 14, 1, WC_BUTTONW, L"OK", 03557};3558const struct {3559DWORD helpID;3560DWORD exStyle;3561DWORD style;3562short x;3563short y;3564short cx;3565short cy;3566DWORD id;3567WCHAR windowClass[5]; // must be "Edit"3568WCHAR title[1]; // must be 03569WORD extraCount;3570} text_field = {35710, 0, WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 0, 0, 250, 14, 2, WC_EDITW, L"", 03572};3573const struct {3574DWORD helpID;3575DWORD exStyle;3576DWORD style;3577short x;3578short y;3579short cx;3580short cy;3581DWORD id;3582WCHAR windowClass[7]; // must be "Static"3583WCHAR title[1]; // must be 03584WORD extraCount;3585} static_text = {35860, 0, WS_VISIBLE, 0, 0, 250, 14, 3, WC_STATICW, L"", 03587};35883589#pragma pack(pop)35903591// Dialog template3592const size_t data_size = sizeof(template_base) + (sizeof(template_base) % 4) +3593sizeof(ok_button) + (sizeof(ok_button) % 4) +3594sizeof(text_field) + (sizeof(text_field) % 4) +3595sizeof(static_text) + (sizeof(static_text) % 4);35963597void *data_template = memalloc(data_size);3598ERR_FAIL_NULL_V_MSG(data_template, FAILED, "Unable to allocate memory for the dialog template.");3599ZeroMemory(data_template, data_size);36003601char *current_block = (char *)data_template;3602CopyMemory(current_block, &template_base, sizeof(template_base));3603current_block += sizeof(template_base) + (sizeof(template_base) % 4);3604CopyMemory(current_block, &ok_button, sizeof(ok_button));3605current_block += sizeof(ok_button) + (sizeof(ok_button) % 4);3606CopyMemory(current_block, &text_field, sizeof(text_field));3607current_block += sizeof(text_field) + (sizeof(text_field) % 4);3608CopyMemory(current_block, &static_text, sizeof(static_text));36093610Char16String title16 = p_title.utf16();3611Char16String description16 = p_description.utf16();3612Char16String partial16 = p_partial.utf16();36133614Win32InputTextDialogInit init = {3615title16.get_data(), description16.get_data(), partial16.get_data(), p_callback3616};36173618// No modal dialogs for specific windows? Assume main window here.3619INT_PTR ret = DialogBoxIndirectParamW(hInstance, (LPDLGTEMPLATEW)data_template, nullptr, (DLGPROC)input_text_dialog_proc, (LPARAM)(&init));36203621Error result = ret != -1 ? OK : FAILED;3622memfree(data_template);36233624if (result == FAILED) {3625ERR_PRINT("Unable to create native dialog.");3626}3627return result;3628}36293630int DisplayServerWindows::keyboard_get_layout_count() const {3631return GetKeyboardLayoutList(0, nullptr);3632}36333634int DisplayServerWindows::keyboard_get_current_layout() const {3635HKL cur_layout = GetKeyboardLayout(0);36363637int layout_count = GetKeyboardLayoutList(0, nullptr);3638HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3639GetKeyboardLayoutList(layout_count, layouts);36403641for (int i = 0; i < layout_count; i++) {3642if (cur_layout == layouts[i]) {3643memfree(layouts);3644return i;3645}3646}3647memfree(layouts);3648return -1;3649}36503651void DisplayServerWindows::keyboard_set_current_layout(int p_index) {3652int layout_count = GetKeyboardLayoutList(0, nullptr);36533654ERR_FAIL_INDEX(p_index, layout_count);36553656HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3657GetKeyboardLayoutList(layout_count, layouts);3658ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS);3659memfree(layouts);3660}36613662String DisplayServerWindows::keyboard_get_layout_language(int p_index) const {3663int layout_count = GetKeyboardLayoutList(0, nullptr);36643665ERR_FAIL_INDEX_V(p_index, layout_count, "");36663667HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3668GetKeyboardLayoutList(layout_count, layouts);36693670WCHAR buf[LOCALE_NAME_MAX_LENGTH];3671memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));3672LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);36733674memfree(layouts);36753676return String::utf16((const char16_t *)buf).substr(0, 2);3677}36783679Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) const {3680Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;3681Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);36823683if (keycode_no_mod == Key::PRINT ||3684keycode_no_mod == Key::KP_ADD ||3685keycode_no_mod == Key::KP_5 ||3686(keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {3687return p_keycode;3688}36893690unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);3691if (scancode == 0) {3692return p_keycode;3693}36943695HKL current_layout = GetKeyboardLayout(0);3696UINT vk = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK, current_layout);3697if (vk == 0) {3698return p_keycode;3699}37003701UINT char_code = MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, current_layout) & 0x7FFF;3702// Unlike a similar Linux/BSD check which matches full Latin-1 range,3703// we limit these to ASCII to fix some layouts, including Arabic ones3704if (char_code >= 32 && char_code <= 127) {3705// Godot uses 'braces' instead of 'brackets'3706if (char_code == (unsigned int)Key::BRACKETLEFT || char_code == (unsigned int)Key::BRACKETRIGHT) {3707char_code += 32;3708}3709return (Key)(char_code | (unsigned int)modifiers);3710}37113712return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers);3713}37143715Key DisplayServerWindows::keyboard_get_label_from_physical(Key p_keycode) const {3716Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;3717Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);37183719if (keycode_no_mod == Key::PRINT ||3720keycode_no_mod == Key::KP_ADD ||3721keycode_no_mod == Key::KP_5 ||3722(keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {3723return p_keycode;3724}37253726unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);3727if (scancode == 0) {3728return p_keycode;3729}37303731Key keycode = KeyMappingWindows::get_keysym(MapVirtualKey(scancode, MAPVK_VSC_TO_VK));37323733HKL current_layout = GetKeyboardLayout(0);3734static BYTE keyboard_state[256];3735memset(keyboard_state, 0, 256);3736wchar_t chars[256] = {};3737UINT extended_code = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);3738if (ToUnicodeEx(extended_code, scancode, keyboard_state, chars, 255, 4, current_layout) > 0) {3739String keysym = String::utf16((char16_t *)chars, 255);3740if (!keysym.is_empty()) {3741return fix_key_label(keysym[0], keycode) | modifiers;3742}3743}3744return p_keycode;3745}37463747void DisplayServerWindows::show_emoji_and_symbol_picker() const {3748// Send Win + Period shortcut, there's no non-WinRT public API.37493750INPUT input[4] = {};3751input[0].type = INPUT_KEYBOARD; // Win down.3752input[0].ki.wVk = VK_LWIN;37533754input[1].type = INPUT_KEYBOARD; // Period down.3755input[1].ki.wVk = VK_OEM_PERIOD;37563757input[2].type = INPUT_KEYBOARD; // Win up.3758input[2].ki.wVk = VK_LWIN;3759input[2].ki.dwFlags = KEYEVENTF_KEYUP;37603761input[3].type = INPUT_KEYBOARD; // Period up.3762input[3].ki.wVk = VK_OEM_PERIOD;3763input[3].ki.dwFlags = KEYEVENTF_KEYUP;37643765SendInput(4, input, sizeof(INPUT));3766}37673768String DisplayServerWindows::_get_keyboard_layout_display_name(const String &p_klid) const {3769String ret;3770HKEY key;3771if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {3772return String();3773}37743775WCHAR buffer[MAX_PATH] = {};3776DWORD buffer_size = MAX_PATH;3777if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Display Name", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {3778if (SHLoadIndirectString(buffer, buffer, buffer_size, nullptr) == S_OK) {3779ret = String::utf16((const char16_t *)buffer, buffer_size);3780}3781} else {3782if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Text", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {3783ret = String::utf16((const char16_t *)buffer, buffer_size);3784}3785}37863787RegCloseKey(key);3788return ret;3789}37903791String DisplayServerWindows::_get_klid(HKL p_hkl) const {3792String ret;37933794WORD device = HIWORD(p_hkl);3795if ((device & 0xf000) == 0xf000) {3796WORD layout_id = device & 0x0fff;37973798HKEY key;3799if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {3800return String();3801}38023803DWORD index = 0;3804wchar_t klid_buffer[KL_NAMELENGTH];3805DWORD klid_buffer_size = KL_NAMELENGTH;3806while (RegEnumKeyExW(key, index, klid_buffer, &klid_buffer_size, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) {3807wchar_t layout_id_buf[MAX_PATH] = {};3808DWORD layout_id_size = MAX_PATH;3809if (RegGetValueW(key, klid_buffer, L"Layout Id", RRF_RT_REG_SZ, nullptr, layout_id_buf, &layout_id_size) == ERROR_SUCCESS) {3810if (layout_id == String::utf16((char16_t *)layout_id_buf, layout_id_size).hex_to_int()) {3811ret = String::utf16((const char16_t *)klid_buffer, klid_buffer_size).lpad(8, "0");3812break;3813}3814}3815klid_buffer_size = KL_NAMELENGTH;3816++index;3817}38183819RegCloseKey(key);3820} else {3821if (device == 0) {3822device = LOWORD(p_hkl);3823}3824ret = (String::num_uint64((uint64_t)device, 16, false)).lpad(8, "0");3825}38263827return ret;3828}38293830String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {3831int layout_count = GetKeyboardLayoutList(0, nullptr);38323833ERR_FAIL_INDEX_V(p_index, layout_count, "");38343835HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3836GetKeyboardLayoutList(layout_count, layouts);38373838String ret = _get_keyboard_layout_display_name(_get_klid(layouts[p_index])); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine).3839if (ret.is_empty()) {3840WCHAR buf[LOCALE_NAME_MAX_LENGTH];3841memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));3842LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);38433844WCHAR name[1024];3845memset(name, 0, 1024 * sizeof(WCHAR));3846GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024);38473848ret = String::utf16((const char16_t *)name);3849}3850memfree(layouts);38513852return ret;3853}38543855void DisplayServerWindows::process_events() {3856ERR_FAIL_COND(!Thread::is_main_thread());38573858if (!drop_events) {3859#ifdef SDL_ENABLED3860if (joypad_sdl) {3861joypad_sdl->process_events();3862}3863#endif3864}38653866_THREAD_SAFE_LOCK_3867MSG msg = {};3868while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {3869TranslateMessage(&msg);3870DispatchMessageW(&msg);3871}3872_THREAD_SAFE_UNLOCK_38733874if (tts) {3875tts->process_events();3876}38773878if (!drop_events) {3879_process_key_events();3880Input::get_singleton()->flush_buffered_events();3881}38823883LocalVector<List<FileDialogData *>::Element *> to_remove;3884for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {3885FileDialogData *fd = E->get();3886if (fd->finished.is_set()) {3887if (fd->listener_thread.is_started()) {3888fd->listener_thread.wait_to_finish();3889}3890to_remove.push_back(E);3891}3892}3893for (List<FileDialogData *>::Element *E : to_remove) {3894memdelete(E->get());3895E->erase();3896}3897process_file_dialog_callbacks();3898}38993900void DisplayServerWindows::force_process_and_drop_events() {3901ERR_FAIL_COND(!Thread::is_main_thread());39023903drop_events = true;3904process_events();3905drop_events = false;3906}39073908void DisplayServerWindows::release_rendering_thread() {3909#if defined(GLES3_ENABLED)3910if (gl_manager_angle) {3911gl_manager_angle->release_current();3912}3913if (gl_manager_native) {3914gl_manager_native->release_current();3915}3916#endif3917}39183919void DisplayServerWindows::swap_buffers() {3920#if defined(GLES3_ENABLED)3921if (gl_manager_angle) {3922gl_manager_angle->swap_buffers();3923}3924if (gl_manager_native) {3925gl_manager_native->swap_buffers();3926}3927#endif3928}39293930void DisplayServerWindows::set_native_icon(const String &p_filename) {3931_THREAD_SAFE_METHOD_39323933if (icon_big) {3934DestroyIcon(icon_big);3935icon_buffer_big.clear();3936icon_big = nullptr;3937}3938if (icon_small) {3939DestroyIcon(icon_small);3940icon_buffer_small.clear();3941icon_small = nullptr;3942}39433944Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);3945ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'.");39463947ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));3948int pos = 0;39493950icon_dir->idReserved = f->get_32();3951pos += sizeof(WORD);3952f->seek(pos);39533954icon_dir->idType = f->get_32();3955pos += sizeof(WORD);3956f->seek(pos);39573958ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!");39593960icon_dir->idCount = f->get_32();3961pos += sizeof(WORD);3962f->seek(pos);39633964icon_dir = (ICONDIR *)memrealloc(icon_dir, sizeof(ICONDIR) - sizeof(ICONDIRENTRY) + icon_dir->idCount * sizeof(ICONDIRENTRY));3965f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY));39663967int small_icon_index = -1; // Select 16x16 with largest color count.3968int small_icon_cc = 0;3969int big_icon_index = -1; // Select largest.3970int big_icon_width = 16;3971int big_icon_cc = 0;39723973for (int i = 0; i < icon_dir->idCount; i++) {3974int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount;3975int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth;3976if (width == 16) {3977if (colors >= small_icon_cc) {3978small_icon_index = i;3979small_icon_cc = colors;3980}3981}3982if (width >= big_icon_width) {3983if (colors >= big_icon_cc) {3984big_icon_index = i;3985big_icon_width = width;3986big_icon_cc = colors;3987}3988}3989}39903991ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!");39923993if (small_icon_index == -1) {3994WARN_PRINT("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!");3995small_icon_index = big_icon_index;3996small_icon_cc = big_icon_cc;3997}39983999// Read the big icon.4000DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes;4001icon_buffer_big.resize(bytecount_big);4002pos = icon_dir->idEntries[big_icon_index].dwImageOffset;4003f->seek(pos);4004f->get_buffer((uint8_t *)&icon_buffer_big.write[0], bytecount_big);4005icon_big = CreateIconFromResourceEx((PBYTE)&icon_buffer_big.write[0], bytecount_big, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);4006ERR_FAIL_NULL_MSG(icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");40074008// Read the small icon.4009DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;4010icon_buffer_small.resize(bytecount_small);4011pos = icon_dir->idEntries[small_icon_index].dwImageOffset;4012f->seek(pos);4013f->get_buffer((uint8_t *)&icon_buffer_small.write[0], bytecount_small);4014icon_small = CreateIconFromResourceEx((PBYTE)&icon_buffer_small.write[0], bytecount_small, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);4015ERR_FAIL_NULL_MSG(icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");40164017// Online tradition says to be sure last error is cleared and set the small icon first.4018int err = 0;4019SetLastError(err);40204021for (const KeyValue<WindowID, WindowData> &E : windows) {4022SendMessage(E.value.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);4023SendMessage(E.value.hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);4024}4025memdelete(icon_dir);4026}40274028void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {4029_THREAD_SAFE_METHOD_40304031if (icon_big) {4032DestroyIcon(icon_big);4033icon_buffer_big.clear();4034icon_big = nullptr;4035}4036if (icon_small) {4037DestroyIcon(icon_small);4038icon_buffer_small.clear();4039icon_small = nullptr;4040}40414042if (p_icon.is_valid()) {4043ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);40444045Ref<Image> img = p_icon->duplicate();4046img->convert(Image::FORMAT_RGBA8);40474048int w = img->get_width();4049int h = img->get_height();40504051// Create temporary bitmap buffer.4052int icon_len = 40 + h * w * 4;4053icon_buffer_big.resize(icon_len);4054BYTE *icon_bmp = icon_buffer_big.ptrw();40554056encode_uint32(40, &icon_bmp[0]);4057encode_uint32(w, &icon_bmp[4]);4058encode_uint32(h * 2, &icon_bmp[8]);4059encode_uint16(1, &icon_bmp[12]);4060encode_uint16(32, &icon_bmp[14]);4061encode_uint32(BI_RGB, &icon_bmp[16]);4062encode_uint32(w * h * 4, &icon_bmp[20]);4063encode_uint32(0, &icon_bmp[24]);4064encode_uint32(0, &icon_bmp[28]);4065encode_uint32(0, &icon_bmp[32]);4066encode_uint32(0, &icon_bmp[36]);40674068uint8_t *wr = &icon_bmp[40];4069const uint8_t *r = img->get_data().ptr();40704071for (int i = 0; i < h; i++) {4072for (int j = 0; j < w; j++) {4073const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4074uint8_t *wpx = &wr[(i * w + j) * 4];4075wpx[0] = rpx[2];4076wpx[1] = rpx[1];4077wpx[2] = rpx[0];4078wpx[3] = rpx[3];4079}4080}4081icon_big = CreateIconFromResourceEx(icon_bmp, icon_len, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);4082ERR_FAIL_NULL(icon_big);40834084for (const KeyValue<WindowID, WindowData> &E : windows) {4085SendMessage(E.value.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_big);4086SendMessage(E.value.hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);4087}4088} else {4089for (const KeyValue<WindowID, WindowData> &E : windows) {4090SendMessage(E.value.hWnd, WM_SETICON, ICON_SMALL, 0);4091SendMessage(E.value.hWnd, WM_SETICON, ICON_BIG, 0);4092}4093}4094}40954096DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) {4097IndicatorData idat;4098if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {4099Ref<Image> img = p_icon->get_image();4100img = img->duplicate();4101if (img->is_compressed()) {4102img->decompress();4103}4104img->convert(Image::FORMAT_RGBA8);41054106int w = img->get_width();4107int h = img->get_height();41084109// Create temporary bitmap buffer.4110int icon_len = 40 + h * w * 4;4111idat.icon_buffer.resize(icon_len);4112BYTE *icon_bmp = idat.icon_buffer.ptrw();41134114encode_uint32(40, &icon_bmp[0]);4115encode_uint32(w, &icon_bmp[4]);4116encode_uint32(h * 2, &icon_bmp[8]);4117encode_uint16(1, &icon_bmp[12]);4118encode_uint16(32, &icon_bmp[14]);4119encode_uint32(BI_RGB, &icon_bmp[16]);4120encode_uint32(w * h * 4, &icon_bmp[20]);4121encode_uint32(0, &icon_bmp[24]);4122encode_uint32(0, &icon_bmp[28]);4123encode_uint32(0, &icon_bmp[32]);4124encode_uint32(0, &icon_bmp[36]);41254126uint8_t *wr = &icon_bmp[40];4127const uint8_t *r = img->get_data().ptr();41284129for (int i = 0; i < h; i++) {4130for (int j = 0; j < w; j++) {4131const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4132uint8_t *wpx = &wr[(i * w + j) * 4];4133wpx[0] = rpx[2];4134wpx[1] = rpx[1];4135wpx[2] = rpx[0];4136wpx[3] = rpx[3];4137}4138}41394140idat.icon = CreateIconFromResourceEx(icon_bmp, icon_len, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);4141}41424143idat.callback = p_callback;41444145NOTIFYICONDATAW ndat;4146ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4147ndat.cbSize = sizeof(NOTIFYICONDATAW);4148ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4149ndat.uID = indicator_id_counter;4150ndat.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;4151ndat.uCallbackMessage = WM_INDICATOR_CALLBACK_MESSAGE;4152ndat.hIcon = idat.icon;4153memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));4154ndat.uVersion = NOTIFYICON_VERSION;41554156Shell_NotifyIconW(NIM_ADD, &ndat);4157Shell_NotifyIconW(NIM_SETVERSION, &ndat);41584159IndicatorID iid = indicator_id_counter++;4160indicators[iid] = idat;41614162return iid;4163}41644165void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon) {4166ERR_FAIL_COND(!indicators.has(p_id));41674168IndicatorData &idat = indicators[p_id];41694170if (idat.icon) {4171DestroyIcon(idat.icon);4172idat.icon_buffer.clear();4173idat.icon = nullptr;4174}41754176if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {4177Ref<Image> img = p_icon->get_image();4178img = img->duplicate();4179if (img->is_compressed()) {4180img->decompress();4181}4182img->convert(Image::FORMAT_RGBA8);41834184int w = img->get_width();4185int h = img->get_height();41864187// Create temporary bitmap buffer.4188int icon_len = 40 + h * w * 4;4189idat.icon_buffer.resize(icon_len);4190BYTE *icon_bmp = idat.icon_buffer.ptrw();41914192encode_uint32(40, &icon_bmp[0]);4193encode_uint32(w, &icon_bmp[4]);4194encode_uint32(h * 2, &icon_bmp[8]);4195encode_uint16(1, &icon_bmp[12]);4196encode_uint16(32, &icon_bmp[14]);4197encode_uint32(BI_RGB, &icon_bmp[16]);4198encode_uint32(w * h * 4, &icon_bmp[20]);4199encode_uint32(0, &icon_bmp[24]);4200encode_uint32(0, &icon_bmp[28]);4201encode_uint32(0, &icon_bmp[32]);4202encode_uint32(0, &icon_bmp[36]);42034204uint8_t *wr = &icon_bmp[40];4205const uint8_t *r = img->get_data().ptr();42064207for (int i = 0; i < h; i++) {4208for (int j = 0; j < w; j++) {4209const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4210uint8_t *wpx = &wr[(i * w + j) * 4];4211wpx[0] = rpx[2];4212wpx[1] = rpx[1];4213wpx[2] = rpx[0];4214wpx[3] = rpx[3];4215}4216}42174218idat.icon = CreateIconFromResourceEx(icon_bmp, icon_len, TRUE, 0x00030000, 0, 0, LR_DEFAULTSIZE);4219}42204221NOTIFYICONDATAW ndat;4222ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4223ndat.cbSize = sizeof(NOTIFYICONDATAW);4224ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4225ndat.uID = p_id;4226ndat.uFlags = NIF_ICON;4227ndat.hIcon = idat.icon;4228ndat.uVersion = NOTIFYICON_VERSION;42294230Shell_NotifyIconW(NIM_MODIFY, &ndat);4231}42324233void DisplayServerWindows::status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) {4234ERR_FAIL_COND(!indicators.has(p_id));42354236NOTIFYICONDATAW ndat;4237ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4238ndat.cbSize = sizeof(NOTIFYICONDATAW);4239ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4240ndat.uID = p_id;4241ndat.uFlags = NIF_TIP;4242memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));4243ndat.uVersion = NOTIFYICON_VERSION;42444245Shell_NotifyIconW(NIM_MODIFY, &ndat);4246}42474248void DisplayServerWindows::status_indicator_set_menu(IndicatorID p_id, const RID &p_menu_rid) {4249ERR_FAIL_COND(!indicators.has(p_id));42504251indicators[p_id].menu_rid = p_menu_rid;4252}42534254void DisplayServerWindows::status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) {4255ERR_FAIL_COND(!indicators.has(p_id));42564257indicators[p_id].callback = p_callback;4258}42594260Rect2 DisplayServerWindows::status_indicator_get_rect(IndicatorID p_id) const {4261ERR_FAIL_COND_V(!indicators.has(p_id), Rect2());42624263NOTIFYICONIDENTIFIER nid;4264ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));4265nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);4266nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;4267nid.uID = p_id;4268nid.guidItem = GUID_NULL;42694270RECT rect;4271if (Shell_NotifyIconGetRect(&nid, &rect) != S_OK) {4272return Rect2();4273}4274Rect2 ind_rect = Rect2(Point2(rect.left, rect.top) - _get_screens_origin(), Size2(rect.right - rect.left, rect.bottom - rect.top));4275for (int i = 0; i < get_screen_count(); i++) {4276Rect2 screen_rect = Rect2(screen_get_position(i), screen_get_size(i));4277if (screen_rect.encloses(ind_rect)) {4278return ind_rect;4279}4280}4281return Rect2();4282}42834284void DisplayServerWindows::delete_status_indicator(IndicatorID p_id) {4285ERR_FAIL_COND(!indicators.has(p_id));42864287IndicatorData &idat = indicators[p_id];4288if (idat.icon) {4289DestroyIcon(idat.icon);4290idat.icon_buffer.clear();4291idat.icon = nullptr;4292}42934294NOTIFYICONDATAW ndat;4295ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4296ndat.cbSize = sizeof(NOTIFYICONDATAW);4297ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4298ndat.uID = p_id;4299ndat.uVersion = NOTIFYICON_VERSION;43004301Shell_NotifyIconW(NIM_DELETE, &ndat);4302indicators.erase(p_id);4303}43044305void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {4306_THREAD_SAFE_METHOD_4307#if defined(RD_ENABLED)4308if (rendering_context) {4309rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);4310}4311#endif43124313#if defined(GLES3_ENABLED)4314if (gl_manager_native) {4315gl_manager_native->set_use_vsync(p_window, p_vsync_mode != DisplayServer::VSYNC_DISABLED);4316}4317if (gl_manager_angle) {4318gl_manager_angle->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);4319}4320#endif4321}43224323DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {4324_THREAD_SAFE_METHOD_4325#if defined(RD_ENABLED)4326if (rendering_context) {4327return rendering_context->window_get_vsync_mode(p_window);4328}4329#endif43304331#if defined(GLES3_ENABLED)4332if (gl_manager_native) {4333return gl_manager_native->is_using_vsync(p_window) ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;4334}4335if (gl_manager_angle) {4336return gl_manager_angle->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;4337}4338#endif4339return DisplayServer::VSYNC_ENABLED;4340}43414342void DisplayServerWindows::window_start_drag(WindowID p_window) {4343_THREAD_SAFE_METHOD_43444345ERR_FAIL_COND(!windows.has(p_window));4346WindowData &wd = windows[p_window];43474348if (wd.parent_hwnd) {4349return; // Embedded window.4350}43514352ReleaseCapture();43534354POINT coords;4355GetCursorPos(&coords);4356ScreenToClient(wd.hWnd, &coords);43574358SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(coords.x, coords.y));43594360for (int btn = (int)MouseButton::LEFT; btn <= (int)MouseButton::MB_XBUTTON2; btn++) {4361if (Input::get_singleton()->is_mouse_button_pressed(MouseButton(btn))) {4362Ref<InputEventMouseButton> mb;4363mb.instantiate();4364mb->set_window_id(p_window);4365mb->set_pressed(false);4366mb->set_button_index(MouseButton::LEFT);4367mb->set_position(Vector2(coords.x, coords.y));4368mb->set_global_position(mb->get_position());4369Input::get_singleton()->parse_input_event(mb);4370}4371}4372}43734374void DisplayServerWindows::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {4375_THREAD_SAFE_METHOD_43764377ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);4378ERR_FAIL_COND(!windows.has(p_window));4379WindowData &wd = windows[p_window];43804381if (wd.parent_hwnd) {4382return; // Embedded window.4383}43844385ReleaseCapture();43864387POINT coords;4388GetCursorPos(&coords);4389ScreenToClient(wd.hWnd, &coords);43904391DWORD op = 0;4392switch (p_edge) {4393case DisplayServer::WINDOW_EDGE_TOP_LEFT: {4394op = WMSZ_TOPLEFT;4395} break;4396case DisplayServer::WINDOW_EDGE_TOP: {4397op = WMSZ_TOP;4398} break;4399case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {4400op = WMSZ_TOPRIGHT;4401} break;4402case DisplayServer::WINDOW_EDGE_LEFT: {4403op = WMSZ_LEFT;4404} break;4405case DisplayServer::WINDOW_EDGE_RIGHT: {4406op = WMSZ_RIGHT;4407} break;4408case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {4409op = WMSZ_BOTTOMLEFT;4410} break;4411case DisplayServer::WINDOW_EDGE_BOTTOM: {4412op = WMSZ_BOTTOM;4413} break;4414case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {4415op = WMSZ_BOTTOMRIGHT;4416} break;4417default:4418break;4419}44204421SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_SIZE | op, MAKELPARAM(coords.x, coords.y));44224423for (int btn = (int)MouseButton::LEFT; btn <= (int)MouseButton::MB_XBUTTON2; btn++) {4424if (Input::get_singleton()->is_mouse_button_pressed(MouseButton(btn))) {4425Ref<InputEventMouseButton> mb;4426mb.instantiate();4427mb->set_window_id(p_window);4428mb->set_pressed(false);4429mb->set_button_index(MouseButton::LEFT);4430mb->set_position(Vector2(coords.x, coords.y));4431mb->set_global_position(mb->get_position());4432Input::get_singleton()->parse_input_event(mb);4433}4434}4435}44364437void DisplayServerWindows::set_context(Context p_context) {4438}44394440bool DisplayServerWindows::is_window_transparency_available() const {4441#if defined(RD_ENABLED)4442if (rendering_device && !rendering_device->is_composite_alpha_supported()) {4443return false;4444}4445#endif4446return OS::get_singleton()->is_layered_allowed();4447}44484449#define MI_WP_SIGNATURE 0xFF5157004450#define SIGNATURE_MASK 0xFFFFFF004451// Keeping the name suggested by Microsoft, but this macro really answers:4452// Is this mouse event emulated from touch or pen input?4453#define IsPenEvent(dw) (((dw) & SIGNATURE_MASK) == MI_WP_SIGNATURE)4454// This one tells whether the event comes from touchscreen (and not from pen).4455#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw) & 0x80))44564457void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx) {4458if (touch_state.has(idx) == p_pressed) {4459return;4460}44614462if (p_pressed) {4463touch_state.insert(idx, Vector2(p_x, p_y));4464} else {4465touch_state.erase(idx);4466}44674468Ref<InputEventScreenTouch> event;4469event.instantiate();4470event->set_index(idx);4471event->set_window_id(p_window);4472event->set_pressed(p_pressed);4473event->set_position(Vector2(p_x, p_y));44744475Input::get_singleton()->parse_input_event(event);4476}44774478void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) {4479RBMap<int, Vector2>::Element *curr = touch_state.find(idx);4480if (!curr) {4481return;4482}44834484if (curr->get() == Vector2(p_x, p_y)) {4485return;4486}44874488Ref<InputEventScreenDrag> event;4489event.instantiate();4490event->set_window_id(p_window);4491event->set_index(idx);4492event->set_position(Vector2(p_x, p_y));4493event->set_relative(Vector2(p_x, p_y) - curr->get());4494event->set_relative_screen_position(event->get_relative());44954496Input::get_singleton()->parse_input_event(event);44974498curr->get() = Vector2(p_x, p_y);4499}45004501void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent p_event) {4502if (wd.event_callback.is_valid()) {4503Variant event = int(p_event);4504wd.event_callback.call(event);4505}4506}45074508void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {4509static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event);4510}45114512void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {4513if (in_dispatch_input_event) {4514return;4515}4516in_dispatch_input_event = true;45174518{4519List<WindowID>::Element *E = popup_list.back();4520if (E && Object::cast_to<InputEventKey>(*p_event)) {4521// Redirect keyboard input to active popup.4522if (windows.has(E->get())) {4523Callable callable = windows[E->get()].input_event_callback;4524if (callable.is_valid()) {4525callable.call(p_event);4526}4527}4528in_dispatch_input_event = false;4529return;4530}4531}45324533Ref<InputEventFromWindow> event_from_window = p_event;4534if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {4535// Send to a single window.4536if (windows.has(event_from_window->get_window_id())) {4537Callable callable = windows[event_from_window->get_window_id()].input_event_callback;4538if (callable.is_valid()) {4539callable.call(p_event);4540}4541}4542} else {4543// Send to all windows. Copy all pending callbacks, since callback can erase window.4544Vector<Callable> cbs;4545for (KeyValue<WindowID, WindowData> &E : windows) {4546Callable callable = E.value.input_event_callback;4547if (callable.is_valid()) {4548cbs.push_back(callable);4549}4550}4551for (const Callable &cb : cbs) {4552cb.call(p_event);4553}4554}45554556in_dispatch_input_event = false;4557}45584559LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {4560DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());4561if (ds_win) {4562return ds_win->MouseProc(code, wParam, lParam);4563} else {4564return ::CallNextHookEx(nullptr, code, wParam, lParam);4565}4566}45674568DisplayServer::WindowID DisplayServerWindows::window_get_active_popup() const {4569const List<WindowID>::Element *E = popup_list.back();4570if (E) {4571return E->get();4572} else {4573return INVALID_WINDOW_ID;4574}4575}45764577void DisplayServerWindows::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {4578_THREAD_SAFE_METHOD_45794580ERR_FAIL_COND(!windows.has(p_window));4581WindowData &wd = windows[p_window];4582wd.parent_safe_rect = p_rect;4583}45844585Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const {4586_THREAD_SAFE_METHOD_45874588ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());4589const WindowData &wd = windows[p_window];4590return wd.parent_safe_rect;4591}45924593void DisplayServerWindows::popup_open(WindowID p_window) {4594_THREAD_SAFE_METHOD_45954596bool has_popup_ancestor = false;4597WindowID transient_root = p_window;4598while (true) {4599WindowID parent = windows[transient_root].transient_parent;4600if (parent == INVALID_WINDOW_ID) {4601break;4602} else {4603transient_root = parent;4604if (windows[parent].is_popup) {4605has_popup_ancestor = true;4606break;4607}4608}4609}46104611// Detect tooltips and other similar popups that shouldn't block input to their parent.4612bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);46134614WindowData &wd = windows[p_window];4615if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {4616// Find current popup parent, or root popup if new window is not transient.4617List<WindowID>::Element *C = nullptr;4618List<WindowID>::Element *E = popup_list.back();4619while (E) {4620if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {4621C = E;4622E = E->prev();4623} else {4624break;4625}4626}4627if (C) {4628_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4629}46304631time_since_popup = OS::get_singleton()->get_ticks_msec();4632popup_list.push_back(p_window);4633}4634}46354636void DisplayServerWindows::popup_close(WindowID p_window) {4637_THREAD_SAFE_METHOD_46384639List<WindowID>::Element *E = popup_list.find(p_window);4640while (E) {4641List<WindowID>::Element *F = E->next();4642WindowID win_id = E->get();4643popup_list.erase(E);46444645if (win_id != p_window) {4646// Only request close on related windows, not this window. We are already processing it.4647_send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4648}4649E = F;4650}4651}46524653BitField<DisplayServerWindows::WinKeyModifierMask> DisplayServerWindows::_get_mods() const {4654BitField<WinKeyModifierMask> mask = {};4655static unsigned char keyboard_state[256];4656if (GetKeyboardState((PBYTE)&keyboard_state)) {4657if ((keyboard_state[VK_LSHIFT] & 0x80) || (keyboard_state[VK_RSHIFT] & 0x80)) {4658mask.set_flag(WinKeyModifierMask::SHIFT);4659}4660if ((keyboard_state[VK_LCONTROL] & 0x80) || (keyboard_state[VK_RCONTROL] & 0x80)) {4661mask.set_flag(WinKeyModifierMask::CTRL);4662}4663if ((keyboard_state[VK_LMENU] & 0x80) || (keyboard_state[VK_RMENU] & 0x80)) {4664mask.set_flag(WinKeyModifierMask::ALT);4665}4666if ((keyboard_state[VK_RMENU] & 0x80)) {4667mask.set_flag(WinKeyModifierMask::ALT_GR);4668}4669if ((keyboard_state[VK_LWIN] & 0x80) || (keyboard_state[VK_RWIN] & 0x80)) {4670mask.set_flag(WinKeyModifierMask::META);4671}4672}46734674return mask;4675}46764677LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) {4678_THREAD_SAFE_METHOD_46794680uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;4681if (delta > 250) {4682switch (wParam) {4683case WM_NCLBUTTONDOWN:4684case WM_NCRBUTTONDOWN:4685case WM_NCMBUTTONDOWN:4686case WM_LBUTTONDOWN:4687case WM_RBUTTONDOWN:4688case WM_MBUTTONDOWN: {4689MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam;4690Point2i pos = Point2i(ms->pt.x, ms->pt.y) - _get_screens_origin();4691List<WindowID>::Element *C = nullptr;4692List<WindowID>::Element *E = popup_list.back();4693// Find top popup to close.4694while (E) {4695// Popup window area.4696Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));4697// Area of the parent window, which responsible for opening sub-menu.4698Rect2i safe_rect = window_get_popup_safe_rect(E->get());4699if (win_rect.has_point(pos)) {4700break;4701} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {4702break;4703} else {4704C = E;4705E = E->prev();4706}4707}4708if (C) {4709_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4710return 1;4711}4712} break;4713}4714}4715return ::CallNextHookEx(mouse_monitor, code, wParam, lParam);4716}47174718// Handle a single window message received while CreateWindowEx is still on the stack and our data4719// structures are not fully initialized.4720LRESULT DisplayServerWindows::_handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {4721switch (uMsg) {4722case WM_GETMINMAXINFO: {4723// We receive this during CreateWindowEx and we haven't initialized the window4724// struct, so let Windows figure out the maximized size.4725// Silently forward to user/default.4726} break;4727case WM_NCCREATE: {4728// We tunnel an unowned pointer to our window context (WindowData) through the4729// first possible message (WM_NCCREATE) to fix up our window context collection.4730CREATESTRUCTW *pCreate = (CREATESTRUCTW *)lParam;4731WindowData *pWindowData = reinterpret_cast<WindowData *>(pCreate->lpCreateParams);47324733// Fix this up so we can recognize the remaining messages.4734pWindowData->hWnd = hWnd;47354736#ifdef ACCESSKIT_ENABLED4737if (accessibility_driver && !accessibility_driver->window_create(pWindowData->id, (void *)hWnd)) {4738if (OS::get_singleton()->is_stdout_verbose()) {4739ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");4740}4741memdelete(accessibility_driver);4742accessibility_driver = nullptr;4743}4744#endif4745} break;4746default: {4747// Additional messages during window creation should happen after we fixed4748// up the data structures on WM_NCCREATE, but this might change in the future,4749// so report an error here and then we can implement them.4750ERR_PRINT_ONCE(vformat("Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error.", uMsg));4751} break;4752}47534754if (user_proc) {4755return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);4756}4757return DefWindowProcW(hWnd, uMsg, wParam, lParam);4758}47594760// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events.4761// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures4762LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {4763if (drop_events) {4764if (user_proc) {4765return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);4766} else {4767return DefWindowProcW(hWnd, uMsg, wParam, lParam);4768}4769}47704771WindowID window_id = INVALID_WINDOW_ID;4772bool window_created = false;47734774// Check whether window exists4775// FIXME this is O(n), where n is the set of currently open windows and subwindows4776// we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below4777for (const KeyValue<WindowID, WindowData> &E : windows) {4778if (E.value.hWnd == hWnd) {4779window_id = E.key;4780window_created = true;4781break;4782}4783}47844785// WARNING: We get called with events before the window is registered in our collection4786// specifically, even the call to CreateWindowEx already calls here while still on the stack,4787// so there is no way to store the window handle in our collection before we get here.4788if (!window_created) {4789// don't let code below operate on incompletely initialized window objects or missing window_id4790return _handle_early_window_message(hWnd, uMsg, wParam, lParam);4791}47924793// Process window messages.4794switch (uMsg) {4795case WM_GETOBJECT: {4796get_object_received = true;4797} break;4798case WM_MENUCOMMAND: {4799native_menu->_menu_activate(HMENU(lParam), (int)wParam);4800} break;4801case WM_CREATE: {4802{4803DWORD value = windows[window_id].sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;4804::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));4805}4806if (is_dark_mode_supported() && dark_title_available) {4807BOOL value = is_dark_mode();48084809::DwmSetWindowAttribute(windows[window_id].hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));4810SendMessageW(windows[window_id].hWnd, WM_PAINT, 0, 0);4811}4812} break;4813case WM_NCHITTEST: {4814if (windows[window_id].mpass) {4815return HTTRANSPARENT;4816}4817} break;4818case WM_MOUSEACTIVATE: {4819if (windows[window_id].no_focus || windows[window_id].is_popup) {4820return MA_NOACTIVATE; // Do not activate, but process mouse messages.4821}4822// When embedded, the window is a child of the parent and is not activated4823// by default because it lacks native controls.4824if (windows[window_id].parent_hwnd) {4825SetFocus(windows[window_id].hWnd);4826return MA_ACTIVATE;4827}4828} break;4829case WM_ACTIVATEAPP: {4830bool new_app_focused = (bool)wParam;4831if (new_app_focused == app_focused) {4832break;4833}4834app_focused = new_app_focused;4835if (OS::get_singleton()->get_main_loop()) {4836OS::get_singleton()->get_main_loop()->notification(app_focused ? MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN : MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);4837}4838} break;4839case WM_ACTIVATE: {4840// Activation can happen just after the window has been created, even before the callbacks are set.4841// Therefore, it's safer to defer the delivery of the event.4842// It's important to set an nIDEvent different from the SetTimer for move_timer_id because4843// if the same nIDEvent is passed, the timer is replaced and the same timer_id is returned.4844// The problem with the timer is that the window cannot be resized or the buttons cannot be used correctly4845// if the window is not activated first. This happens because the code in the activation process runs4846// after the mouse click is handled. To address this, the timer is now used only during the window creation,4847// and only as part of the activation process. We don't want 'Input::release_pressed_events()'4848// to be called immediately in '_process_activate_event' when the window is not yet activated,4849// as it would reset the currently pressed keys when hiding a window, which is incorrect behavior.4850windows[window_id].activate_state = GET_WM_ACTIVATE_STATE(wParam, lParam);4851if (windows[window_id].first_activation_done && (windows[window_id].activate_state == WA_ACTIVE || windows[window_id].activate_state == WA_CLICKACTIVE)) {4852_process_activate_event(window_id);4853} else {4854windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_WINDOW_ACTIVATION, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);4855}4856return 0;4857} break;4858case WM_GETMINMAXINFO: {4859if (windows[window_id].resizable && !windows[window_id].fullscreen) {4860// Size of window decorations.4861Size2 decor = window_get_size_with_decorations(window_id) - window_get_size(window_id);48624863MINMAXINFO *min_max_info = (MINMAXINFO *)lParam;4864if (windows[window_id].min_size != Size2()) {4865min_max_info->ptMinTrackSize.x = windows[window_id].min_size.x + decor.x;4866min_max_info->ptMinTrackSize.y = windows[window_id].min_size.y + decor.y;4867}4868if (windows[window_id].max_size != Size2()) {4869min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x;4870min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y;4871}4872if (windows[window_id].borderless) {4873Rect2i screen_rect = screen_get_usable_rect(window_get_current_screen(window_id));48744875// Set the size of (borderless) maximized mode to exclude taskbar (or any other panel) if present.4876min_max_info->ptMaxPosition.x = screen_rect.position.x;4877min_max_info->ptMaxPosition.y = screen_rect.position.y;4878min_max_info->ptMaxSize.x = screen_rect.size.x;4879min_max_info->ptMaxSize.y = screen_rect.size.y;4880}4881return 0;4882}4883} break;4884case WM_ERASEBKGND: {4885Color early_color;4886if (!_get_window_early_clear_override(early_color)) {4887break;4888}4889bool must_recreate_brush = !window_bkg_brush || window_bkg_brush_color != early_color.to_argb32();4890if (must_recreate_brush) {4891if (window_bkg_brush) {4892DeleteObject(window_bkg_brush);4893}4894window_bkg_brush = CreateSolidBrush(RGB(early_color.get_r8(), early_color.get_g8(), early_color.get_b8()));4895}4896HDC hdc = (HDC)wParam;4897RECT rect = {};4898if (GetUpdateRect(hWnd, &rect, true)) {4899FillRect(hdc, &rect, window_bkg_brush);4900}4901return 1;4902} break;4903case WM_PAINT: {4904Main::force_redraw();4905} break;4906case WM_SETTINGCHANGE:4907case WM_SYSCOLORCHANGE: {4908if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {4909if (is_dark_mode_supported() && dark_title_available) {4910BOOL value = is_dark_mode();4911::DwmSetWindowAttribute(windows[window_id].hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));4912}4913}4914if (system_theme_changed.is_valid()) {4915Variant ret;4916Callable::CallError ce;4917system_theme_changed.callp(nullptr, 0, ret, ce);4918if (ce.error != Callable::CallError::CALL_OK) {4919ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));4920}4921}4922} break;4923case WM_THEMECHANGED: {4924if (is_dark_mode_supported() && dark_title_available) {4925BOOL value = is_dark_mode();4926::DwmSetWindowAttribute(windows[window_id].hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));4927}4928} break;4929case WM_SYSCOMMAND: // Intercept system commands.4930{4931switch (wParam) // Check system calls.4932{4933case SC_SCREENSAVE: // Screensaver trying to start?4934case SC_MONITORPOWER: // Monitor trying to enter powersave?4935return 0; // Prevent from happening.4936case SC_KEYMENU:4937Engine *engine = Engine::get_singleton();4938if (((lParam >> 16) <= 0) && !engine->is_project_manager_hint() && !engine->is_editor_hint() && !GLOBAL_GET_CACHED(bool, "application/run/enable_alt_space_menu")) {4939return 0;4940}4941if (!_get_mods().has_flag(WinKeyModifierMask::ALT) || !(GetAsyncKeyState(VK_SPACE) & (1 << 15))) {4942return 0;4943}4944SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_SPACE, 0);4945SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_MENU, 0);4946}4947} break;4948case WM_INDICATOR_CALLBACK_MESSAGE: {4949if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN || lParam == WM_MBUTTONDOWN || lParam == WM_XBUTTONDOWN) {4950IndicatorID iid = (IndicatorID)wParam;4951MouseButton mb = MouseButton::LEFT;4952if (lParam == WM_RBUTTONDOWN) {4953mb = MouseButton::RIGHT;4954} else if (lParam == WM_MBUTTONDOWN) {4955mb = MouseButton::MIDDLE;4956} else if (lParam == WM_XBUTTONDOWN) {4957mb = MouseButton::MB_XBUTTON1;4958}4959if (indicators.has(iid)) {4960if (lParam == WM_RBUTTONDOWN && indicators[iid].menu_rid.is_valid() && native_menu->has_menu(indicators[iid].menu_rid)) {4961NOTIFYICONIDENTIFIER nid;4962ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));4963nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);4964nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;4965nid.uID = iid;4966nid.guidItem = GUID_NULL;49674968RECT rect;4969if (Shell_NotifyIconGetRect(&nid, &rect) == S_OK) {4970native_menu->popup(indicators[iid].menu_rid, Vector2i((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2));4971}4972} else if (indicators[iid].callback.is_valid()) {4973Variant v_button = mb;4974Variant v_pos = mouse_get_position();4975const Variant *v_args[2] = { &v_button, &v_pos };4976Variant ret;4977Callable::CallError ce;4978indicators[iid].callback.callp((const Variant **)&v_args, 2, ret, ce);4979if (ce.error != Callable::CallError::CALL_OK) {4980ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(indicators[iid].callback, v_args, 2, ce)));4981}4982}4983}4984return 0;4985}4986} break;4987case WM_CLOSE: {4988if (windows[window_id].activate_timer_id) {4989KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);4990windows[window_id].activate_timer_id = 0;4991}4992_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);4993return 0;4994}4995case WM_MOUSELEAVE: {4996if (window_mouseover_id == window_id) {4997old_invalid = true;4998window_mouseover_id = INVALID_WINDOW_ID;49995000_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);5001} else if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {5002// This is reached during drag and drop, after dropping in a different window.5003// Once-off notification, must call again.5004track_mouse_leave_event(windows[window_mouseover_id].hWnd);5005}50065007} break;5008case WM_INPUT: {5009if (!use_raw_input) {5010break;5011}50125013UINT dwSize;50145015GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER));5016LPBYTE lpb = new BYTE[dwSize];5017if (lpb == nullptr) {5018return 0;5019}50205021if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {5022OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));5023}50245025RAWINPUT *raw = (RAWINPUT *)lpb;50265027const BitField<WinKeyModifierMask> &mods = _get_mods();5028if (raw->header.dwType == RIM_TYPEKEYBOARD) {5029if (raw->data.keyboard.VKey == VK_SHIFT) {5030// If multiple Shifts are held down at the same time,5031// Windows natively only sends a KEYUP for the last one to be released.5032if (raw->data.keyboard.Flags & RI_KEY_BREAK) {5033// Make sure to check the latest key state since5034// we're in the middle of the message queue.5035if (GetAsyncKeyState(VK_SHIFT) < 0) {5036// A Shift is released, but another Shift is still held5037ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);50385039KeyEvent ke;5040ke.shift = false;5041ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);5042ke.alt = mods.has_flag(WinKeyModifierMask::ALT);5043ke.control = mods.has_flag(WinKeyModifierMask::CTRL);5044ke.meta = mods.has_flag(WinKeyModifierMask::META);5045ke.uMsg = WM_KEYUP;5046ke.window_id = window_id;50475048ke.wParam = VK_SHIFT;5049// data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.5050// Bit 30 -> key was previously down, bit 31 -> key is being released.5051ke.lParam = raw->data.keyboard.MakeCode << 16 | 1 << 30 | 1 << 31;5052key_event_buffer[key_event_pos++] = ke;5053}5054}5055}5056} else if (mouse_mode == MOUSE_MODE_CAPTURED && raw->header.dwType == RIM_TYPEMOUSE) {5057Ref<InputEventMouseMotion> mm;5058mm.instantiate();50595060mm->set_window_id(window_id);5061mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5062mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5063mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5064mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));50655066mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f);50675068mm->set_button_mask(mouse_get_button_state());50695070Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);50715072// Centering just so it works as before.5073POINT pos = { (int)c.x, (int)c.y };5074ClientToScreen(windows[window_id].hWnd, &pos);5075SetCursorPos(pos.x, pos.y);50765077mm->set_position(c);5078mm->set_global_position(c);5079mm->set_velocity(Vector2(0, 0));5080mm->set_screen_velocity(Vector2(0, 0));50815082if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {5083mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));50845085} else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {5086int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);5087int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);5088int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);5089int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);50905091Vector2 abs_pos(5092(double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,5093(double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);50945095POINT coords; // Client coords.5096coords.x = abs_pos.x;5097coords.y = abs_pos.y;50985099ScreenToClient(hWnd, &coords);51005101mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));5102old_x = coords.x;5103old_y = coords.y;5104}5105mm->set_relative_screen_position(mm->get_relative());51065107if ((windows[window_id].window_focused || windows[window_id].is_popup) && mm->get_relative() != Vector2()) {5108Input::get_singleton()->parse_input_event(mm);5109}5110}5111delete[] lpb;5112} break;5113case WT_CSRCHANGE:5114case WT_PROXIMITY: {5115if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5116AXIS pressure;5117if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {5118windows[window_id].min_pressure = int(pressure.axMin);5119windows[window_id].max_pressure = int(pressure.axMax);5120}5121AXIS orientation[3];5122if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {5123windows[window_id].tilt_supported = orientation[0].axResolution && orientation[1].axResolution;5124}5125return 0;5126}5127} break;5128case WT_PACKET: {5129if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5130PACKET packet;5131if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) {5132POINT coords;5133GetCursorPos(&coords);5134ScreenToClient(windows[window_id].hWnd, &coords);51355136windows[window_id].last_pressure_update = 0;51375138float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);5139double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math::PI / 180);5140double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math::PI / 180));5141bool inverted = packet.pkStatus & TPS_INVERT;51425143Vector2 tilt = (windows[window_id].tilt_supported) ? Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)) : Vector2();51445145// Nothing changed, ignore event.5146if (!old_invalid && coords.x == old_x && coords.y == old_y && windows[window_id].last_pressure == pressure && windows[window_id].last_tilt == tilt && windows[window_id].last_pen_inverted == inverted) {5147break;5148}51495150windows[window_id].last_pressure = pressure;5151windows[window_id].last_tilt = tilt;5152windows[window_id].last_pen_inverted = inverted;51535154// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5155if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5156break;5157}51585159const BitField<WinKeyModifierMask> &mods = _get_mods();5160Ref<InputEventMouseMotion> mm;5161mm.instantiate();5162mm->set_window_id(window_id);5163mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5164mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5165mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5166mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));51675168mm->set_pressure(windows[window_id].last_pressure);5169mm->set_tilt(windows[window_id].last_tilt);5170mm->set_pen_inverted(windows[window_id].last_pen_inverted);51715172mm->set_button_mask(mouse_get_button_state());51735174mm->set_position(Vector2(coords.x, coords.y));5175mm->set_global_position(Vector2(coords.x, coords.y));51765177if (mouse_mode == MOUSE_MODE_CAPTURED) {5178Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5179old_x = c.x;5180old_y = c.y;51815182if (mm->get_position() == c) {5183center = c;5184return 0;5185}51865187Point2i ncenter = mm->get_position();5188center = ncenter;5189POINT pos = { (int)c.x, (int)c.y };5190ClientToScreen(windows[window_id].hWnd, &pos);5191SetCursorPos(pos.x, pos.y);5192}51935194mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5195mm->set_screen_velocity(mm->get_velocity());51965197if (old_invalid) {5198old_x = mm->get_position().x;5199old_y = mm->get_position().y;5200old_invalid = false;5201}52025203mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5204mm->set_relative_screen_position(mm->get_relative());5205old_x = mm->get_position().x;5206old_y = mm->get_position().y;52075208if (windows[window_id].window_focused || window_get_active_popup() == window_id) {5209Input::get_singleton()->parse_input_event(mm);5210}5211}5212return 0;5213}5214} break;5215case WM_POINTERENTER: {5216if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5217break;5218}52195220if (tablet_get_current_driver() != "winink") {5221break;5222}52235224uint32_t pointer_id = LOWORD(wParam);5225POINTER_INPUT_TYPE pointer_type = PT_POINTER;5226if (!GetPointerType(pointer_id, &pointer_type)) {5227break;5228}52295230if (pointer_type != PT_PEN) {5231break;5232}52335234pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;5235windows[window_id].block_mm = true;5236return 0;5237} break;5238case WM_POINTERLEAVE: {5239pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;5240windows[window_id].block_mm = false;5241return 0;5242} break;5243case WM_POINTERDOWN:5244case WM_POINTERUP: {5245if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5246break;5247}52485249if (tablet_get_current_driver() != "winink") {5250break;5251}52525253uint32_t pointer_id = LOWORD(wParam);5254POINTER_INPUT_TYPE pointer_type = PT_POINTER;5255if (!GetPointerType(pointer_id, &pointer_type)) {5256break;5257}52585259if (pointer_type != PT_PEN) {5260break;5261}52625263Ref<InputEventMouseButton> mb;5264mb.instantiate();5265mb->set_window_id(window_id);52665267BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;5268if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {5269last_button_state.set_flag(MouseButtonMask::LEFT);5270mb->set_button_index(MouseButton::LEFT);5271}5272if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {5273last_button_state.set_flag(MouseButtonMask::RIGHT);5274mb->set_button_index(MouseButton::RIGHT);5275}5276if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {5277last_button_state.set_flag(MouseButtonMask::MIDDLE);5278mb->set_button_index(MouseButton::MIDDLE);5279}5280if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {5281last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);5282mb->set_button_index(MouseButton::MB_XBUTTON1);5283}5284if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {5285last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);5286mb->set_button_index(MouseButton::MB_XBUTTON2);5287}5288mb->set_button_mask(last_button_state);52895290const BitField<WinKeyModifierMask> &mods = _get_mods();5291mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5292mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5293mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5294mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));52955296POINT coords; // Client coords.5297coords.x = GET_X_LPARAM(lParam);5298coords.y = GET_Y_LPARAM(lParam);52995300// Note: Handle popup closing here, since mouse event is not emulated and hook will not be called.5301uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;5302if (delta > 250) {5303Point2i pos = Point2i(coords.x, coords.y) - _get_screens_origin();5304List<WindowID>::Element *C = nullptr;5305List<WindowID>::Element *E = popup_list.back();5306// Find top popup to close.5307while (E) {5308// Popup window area.5309Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));5310// Area of the parent window, which responsible for opening sub-menu.5311Rect2i safe_rect = window_get_popup_safe_rect(E->get());5312if (win_rect.has_point(pos)) {5313break;5314} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {5315break;5316} else {5317C = E;5318E = E->prev();5319}5320}5321if (C) {5322_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);5323}5324}53255326int64_t pen_id = GET_POINTERID_WPARAM(wParam);5327if (uMsg == WM_POINTERDOWN) {5328mb->set_pressed(true);5329if (pointer_down_time.has(pen_id) && (pointer_prev_button[pen_id] == mb->get_button_index()) && (Math::abs(coords.y - pointer_last_pos[pen_id].y) < GetSystemMetrics(SM_CYDOUBLECLK)) && GetMessageTime() - pointer_down_time[pen_id] < (LONG)GetDoubleClickTime()) {5330mb->set_double_click(true);5331pointer_down_time[pen_id] = 0;5332} else {5333pointer_down_time[pen_id] = GetMessageTime();5334pointer_prev_button[pen_id] = mb->get_button_index();5335pointer_last_pos[pen_id] = Vector2(coords.x, coords.y);5336}5337pointer_button[pen_id] = mb->get_button_index();5338} else {5339if (!pointer_button.has(pen_id)) {5340return 0;5341}5342mb->set_pressed(false);5343mb->set_button_index(pointer_button[pen_id]);5344pointer_button[pen_id] = MouseButton::NONE;5345}53465347ScreenToClient(windows[window_id].hWnd, &coords);53485349mb->set_position(Vector2(coords.x, coords.y));5350mb->set_global_position(Vector2(coords.x, coords.y));53515352Input::get_singleton()->parse_input_event(mb);53535354return 0;5355} break;5356case WM_POINTERUPDATE: {5357if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5358break;5359}53605361if (tablet_get_current_driver() != "winink") {5362break;5363}53645365uint32_t pointer_id = LOWORD(wParam);5366POINTER_INPUT_TYPE pointer_type = PT_POINTER;5367if (!GetPointerType(pointer_id, &pointer_type)) {5368break;5369}53705371if (pointer_type != PT_PEN) {5372break;5373}53745375POINTER_PEN_INFO pen_info;5376if (!GetPointerPenInfo(pointer_id, &pen_info)) {5377break;5378}53795380if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5381// Universal translation enabled; ignore OS translation.5382LPARAM extra = GetMessageExtraInfo();5383if (IsTouchEvent(extra)) {5384break;5385}5386}53875388if (window_mouseover_id != window_id) {5389// Mouse enter.53905391if (mouse_mode != MOUSE_MODE_CAPTURED) {5392if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {5393// Leave previous window.5394_send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);5395}5396_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);5397}53985399CursorShape c = cursor_shape;5400cursor_shape = CURSOR_MAX;5401cursor_set_shape(c);5402window_mouseover_id = window_id;54035404// Once-off notification, must call again.5405track_mouse_leave_event(hWnd);5406}54075408// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5409if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5410break;5411}54125413Ref<InputEventMouseMotion> mm;5414mm.instantiate();54155416mm->set_window_id(window_id);5417if (pen_info.penMask & PEN_MASK_PRESSURE) {5418mm->set_pressure((float)pen_info.pressure / 1024);5419} else {5420mm->set_pressure((HIWORD(wParam) & POINTER_MESSAGE_FLAG_FIRSTBUTTON) ? 1.0f : 0.0f);5421}5422if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) {5423mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90));5424}5425mm->set_pen_inverted(pen_info.penFlags & (PEN_FLAG_INVERTED | PEN_FLAG_ERASER));54265427const BitField<WinKeyModifierMask> &mods = _get_mods();5428mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5429mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5430mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5431mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));54325433BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;5434if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {5435last_button_state.set_flag(MouseButtonMask::LEFT);5436}5437if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {5438last_button_state.set_flag(MouseButtonMask::RIGHT);5439}5440if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {5441last_button_state.set_flag(MouseButtonMask::MIDDLE);5442}5443if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {5444last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);5445}5446if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {5447last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);5448}5449mm->set_button_mask(last_button_state);54505451POINT coords; // Client coords.5452coords.x = GET_X_LPARAM(lParam);5453coords.y = GET_Y_LPARAM(lParam);54545455ScreenToClient(windows[window_id].hWnd, &coords);54565457mm->set_position(Vector2(coords.x, coords.y));5458mm->set_global_position(Vector2(coords.x, coords.y));54595460if (mouse_mode == MOUSE_MODE_CAPTURED) {5461Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5462old_x = c.x;5463old_y = c.y;54645465if (mm->get_position() == c) {5466center = c;5467return 0;5468}54695470Point2i ncenter = mm->get_position();5471center = ncenter;5472POINT pos = { (int)c.x, (int)c.y };5473ClientToScreen(hWnd, &pos);5474SetCursorPos(pos.x, pos.y);5475}54765477mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5478mm->set_screen_velocity(mm->get_velocity());54795480if (old_invalid) {5481old_x = mm->get_position().x;5482old_y = mm->get_position().y;5483old_invalid = false;5484}54855486mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5487mm->set_relative_screen_position(mm->get_relative());5488old_x = mm->get_position().x;5489old_y = mm->get_position().y;5490if (windows[window_id].window_focused || window_get_active_popup() == window_id) {5491Input::get_singleton()->parse_input_event(mm);5492}54935494return 0; // Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event.5495} break;5496case WM_MOUSEMOVE: {5497if (windows[window_id].block_mm) {5498break;5499}55005501if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5502break;5503}55045505if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5506// Universal translation enabled; ignore OS translation.5507LPARAM extra = GetMessageExtraInfo();5508if (IsTouchEvent(extra)) {5509break;5510}5511}55125513DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position());5514if (windows.has(over_id) && !Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) {5515// Don't consider the windowborder as part of the window.5516over_id = INVALID_WINDOW_ID;5517}5518if (window_mouseover_id != over_id) {5519// Mouse enter.55205521if (mouse_mode != MOUSE_MODE_CAPTURED) {5522if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {5523// Leave previous window.5524_send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);5525}55265527if (over_id != INVALID_WINDOW_ID && windows.has(over_id)) {5528_send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER);5529}5530}55315532CursorShape c = cursor_shape;5533cursor_shape = CURSOR_MAX;5534cursor_set_shape(c);5535window_mouseover_id = over_id;55365537// Once-off notification, must call again.5538track_mouse_leave_event(hWnd);5539}55405541// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5542if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5543break;5544}55455546DisplayServer::WindowID receiving_window_id = window_id;5547if (!windows[window_id].no_focus) {5548receiving_window_id = _get_focused_window_or_popup();5549if (receiving_window_id == INVALID_WINDOW_ID) {5550receiving_window_id = window_id;5551}5552}55535554const BitField<WinKeyModifierMask> &mods = _get_mods();5555Ref<InputEventMouseMotion> mm;5556mm.instantiate();5557mm->set_window_id(receiving_window_id);5558mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5559mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5560mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5561mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));55625563if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5564// Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not updated recently.5565if (windows[window_id].last_pressure_update < 10) {5566windows[window_id].last_pressure_update++;5567} else {5568windows[window_id].last_tilt = Vector2();5569windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;5570windows[window_id].last_pen_inverted = false;5571}5572} else {5573windows[window_id].last_tilt = Vector2();5574windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;5575windows[window_id].last_pen_inverted = false;5576}55775578mm->set_pressure(windows[window_id].last_pressure);5579mm->set_tilt(windows[window_id].last_tilt);5580mm->set_pen_inverted(windows[window_id].last_pen_inverted);55815582mm->set_button_mask(mouse_get_button_state());55835584mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));5585mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));55865587if (mouse_mode == MOUSE_MODE_CAPTURED) {5588Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5589old_x = c.x;5590old_y = c.y;55915592if (mm->get_position() == c) {5593center = c;5594return 0;5595}55965597Point2i ncenter = mm->get_position();5598center = ncenter;5599POINT pos = { (int)c.x, (int)c.y };5600ClientToScreen(windows[window_id].hWnd, &pos);5601SetCursorPos(pos.x, pos.y);5602}56035604mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5605mm->set_screen_velocity(mm->get_velocity());56065607if (old_invalid) {5608old_x = mm->get_position().x;5609old_y = mm->get_position().y;5610old_invalid = false;5611}56125613mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5614mm->set_relative_screen_position(mm->get_relative());5615old_x = mm->get_position().x;5616old_y = mm->get_position().y;56175618if (receiving_window_id != window_id) {5619// Adjust event position relative to window distance when event is sent to a different window.5620mm->set_position(mm->get_position() - window_get_position(receiving_window_id) + window_get_position(window_id));5621mm->set_global_position(mm->get_position());5622}56235624Input::get_singleton()->parse_input_event(mm);56255626} break;5627case WM_LBUTTONDOWN:5628case WM_LBUTTONUP:5629if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5630// Universal translation enabled; ignore OS translations for left button.5631LPARAM extra = GetMessageExtraInfo();5632if (IsTouchEvent(extra)) {5633break;5634}5635}5636[[fallthrough]];5637case WM_MBUTTONDOWN:5638case WM_MBUTTONUP:5639case WM_RBUTTONDOWN:5640case WM_RBUTTONUP:5641case WM_MOUSEWHEEL:5642case WM_MOUSEHWHEEL:5643case WM_LBUTTONDBLCLK:5644case WM_MBUTTONDBLCLK:5645case WM_RBUTTONDBLCLK:5646case WM_XBUTTONDBLCLK:5647case WM_XBUTTONDOWN:5648case WM_XBUTTONUP: {5649Ref<InputEventMouseButton> mb;5650mb.instantiate();5651mb->set_window_id(window_id);56525653switch (uMsg) {5654case WM_LBUTTONDOWN: {5655mb->set_pressed(true);5656mb->set_button_index(MouseButton::LEFT);5657} break;5658case WM_LBUTTONUP: {5659mb->set_pressed(false);5660mb->set_button_index(MouseButton::LEFT);5661} break;5662case WM_MBUTTONDOWN: {5663mb->set_pressed(true);5664mb->set_button_index(MouseButton::MIDDLE);5665} break;5666case WM_MBUTTONUP: {5667mb->set_pressed(false);5668mb->set_button_index(MouseButton::MIDDLE);5669} break;5670case WM_RBUTTONDOWN: {5671mb->set_pressed(true);5672mb->set_button_index(MouseButton::RIGHT);5673} break;5674case WM_RBUTTONUP: {5675mb->set_pressed(false);5676mb->set_button_index(MouseButton::RIGHT);5677} break;5678case WM_LBUTTONDBLCLK: {5679mb->set_pressed(true);5680mb->set_button_index(MouseButton::LEFT);5681mb->set_double_click(true);5682} break;5683case WM_RBUTTONDBLCLK: {5684mb->set_pressed(true);5685mb->set_button_index(MouseButton::RIGHT);5686mb->set_double_click(true);5687} break;5688case WM_MBUTTONDBLCLK: {5689mb->set_pressed(true);5690mb->set_button_index(MouseButton::MIDDLE);5691mb->set_double_click(true);5692} break;5693case WM_MOUSEWHEEL: {5694mb->set_pressed(true);5695int motion = (short)HIWORD(wParam);5696if (!motion) {5697return 0;5698}56995700if (motion > 0) {5701mb->set_button_index(MouseButton::WHEEL_UP);5702} else {5703mb->set_button_index(MouseButton::WHEEL_DOWN);5704}5705mb->set_factor(std::fabs((double)motion / (double)WHEEL_DELTA));5706} break;5707case WM_MOUSEHWHEEL: {5708mb->set_pressed(true);5709int motion = (short)HIWORD(wParam);5710if (!motion) {5711return 0;5712}57135714if (motion < 0) {5715mb->set_button_index(MouseButton::WHEEL_LEFT);5716} else {5717mb->set_button_index(MouseButton::WHEEL_RIGHT);5718}5719mb->set_factor(std::fabs((double)motion / (double)WHEEL_DELTA));5720} break;5721case WM_XBUTTONDOWN: {5722mb->set_pressed(true);5723if (HIWORD(wParam) == XBUTTON1) {5724mb->set_button_index(MouseButton::MB_XBUTTON1);5725} else {5726mb->set_button_index(MouseButton::MB_XBUTTON2);5727}5728} break;5729case WM_XBUTTONUP: {5730mb->set_pressed(false);5731if (HIWORD(wParam) == XBUTTON1) {5732mb->set_button_index(MouseButton::MB_XBUTTON1);5733} else {5734mb->set_button_index(MouseButton::MB_XBUTTON2);5735}5736} break;5737case WM_XBUTTONDBLCLK: {5738mb->set_pressed(true);5739if (HIWORD(wParam) == XBUTTON1) {5740mb->set_button_index(MouseButton::MB_XBUTTON1);5741} else {5742mb->set_button_index(MouseButton::MB_XBUTTON2);5743}5744mb->set_double_click(true);5745} break;5746default: {5747return 0;5748}5749}57505751const BitField<WinKeyModifierMask> &mods = _get_mods();5752mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5753mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5754mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5755mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));57565757if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) {5758MouseButtonMask mask = mouse_button_to_mask(mb->get_button_index());5759BitField<MouseButtonMask> scroll_mask = mouse_get_button_state();5760scroll_mask.set_flag(mask);5761mb->set_button_mask(scroll_mask);5762} else {5763mb->set_button_mask(mouse_get_button_state());5764}57655766mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));57675768if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {5769mb->set_position(Vector2(old_x, old_y));5770}57715772if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {5773if (mb->is_pressed()) {5774if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED) {5775SetCapture(hWnd);5776}5777} else {5778if (--pressrc <= 0 || mouse_get_button_state().is_empty()) {5779if (mouse_mode != MOUSE_MODE_CAPTURED) {5780ReleaseCapture();5781}5782pressrc = 0;5783}5784}5785} else {5786// For reasons unknown to humanity, wheel comes in screen coordinates.5787POINT coords;5788coords.x = mb->get_position().x;5789coords.y = mb->get_position().y;57905791ScreenToClient(hWnd, &coords);57925793mb->set_position(Vector2(coords.x, coords.y));5794}57955796mb->set_global_position(mb->get_position());57975798Input::get_singleton()->parse_input_event(mb);5799if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) {5800// Send release for mouse wheel.5801Ref<InputEventMouseButton> mbd = mb->duplicate();5802mbd->set_window_id(window_id);5803mbd->set_button_mask(mouse_get_button_state());5804mbd->set_pressed(false);5805Input::get_singleton()->parse_input_event(mbd);5806}58075808// Propagate the button up event to the window on which the button down5809// event was triggered. This is needed for drag & drop to work between windows,5810// because the engine expects events to keep being processed5811// on the same window dragging started.5812if (mb->is_pressed()) {5813last_mouse_button_down_window = window_id;5814} else if (last_mouse_button_down_window != INVALID_WINDOW_ID) {5815mb->set_window_id(last_mouse_button_down_window);5816last_mouse_button_down_window = INVALID_WINDOW_ID;5817}5818} break;58195820case WM_WINDOWPOSCHANGED: {5821WindowData &window = windows[window_id];58225823Vector2i off = (window.multiwindow_fs || (!window.fullscreen && window.borderless && window.maximized)) ? _get_screen_expand_offset(window_get_current_screen(window_id)) : Vector2i();5824Rect2i window_client_rect;5825Rect2i window_rect;5826{5827RECT rect;5828GetClientRect(hWnd, &rect);5829ClientToScreen(hWnd, (POINT *)&rect.left);5830ClientToScreen(hWnd, (POINT *)&rect.right);5831window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left - off.x, rect.bottom - rect.top - off.y);5832window_client_rect.position -= _get_screens_origin();58335834RECT wrect;5835GetWindowRect(hWnd, &wrect);5836window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left - off.x, wrect.bottom - wrect.top - off.y);5837window_rect.position -= _get_screens_origin();5838}58395840WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam;58415842bool rect_changed = false;5843if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) {5844int screen_id = window_get_current_screen(window_id);5845Size2i screen_size = screen_get_size(screen_id);5846Point2i screen_position = screen_get_position(screen_id);5847Rect2i usable = screen_get_usable_rect(screen_id);58485849window.maximized = false;5850window.minimized = false;5851window.fullscreen = false;58525853if (IsIconic(hWnd)) {5854window.minimized = true;5855} else if (IsZoomed(hWnd)) {5856window.maximized = true;58575858// If maximized_window_size == screen_size add 1px border to prevent switching to exclusive_fs.5859if (!window.maximized_fs && window.borderless && window_rect.position == screen_position && window_rect.size == screen_size) {5860// Window (borderless) was just maximized and the covers the entire screen.5861window.maximized_fs = true;5862_update_window_style(window_id, false);5863}5864if (window.borderless && (screen_size != usable.size || screen_position != usable.position)) {5865Point2 pos = usable.position + _get_screens_origin();5866Size2 size = usable.size;5867MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);5868}5869} else if (window_rect.position == screen_position && window_rect.size == screen_size) {5870window.fullscreen = true;5871} else if (window.borderless && usable.position == window_rect.position && usable.size == window_rect.size) {5872window.maximized = true;5873}58745875if (window.maximized_fs && !window.maximized) {5876// Window (maximized and covering fullscreen) was just non-maximized.5877window.maximized_fs = false;5878_update_window_style(window_id, false);5879}58805881if (!window.minimized) {5882window.width = window_client_rect.size.width;5883window.height = window_client_rect.size.height;5884window.width_with_decorations = window_rect.size.width;5885window.height_with_decorations = window_rect.size.height;58865887rect_changed = true;5888}5889#if defined(RD_ENABLED)5890if (window.create_completed && rendering_context && window.rendering_context_window_created) {5891// Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.5892rendering_context->window_set_size(window_id, window.width + off.x, window.height + off.y);5893}5894#endif5895#if defined(GLES3_ENABLED)5896if (window.create_completed && gl_manager_native && window.gl_native_window_created) {5897gl_manager_native->window_resize(window_id, window.width + off.x, window.height + off.y);5898}5899if (window.create_completed && gl_manager_angle && window.gl_angle_window_created) {5900gl_manager_angle->window_resize(window_id, window.width + off.x, window.height + off.y);5901}5902#endif5903}59045905if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) {5906window.last_pos = window_client_rect.position;5907rect_changed = true;5908}59095910if (rect_changed) {5911if (window.rect_changed_callback.is_valid()) {5912window.rect_changed_callback.call(Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height));5913}59145915// Update cursor clip region after window rect has changed.5916if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {5917RECT crect;5918GetClientRect(window.hWnd, &crect);5919crect.right -= off.x;5920crect.bottom -= off.y;5921ClientToScreen(window.hWnd, (POINT *)&crect.left);5922ClientToScreen(window.hWnd, (POINT *)&crect.right);5923ClipCursor(&crect);5924}59255926if (!window.minimized && window.was_fullscreen_pre_min) {5927// Restore fullscreen mode if window was in fullscreen before it was minimized.5928int cs = window_get_current_screen(window_id);5929Point2 pos = screen_get_position(cs) + _get_screens_origin();5930Size2 size = screen_get_size(cs);59315932window.was_fullscreen_pre_min = false;5933window.fullscreen = true;5934window.maximized = false;5935window.minimized = false;59365937_update_window_style(window_id, false);59385939MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);5940}5941} else {5942if (window.parent_hwnd) {5943// WM_WINDOWPOSCHANGED is sent when the parent changes.5944// If we are supposed to have a parent and now we don't, it's likely5945// because the parent was closed. We will close our window as well.5946// This prevents an embedded game from staying alive when the editor is closed or crashes.5947if (!GetParent(window.hWnd)) {5948SendMessage(window.hWnd, WM_CLOSE, 0, 0);5949}5950}5951}59525953// Return here to prevent WM_MOVE and WM_SIZE from being sent5954// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks5955return 0;5956} break;59575958case WM_ENTERSIZEMOVE: {5959Input::get_singleton()->release_pressed_events();5960windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_MOVE_REDRAW, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);5961} break;5962case WM_EXITSIZEMOVE: {5963KillTimer(windows[window_id].hWnd, windows[window_id].move_timer_id);5964windows[window_id].move_timer_id = 0;5965// Reset the correct mouse mode because we couldn't call ReleaseCapture in5966// _set_mouse_mode_impl while in _process_activate_event (because the user was moving a window).5967_set_mouse_mode_impl(mouse_mode);5968} break;5969case WM_TIMER: {5970if (wParam == windows[window_id].move_timer_id) {5971_THREAD_SAFE_UNLOCK_5972_process_key_events();5973if (!Main::is_iterating()) {5974Main::iteration();5975}5976_THREAD_SAFE_LOCK_5977} else if (wParam == windows[window_id].activate_timer_id) {5978_process_activate_event(window_id);5979KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);5980windows[window_id].activate_timer_id = 0;5981windows[window_id].first_activation_done = true;5982}5983} break;5984case WM_SYSKEYUP:5985case WM_KEYUP:5986case WM_SYSKEYDOWN:5987case WM_KEYDOWN: {5988if (windows[window_id].ime_suppress_next_keyup && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)) {5989windows[window_id].ime_suppress_next_keyup = false;5990break;5991}5992if (windows[window_id].ime_in_progress) {5993break;5994}59955996if (mouse_mode == MOUSE_MODE_CAPTURED) {5997// When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves5998if (wParam == VK_F4 && _get_mods().has_flag(WinKeyModifierMask::ALT) && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) {5999_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);6000}6001}6002[[fallthrough]];6003}6004case WM_CHAR: {6005ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);6006const BitField<WinKeyModifierMask> &mods = _get_mods();60076008KeyEvent ke;6009ke.shift = mods.has_flag(WinKeyModifierMask::SHIFT);6010ke.alt = mods.has_flag(WinKeyModifierMask::ALT);6011ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);6012ke.control = mods.has_flag(WinKeyModifierMask::CTRL);6013ke.meta = mods.has_flag(WinKeyModifierMask::META);6014ke.uMsg = uMsg;6015ke.window_id = window_id;60166017if (ke.uMsg == WM_SYSKEYDOWN) {6018ke.uMsg = WM_KEYDOWN;6019}6020if (ke.uMsg == WM_SYSKEYUP) {6021ke.uMsg = WM_KEYUP;6022}60236024ke.wParam = wParam;6025ke.lParam = lParam;6026key_event_buffer[key_event_pos++] = ke;60276028} break;6029case WM_IME_COMPOSITION: {6030CANDIDATEFORM cf;6031cf.dwIndex = 0;60326033cf.dwStyle = CFS_CANDIDATEPOS;6034cf.ptCurrentPos.x = windows[window_id].im_position.x;6035cf.ptCurrentPos.y = windows[window_id].im_position.y;6036ImmSetCandidateWindow(windows[window_id].im_himc, &cf);60376038cf.dwStyle = CFS_EXCLUDE;6039cf.rcArea.left = windows[window_id].im_position.x;6040cf.rcArea.right = windows[window_id].im_position.x;6041cf.rcArea.top = windows[window_id].im_position.y;6042cf.rcArea.bottom = windows[window_id].im_position.y;6043ImmSetCandidateWindow(windows[window_id].im_himc, &cf);60446045if (windows[window_id].ime_active) {6046SetCaretPos(windows[window_id].im_position.x, windows[window_id].im_position.y);6047OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);6048}6049} break;6050case WM_INPUTLANGCHANGEREQUEST: {6051// FIXME: Do something?6052} break;6053case WM_IME_STARTCOMPOSITION: {6054if (windows[window_id].ime_active) {6055windows[window_id].ime_in_progress = true;6056if (key_event_pos > 0) {6057key_event_pos--;6058}6059}6060return 0;6061} break;6062case WM_IME_ENDCOMPOSITION: {6063if (windows[window_id].ime_active) {6064windows[window_id].ime_in_progress = false;6065windows[window_id].ime_suppress_next_keyup = true;6066}6067return 0;6068} break;6069case WM_IME_NOTIFY: {6070return 0;6071} break;6072case WM_TOUCH: {6073BOOL bHandled = FALSE;6074UINT cInputs = LOWORD(wParam);6075PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);6076if (pInputs) {6077if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {6078for (UINT i = 0; i < cInputs; i++) {6079TOUCHINPUT ti = pInputs[i];6080POINT touch_pos = {6081TOUCH_COORD_TO_PIXEL(ti.x),6082TOUCH_COORD_TO_PIXEL(ti.y),6083};6084ScreenToClient(hWnd, &touch_pos);6085// Do something with each touch input entry.6086if (ti.dwFlags & TOUCHEVENTF_MOVE) {6087_drag_event(window_id, touch_pos.x, touch_pos.y, ti.dwID);6088} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {6089_touch_event(window_id, ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);6090}6091}6092bHandled = TRUE;6093} else {6094// TODO: Handle the error here.6095}6096memdelete_arr(pInputs);6097} else {6098// TODO: Handle the error here, probably out of memory.6099}6100if (bHandled) {6101CloseTouchInputHandle((HTOUCHINPUT)lParam);6102return 0;6103}61046105} break;6106case WM_DESTROY: {6107#ifdef ACCESSKIT_ENABLED6108if (accessibility_driver) {6109accessibility_driver->window_destroy(window_id);6110}6111#endif6112Input::get_singleton()->flush_buffered_events();6113if (window_mouseover_id == window_id) {6114window_mouseover_id = INVALID_WINDOW_ID;6115_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);6116}6117} break;6118case WM_SETCURSOR: {6119if (LOWORD(lParam) == HTCLIENT) {6120if (windows[window_id].window_focused && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) {6121// Hide the cursor.6122if (hCursor == nullptr) {6123hCursor = SetCursor(nullptr);6124} else {6125SetCursor(nullptr);6126}6127} else {6128if (hCursor != nullptr) {6129CursorShape c = cursor_shape;6130cursor_shape = CURSOR_MAX;6131cursor_set_shape(c);6132hCursor = nullptr;6133}6134}6135}6136} break;6137default: {6138if (user_proc) {6139return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);6140}6141}6142}61436144return DefWindowProcW(hWnd, uMsg, wParam, lParam);6145}61466147LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {6148DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());6149if (ds_win) {6150return ds_win->WndProc(hWnd, uMsg, wParam, lParam);6151} else {6152return DefWindowProcW(hWnd, uMsg, wParam, lParam);6153}6154}61556156void DisplayServerWindows::_process_activate_event(WindowID p_window_id) {6157WindowData &wd = windows[p_window_id];6158if (wd.activate_state == WA_ACTIVE || wd.activate_state == WA_CLICKACTIVE) {6159last_focused_window = p_window_id;6160_set_mouse_mode_impl(mouse_mode);6161if (!IsIconic(wd.hWnd)) {6162SetFocus(wd.hWnd);6163}6164wd.window_focused = true;6165#ifdef ACCESSKIT_ENABLED6166if (accessibility_driver) {6167accessibility_driver->accessibility_set_window_focused(p_window_id, true);6168}6169#endif6170_send_window_event(wd, WINDOW_EVENT_FOCUS_IN);6171} else { // WM_INACTIVE.6172Input::get_singleton()->release_pressed_events();6173track_mouse_leave_event(wd.hWnd);6174// Release capture unconditionally because it can be set due to dragging, in addition to captured mode.6175// When the user is moving a window, it's important to not ReleaseCapture because it will cause6176// the window movement to stop and if the user tries to move the Windows when it's not activated,6177// it will prevent the window movement. If we are here and a window is moving, it's because we had multiple6178// opened windows in the editor and we are definitively not in a middle of dragging.6179if (!_has_moving_window()) {6180ReleaseCapture();6181}6182wd.window_focused = false;6183#ifdef ACCESSKIT_ENABLED6184if (accessibility_driver) {6185accessibility_driver->accessibility_set_window_focused(p_window_id, false);6186}6187#endif6188_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);6189}61906191if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {6192wintab_WTEnable(wd.wtctx, wd.activate_state);6193}6194}61956196void DisplayServerWindows::_process_key_events() {6197for (int i = 0; i < key_event_pos; i++) {6198KeyEvent &ke = key_event_buffer[i];6199switch (ke.uMsg) {6200case WM_CHAR: {6201// Extended keys should only be processed as WM_KEYDOWN message.6202if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) {6203static char32_t prev_wc = 0;6204char32_t unicode = ke.wParam;6205if ((unicode & 0xfffffc00) == 0xd800) {6206if (prev_wc != 0) {6207ERR_PRINT("invalid utf16 surrogate input");6208}6209prev_wc = unicode;6210break; // Skip surrogate.6211} else if ((unicode & 0xfffffc00) == 0xdc00) {6212if (prev_wc == 0) {6213ERR_PRINT("invalid utf16 surrogate input");6214break; // Skip invalid surrogate.6215}6216unicode = (prev_wc << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);6217prev_wc = 0;6218} else {6219prev_wc = 0;6220}6221Ref<InputEventKey> k;6222k.instantiate();62236224UINT vk = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK);6225bool is_oem = (vk >= 0xB8) && (vk <= 0xE6);6226Key keycode = KeyMappingWindows::get_keysym(vk);6227Key key_label = keycode;6228Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));62296230static BYTE keyboard_state[256];6231memset(keyboard_state, 0, 256);6232wchar_t chars[256] = {};6233UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX);6234if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) {6235String keysym = String::utf16((char16_t *)chars, 255);6236if (!keysym.is_empty()) {6237char32_t unicode_value = keysym[0];6238// For printable ASCII characters (0x20-0x7E), override the original keycode with the character value.6239if (is_oem && Key::SPACE <= (Key)unicode_value && (Key)unicode_value <= Key::ASCIITILDE) {6240keycode = fix_keycode(unicode_value, (Key)unicode_value);6241}6242key_label = fix_key_label(unicode_value, keycode);6243}6244}62456246k->set_window_id(ke.window_id);6247if (keycode != Key::SHIFT) {6248k->set_shift_pressed(ke.shift);6249}6250if (keycode != Key::ALT) {6251k->set_alt_pressed(ke.alt);6252}6253if (keycode != Key::CTRL) {6254k->set_ctrl_pressed(ke.control);6255}6256if (keycode != Key::META) {6257k->set_meta_pressed(ke.meta);6258}6259k->set_pressed(true);6260k->set_keycode(keycode);6261k->set_physical_keycode(physical_keycode);6262k->set_key_label(key_label);6263k->set_unicode(fix_unicode(unicode));6264if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {6265k->set_alt_pressed(false);6266k->set_ctrl_pressed(false);6267}62686269Input::get_singleton()->parse_input_event(k);6270} else {6271// Do nothing.6272}6273} break;6274case WM_KEYUP:6275case WM_KEYDOWN: {6276Ref<InputEventKey> k;6277k.instantiate();62786279k->set_window_id(ke.window_id);6280k->set_pressed(ke.uMsg == WM_KEYDOWN);62816282bool is_oem = (ke.wParam >= 0xB8) && (ke.wParam <= 0xE6);6283Key keycode = KeyMappingWindows::get_keysym(ke.wParam);6284if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) {6285// Special case for Numpad Enter key.6286keycode = Key::KP_ENTER;6287}6288Key key_label = keycode;6289Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));6290KeyLocation location = KeyMappingWindows::get_location((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));62916292static BYTE keyboard_state[256];6293memset(keyboard_state, 0, 256);6294wchar_t chars[256] = {};6295UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX);6296if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) {6297String keysym = String::utf16((char16_t *)chars, 255);6298if (!keysym.is_empty()) {6299char32_t unicode_value = keysym[0];6300// For printable ASCII characters (0x20-0x7E), override the original keycode with the character value.6301if (is_oem && Key::SPACE <= (Key)unicode_value && (Key)unicode_value <= Key::ASCIITILDE) {6302keycode = fix_keycode(unicode_value, (Key)unicode_value);6303}6304key_label = fix_key_label(unicode_value, keycode);6305}6306}63076308if (keycode != Key::SHIFT) {6309k->set_shift_pressed(ke.shift);6310}6311if (keycode != Key::ALT) {6312k->set_alt_pressed(ke.alt);6313}6314if (keycode != Key::CTRL) {6315k->set_ctrl_pressed(ke.control);6316}6317if (keycode != Key::META) {6318k->set_meta_pressed(ke.meta);6319}6320k->set_keycode(keycode);6321k->set_physical_keycode(physical_keycode);6322k->set_location(location);6323k->set_key_label(key_label);63246325if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {6326char32_t unicode = key_event_buffer[i + 1].wParam;6327static char32_t prev_wck = 0;6328if ((unicode & 0xfffffc00) == 0xd800) {6329if (prev_wck != 0) {6330ERR_PRINT("invalid utf16 surrogate input");6331}6332prev_wck = unicode;6333break; // Skip surrogate.6334} else if ((unicode & 0xfffffc00) == 0xdc00) {6335if (prev_wck == 0) {6336ERR_PRINT("invalid utf16 surrogate input");6337break; // Skip invalid surrogate.6338}6339unicode = (prev_wck << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);6340prev_wck = 0;6341} else {6342prev_wck = 0;6343}6344k->set_unicode(fix_unicode(unicode));6345}6346if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {6347k->set_alt_pressed(false);6348k->set_ctrl_pressed(false);6349}63506351k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));63526353Input::get_singleton()->parse_input_event(k);63546355} break;6356}6357}63586359key_event_pos = 0;6360}63616362void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const String &p_new_driver) {6363for (KeyValue<WindowID, WindowData> &E : windows) {6364WindowData &wd = E.value;6365wd.block_mm = false;6366if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) {6367wintab_WTEnable(wd.wtctx, false);6368wintab_WTClose(wd.wtctx);6369wd.wtctx = nullptr;6370}6371if ((p_new_driver == "wintab") && wintab_available) {6372wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);6373wd.wtlc.lcOptions |= CXO_MESSAGES;6374wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;6375wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;6376wd.wtlc.lcPktMode = 0;6377wd.wtlc.lcOutOrgX = 0;6378wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;6379wd.wtlc.lcOutOrgY = 0;6380wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;6381wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);6382if (wd.wtctx) {6383wintab_WTEnable(wd.wtctx, true);6384AXIS pressure;6385if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {6386wd.min_pressure = int(pressure.axMin);6387wd.max_pressure = int(pressure.axMax);6388}6389AXIS orientation[3];6390if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {6391wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;6392}6393wintab_WTEnable(wd.wtctx, true);6394} else {6395print_verbose("WinTab context creation failed.");6396}6397}6398}6399}64006401Error DisplayServerWindows::_create_window(WindowID p_window_id, WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent, HWND p_parent_hwnd, bool p_no_redirection_bitmap) {6402DWORD dwExStyle;6403DWORD dwStyle;64046405_get_window_style(p_window_id == MAIN_WINDOW_ID, false, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_flags & WINDOW_FLAG_MINIMIZE_DISABLED_BIT, p_flags & WINDOW_FLAG_MAXIMIZE_DISABLED_BIT, p_mode == WINDOW_MODE_MINIMIZED, p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP_BIT), p_parent_hwnd, p_no_redirection_bitmap, dwStyle, dwExStyle);64066407int rq_screen = get_screen_from_rect(p_rect);6408if (rq_screen < 0) {6409rq_screen = get_primary_screen(); // Requested window rect is outside any screen bounds.6410}6411Rect2i usable_rect = screen_get_usable_rect(rq_screen);64126413Point2i offset = _get_screens_origin();64146415RECT WindowRect;64166417Vector2i off = (p_mode == WINDOW_MODE_FULLSCREEN || ((p_flags & WINDOW_FLAG_BORDERLESS_BIT) && p_mode == WINDOW_MODE_MAXIMIZED)) ? _get_screen_expand_offset(rq_screen) : Vector2i();64186419WindowRect.left = p_rect.position.x;6420WindowRect.right = p_rect.position.x + p_rect.size.x + off.x;6421WindowRect.top = p_rect.position.y;6422WindowRect.bottom = p_rect.position.y + p_rect.size.y + off.y;64236424if (!p_parent_hwnd) {6425if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6426Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));64276428WindowRect.left = screen_rect.position.x;6429WindowRect.right = screen_rect.position.x + screen_rect.size.x + off.x;6430WindowRect.top = screen_rect.position.y;6431WindowRect.bottom = screen_rect.position.y + screen_rect.size.y + off.y;6432} else {6433Rect2i srect = screen_get_usable_rect(rq_screen);6434Point2i wpos = p_rect.position;6435if (srect != Rect2i()) {6436wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);6437}64386439WindowRect.left = wpos.x;6440WindowRect.right = wpos.x + p_rect.size.x + off.x;6441WindowRect.top = wpos.y;6442WindowRect.bottom = wpos.y + p_rect.size.y + off.y;6443}6444}64456446WindowRect.left += offset.x;6447WindowRect.right += offset.x;6448WindowRect.top += offset.y;6449WindowRect.bottom += offset.y;64506451if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6452AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);6453}64546455WindowID id = p_window_id;6456{6457WindowData *wd_transient_parent = nullptr;6458HWND owner_hwnd = nullptr;6459if (p_parent_hwnd) {6460owner_hwnd = p_parent_hwnd;6461} else if (p_transient_parent != INVALID_WINDOW_ID) {6462if (!windows.has(p_transient_parent)) {6463ERR_PRINT("Condition \"!windows.has(p_transient_parent)\" is true.");6464p_transient_parent = INVALID_WINDOW_ID;6465} else {6466wd_transient_parent = &windows[p_transient_parent];6467if (p_exclusive) {6468owner_hwnd = wd_transient_parent->hWnd;6469}6470}6471}64726473WindowData &wd = windows[id];64746475wd.id = id;6476wd.hWnd = CreateWindowExW(6477dwExStyle,6478L"Engine", L"",6479dwStyle,6480WindowRect.left,6481WindowRect.top,6482WindowRect.right - WindowRect.left,6483WindowRect.bottom - WindowRect.top,6484owner_hwnd,6485nullptr,6486hInstance,6487// tunnel the WindowData we need to handle creation message6488// lifetime is ensured because we are still on the stack when this is6489// processed in the window proc6490reinterpret_cast<void *>(&wd));6491if (!wd.hWnd) {6492MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);6493windows.erase(id);6494ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Failed to create Windows OS window.");6495}64966497wd.parent_hwnd = p_parent_hwnd;64986499// Detach the input queue from the parent window.6500// This prevents the embedded window from waiting on the main window's input queue,6501// causing lags input lags when resizing or moving the main window.6502if (p_parent_hwnd) {6503DWORD mainThreadId = GetWindowThreadProcessId(owner_hwnd, nullptr);6504DWORD embeddedThreadId = GetCurrentThreadId();6505AttachThreadInput(embeddedThreadId, mainThreadId, FALSE);6506}65076508if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6509wd.fullscreen = true;6510if (p_mode == WINDOW_MODE_FULLSCREEN) {6511wd.multiwindow_fs = true;6512}6513}65146515if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6516// Save initial non-fullscreen rect.6517Rect2i srect = screen_get_usable_rect(rq_screen);6518Point2i wpos = p_rect.position;6519if (srect != Rect2i()) {6520wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);6521}65226523wd.pre_fs_rect.left = wpos.x + offset.x;6524wd.pre_fs_rect.right = wpos.x + p_rect.size.x + offset.x;6525wd.pre_fs_rect.top = wpos.y + offset.y;6526wd.pre_fs_rect.bottom = wpos.y + p_rect.size.y + offset.y;6527wd.pre_fs_valid = true;6528}65296530wd.exclusive = p_exclusive;6531if (wd_transient_parent) {6532wd.transient_parent = p_transient_parent;6533wd_transient_parent->transient_children.insert(id);6534}65356536wd.sharp_corners = p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT;6537{6538DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;6539::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));6540}65416542if (is_dark_mode_supported() && dark_title_available) {6543BOOL value = is_dark_mode();6544::DwmSetWindowAttribute(wd.hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));6545}65466547RegisterTouchWindow(wd.hWnd, 0);6548DragAcceptFiles(wd.hWnd, true);65496550if ((tablet_get_current_driver() == "wintab") && wintab_available) {6551wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);6552wd.wtlc.lcOptions |= CXO_MESSAGES;6553wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;6554wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;6555wd.wtlc.lcPktMode = 0;6556wd.wtlc.lcOutOrgX = 0;6557wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;6558wd.wtlc.lcOutOrgY = 0;6559wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;6560wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);6561if (wd.wtctx) {6562wintab_WTEnable(wd.wtctx, true);6563AXIS pressure;6564if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {6565wd.min_pressure = int(pressure.axMin);6566wd.max_pressure = int(pressure.axMax);6567}6568AXIS orientation[3];6569if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {6570wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;6571}6572} else {6573print_verbose("WinTab context creation failed.");6574}6575} else {6576wd.wtctx = nullptr;6577}65786579if (p_mode == WINDOW_MODE_MAXIMIZED) {6580wd.maximized = true;6581wd.minimized = false;6582}65836584if (p_mode == WINDOW_MODE_MINIMIZED) {6585wd.maximized = false;6586wd.minimized = true;6587}65886589wd.last_pressure = 0;6590wd.last_pressure_update = 0;6591wd.last_tilt = Vector2();65926593IPropertyStore *prop_store;6594HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);6595if (hr == S_OK) {6596PROPVARIANT val;6597String appname;6598if (Engine::get_singleton()->is_editor_hint()) {6599appname = "Godot.GodotEditor." + String(GODOT_VERSION_FULL_CONFIG);6600} else {6601String name = GLOBAL_GET("application/config/name");6602String version = GLOBAL_GET("application/config/version");6603if (version.is_empty()) {6604version = "0";6605}6606String clean_app_name = name.to_pascal_case();6607for (int i = 0; i < clean_app_name.length(); i++) {6608if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {6609clean_app_name[i] = '_';6610}6611}6612clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");6613appname = "Godot." + clean_app_name + "." + version;6614}6615InitPropVariantFromString((PCWSTR)appname.utf16().get_data(), &val);6616prop_store->SetValue(PKEY_AppUserModel_ID, val);6617prop_store->Release();6618}66196620// IME.6621wd.im_himc = ImmGetContext(wd.hWnd);6622ImmAssociateContext(wd.hWnd, (HIMC) nullptr);66236624wd.im_position = Vector2();66256626if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN || p_mode == WINDOW_MODE_MAXIMIZED) {6627RECT r;6628GetClientRect(wd.hWnd, &r);6629ClientToScreen(wd.hWnd, (POINT *)&r.left);6630ClientToScreen(wd.hWnd, (POINT *)&r.right);6631wd.last_pos = Point2i(r.left, r.top) - _get_screens_origin();6632wd.width = r.right - r.left - off.x;6633wd.height = r.bottom - r.top - off.y;6634} else {6635wd.last_pos = p_rect.position;6636wd.width = p_rect.size.width;6637wd.height = p_rect.size.height;6638}66396640wd.no_redirection_bitmap = p_no_redirection_bitmap;66416642wd.create_completed = true;6643// Set size of maximized borderless window (by default it covers the entire screen).6644if (!p_parent_hwnd && p_mode == WINDOW_MODE_MAXIMIZED && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {6645SetWindowPos(wd.hWnd, HWND_TOP, usable_rect.position.x - off.x, usable_rect.position.y - off.y, usable_rect.size.width + off.x, usable_rect.size.height + off.y, SWP_NOZORDER | SWP_NOACTIVATE);6646}6647_update_window_mouse_passthrough(id);6648}66496650return OK;6651}66526653void DisplayServerWindows::_destroy_window(WindowID p_window_id) {6654WindowData &wd = windows[p_window_id];66556656IPropertyStore *prop_store;6657HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);6658if (hr == S_OK) {6659PROPVARIANT val;6660PropVariantInit(&val);6661prop_store->SetValue(PKEY_AppUserModel_ID, val);6662prop_store->Release();6663}66646665if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {6666wintab_WTClose(wd.wtctx);6667wd.wtctx = nullptr;6668}66696670if (wd.drop_target != nullptr) {6671RevokeDragDrop(wd.hWnd);6672wd.drop_target->Release();6673}66746675DestroyWindow(wd.hWnd);6676windows.erase(p_window_id);6677}66786679#ifdef RD_ENABLED6680Error DisplayServerWindows::_create_rendering_context_window(WindowID p_window_id, const String &p_rendering_driver) {6681DEV_ASSERT(rendering_context != nullptr);66826683WindowData &wd = windows[p_window_id];66846685union {6686#ifdef VULKAN_ENABLED6687RenderingContextDriverVulkanWindows::WindowPlatformData vulkan;6688#endif6689#ifdef D3D12_ENABLED6690RenderingContextDriverD3D12::WindowPlatformData d3d12;6691#endif6692} wpd;6693#ifdef VULKAN_ENABLED6694if (p_rendering_driver == "vulkan") {6695wpd.vulkan.window = wd.hWnd;6696wpd.vulkan.instance = hInstance;6697}6698#endif6699#ifdef D3D12_ENABLED6700if (p_rendering_driver == "d3d12") {6701wpd.d3d12.window = wd.hWnd;6702}6703#endif67046705Error err = rendering_context->window_create(p_window_id, &wpd);6706ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to create %s window.", p_rendering_driver));67076708Vector2i off = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? _get_screen_expand_offset(window_get_current_screen(p_window_id)) : Vector2i();6709rendering_context->window_set_size(p_window_id, wd.width + off.x, wd.height + off.y);6710wd.rendering_context_window_created = true;67116712return OK;6713}67146715void DisplayServerWindows::_destroy_rendering_context_window(WindowID p_window_id) {6716DEV_ASSERT(rendering_context != nullptr);67176718WindowData &wd = windows[p_window_id];6719DEV_ASSERT(wd.rendering_context_window_created);67206721rendering_context->window_destroy(p_window_id);6722wd.rendering_context_window_created = false;6723}6724#endif67256726#ifdef GLES3_ENABLED6727Error DisplayServerWindows::_create_gl_window(WindowID p_window_id) {6728if (gl_manager_native) {6729WindowData &wd = windows[p_window_id];67306731Error err = gl_manager_native->window_create(p_window_id, wd.hWnd, hInstance, wd.width, wd.height);6732ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to create native OpenGL window.");67336734wd.gl_native_window_created = true;6735}67366737if (gl_manager_angle) {6738WindowData &wd = windows[p_window_id];67396740Error err = gl_manager_angle->window_create(p_window_id, nullptr, wd.hWnd, wd.width, wd.height);6741ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to create ANGLE OpenGL window.");67426743wd.gl_angle_window_created = true;6744}67456746return OK;6747}6748#endif67496750BitField<DisplayServerWindows::DriverID> DisplayServerWindows::tested_drivers = 0;67516752// WinTab API.6753bool DisplayServerWindows::wintab_available = false;6754WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr;6755WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr;6756WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr;6757WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;6758WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;67596760// UXTheme API.6761bool DisplayServerWindows::dark_title_available = false;6762bool DisplayServerWindows::use_legacy_dark_mode_before_20H1 = false;6763bool DisplayServerWindows::ux_theme_available = false;6764ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;6765GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;6766GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr;6767GetImmersiveUserColorSetPreferencePtr DisplayServerWindows::GetImmersiveUserColorSetPreference = nullptr;67686769Vector2i _get_device_ids_reg(const String &p_device_name) {6770Vector2i out;67716772String subkey = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}";6773HKEY hkey = nullptr;6774LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)subkey.utf16().get_data(), 0, KEY_READ, &hkey);6775if (result != ERROR_SUCCESS) {6776return Vector2i();6777}67786779DWORD subkeys = 0;6780result = RegQueryInfoKeyW(hkey, nullptr, nullptr, nullptr, &subkeys, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);6781if (result != ERROR_SUCCESS) {6782RegCloseKey(hkey);6783return Vector2i();6784}6785for (DWORD i = 0; i < subkeys; i++) {6786WCHAR key_name[MAX_PATH] = L"";6787DWORD key_name_size = MAX_PATH;6788result = RegEnumKeyExW(hkey, i, key_name, &key_name_size, nullptr, nullptr, nullptr, nullptr);6789if (result != ERROR_SUCCESS) {6790continue;6791}6792String id = String::utf16((const char16_t *)key_name, key_name_size);6793if (!id.is_empty()) {6794HKEY sub_hkey = nullptr;6795result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(subkey + "\\" + id).utf16().get_data(), 0, KEY_QUERY_VALUE, &sub_hkey);6796if (result != ERROR_SUCCESS) {6797continue;6798}67996800WCHAR buffer[4096];6801DWORD buffer_len = 4096;6802DWORD vtype = REG_SZ;6803if (RegQueryValueExW(sub_hkey, L"DriverDesc", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) != ERROR_SUCCESS || buffer_len == 0) {6804buffer_len = 4096;6805if (RegQueryValueExW(sub_hkey, L"HardwareInformation.AdapterString", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) != ERROR_SUCCESS || buffer_len == 0) {6806RegCloseKey(sub_hkey);6807continue;6808}6809}68106811String driver_name = String::utf16((const char16_t *)buffer, buffer_len).strip_edges();6812if (driver_name == p_device_name) {6813String driver_id;68146815buffer_len = 4096;6816if (RegQueryValueExW(sub_hkey, L"MatchingDeviceId", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) == ERROR_SUCCESS && buffer_len != 0) {6817driver_id = String::utf16((const char16_t *)buffer, buffer_len).strip_edges();68186819Vector<String> id_parts = driver_id.to_lower().split("&");6820for (const String &id_part : id_parts) {6821int ven_off = id_part.find("ven_");6822if (ven_off >= 0) {6823out.x = id_part.substr(ven_off + 4).hex_to_int();6824}6825int dev_off = id_part.find("dev_");6826if (dev_off >= 0) {6827out.y = id_part.substr(dev_off + 4).hex_to_int();6828}6829}68306831RegCloseKey(sub_hkey);6832break;6833}6834}6835RegCloseKey(sub_hkey);6836}6837}6838RegCloseKey(hkey);6839return out;6840}68416842Vector2i _get_device_ids_wmi(const String &p_device_name) {6843if (p_device_name.is_empty()) {6844return Vector2i();6845}68466847REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID6848REFIID uuid = IID_IWbemLocator; // Interface UUID6849IWbemLocator *wbemLocator = nullptr; // to get the services6850IWbemServices *wbemServices = nullptr; // to get the class6851IEnumWbemClassObject *iter = nullptr;6852IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.68536854HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);6855if (hr != S_OK) {6856return Vector2i();6857}6858BSTR resource_name = SysAllocString(L"root\\CIMV2");6859hr = wbemLocator->ConnectServer(resource_name, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemServices);6860SysFreeString(resource_name);68616862SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices`6863if (hr != S_OK) {6864SAFE_RELEASE(wbemServices)6865return Vector2i();6866}68676868Vector2i ids;68696870const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", p_device_name);6871BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data());6872BSTR query_lang = SysAllocString(L"WQL");6873hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, nullptr, &iter);6874SysFreeString(query_lang);6875SysFreeString(query);6876if (hr == S_OK) {6877ULONG resultCount;6878hr = iter->Next(5000, 1, pnpSDriverObject, &resultCount); // Get exactly 1. Wait max 5 seconds.68796880if (hr == S_OK && resultCount > 0) {6881VARIANT did;6882VariantInit(&did);6883BSTR object_name = SysAllocString(L"DeviceID");6884hr = pnpSDriverObject[0]->Get(object_name, 0, &did, nullptr, nullptr);6885SysFreeString(object_name);6886if (hr == S_OK) {6887String device_id = String(V_BSTR(&did));6888ids.x = device_id.get_slicec('&', 0).lstrip("PCI\\VEN_").hex_to_int();6889ids.y = device_id.get_slicec('&', 1).lstrip("DEV_").hex_to_int();6890}68916892for (ULONG i = 0; i < resultCount; i++) {6893SAFE_RELEASE(pnpSDriverObject[i])6894}6895}6896}68976898SAFE_RELEASE(wbemServices)6899SAFE_RELEASE(iter)69006901return ids;6902}69036904Vector2i _get_device_ids(const String &p_device_name) {6905Vector2i out = _get_device_ids_reg(p_device_name);6906if (out == Vector2i()) {6907out = _get_device_ids_wmi(p_device_name);6908}6909return out;6910}69116912bool DisplayServerWindows::is_dark_mode_supported() const {6913return ux_theme_available;6914}69156916bool DisplayServerWindows::is_dark_mode() const {6917return ux_theme_available && ShouldAppsUseDarkMode();6918}69196920Color DisplayServerWindows::get_accent_color() const {6921if (!ux_theme_available) {6922return Color(0, 0, 0, 0);6923}69246925int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"), false, 0);6926return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);6927}69286929Color DisplayServerWindows::get_base_color() const {6930if (!ux_theme_available) {6931return Color(0, 0, 0, 0);6932}69336934int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(ShouldAppsUseDarkMode() ? L"ImmersiveDarkChromeMediumLow" : L"ImmersiveLightChromeMediumLow"), false, 0);6935return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);6936}69376938void DisplayServerWindows::set_system_theme_change_callback(const Callable &p_callable) {6939system_theme_changed = p_callable;6940}69416942int DisplayServerWindows::tablet_get_driver_count() const {6943return tablet_drivers.size();6944}69456946String DisplayServerWindows::tablet_get_driver_name(int p_driver) const {6947if (p_driver < 0 || p_driver >= tablet_drivers.size()) {6948return "";6949} else {6950return tablet_drivers[p_driver];6951}6952}69536954String DisplayServerWindows::tablet_get_current_driver() const {6955return tablet_driver;6956}69576958void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {6959if (tablet_get_driver_count() == 0) {6960return;6961}69626963String driver = p_driver;6964if (driver == "auto") {6965if (!winink_disabled) {6966driver = "winink";6967} else if (wintab_available) {6968driver = "wintab";6969} else {6970driver = "dummy";6971}6972}69736974bool found = false;6975for (int i = 0; i < tablet_get_driver_count(); i++) {6976if (driver == tablet_get_driver_name(i)) {6977found = true;6978}6979}6980if (found) {6981_update_tablet_ctx(tablet_driver, driver);6982tablet_driver = driver;6983} else {6984ERR_PRINT("Unknown tablet driver " + p_driver + ".");6985}6986}69876988DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {6989KeyMappingWindows::initialize();69906991tested_drivers.clear();69926993drop_events = false;6994key_event_pos = 0;69956996hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance();69976998pressrc = 0;6999old_invalid = true;7000mouse_mode = MOUSE_MODE_VISIBLE;70017002rendering_driver = p_rendering_driver;70037004// Init TTS7005bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");7006if (tts_enabled) {7007initialize_tts();7008}7009native_menu = memnew(NativeMenuWindows);70107011#ifdef ACCESSKIT_ENABLED7012if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {7013accessibility_driver = memnew(AccessibilityDriverAccessKit);7014if (accessibility_driver->init() != OK) {7015if (OS::get_singleton()->is_stdout_verbose()) {7016ERR_PRINT("Can't create an accessibility driver, accessibility support disabled!");7017}7018memdelete(accessibility_driver);7019accessibility_driver = nullptr;7020}7021}7022#endif70237024// Enforce default keep screen on value.7025screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));70267027// Load Windows version info.7028ZeroMemory(&os_ver, sizeof(OSVERSIONINFOW));7029os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);70307031HMODULE nt_lib = LoadLibraryW(L"ntdll.dll");7032bool is_wine = false;7033if (nt_lib) {7034WineGetVersionPtr wine_get_version = (WineGetVersionPtr)(void *)GetProcAddress(nt_lib, "wine_get_version"); // Do not read Windows build number under Wine, it can be set to arbitrary value.7035if (wine_get_version) {7036is_wine = true;7037} else {7038RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)(void *)GetProcAddress(nt_lib, "RtlGetVersion");7039if (RtlGetVersion) {7040RtlGetVersion(&os_ver);7041}7042}7043FreeLibrary(nt_lib);7044}70457046// Load UXTheme.7047if (os_ver.dwBuildNumber >= 10240) { // Not available on Wine, use only if real Windows 10/11 detected.7048HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");7049if (ux_theme_lib) {7050ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132));7051GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));7052GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));7053GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));7054if (os_ver.dwBuildNumber >= 17763) { // Windows 10 Redstone 5 (1809)+ only.7055AllowDarkModeForAppPtr AllowDarkModeForApp = nullptr;7056SetPreferredAppModePtr SetPreferredAppMode = nullptr;7057FlushMenuThemesPtr FlushMenuThemes = nullptr;7058if (os_ver.dwBuildNumber < 18362) { // Windows 10 Redstone 5 (1809) and 19H1 (1903) only.7059AllowDarkModeForApp = (AllowDarkModeForAppPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));7060} else { // Windows 10 19H2 (1909)+ only.7061SetPreferredAppMode = (SetPreferredAppModePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));7062FlushMenuThemes = (FlushMenuThemesPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));7063}7064RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyState = (RefreshImmersiveColorPolicyStatePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(104));7065if (ShouldAppsUseDarkMode) {7066bool dark_mode = ShouldAppsUseDarkMode();7067if (SetPreferredAppMode) {7068SetPreferredAppMode(dark_mode ? APPMODE_ALLOWDARK : APPMODE_DEFAULT);7069} else if (AllowDarkModeForApp) {7070AllowDarkModeForApp(dark_mode);7071}7072if (RefreshImmersiveColorPolicyState) {7073RefreshImmersiveColorPolicyState();7074}7075if (FlushMenuThemes) {7076FlushMenuThemes();7077}7078}7079}70807081ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;7082if (os_ver.dwBuildNumber >= 18363) {7083dark_title_available = true;7084if (os_ver.dwBuildNumber < 19041) {7085use_legacy_dark_mode_before_20H1 = true;7086}7087}7088}7089}70907091tablet_drivers.push_back("auto");7092tablet_drivers.push_back("winink");70937094// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.7095HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");7096if (wintab_lib) {7097wintab_WTOpen = (WTOpenPtr)(void *)GetProcAddress(wintab_lib, "WTOpenW");7098wintab_WTClose = (WTClosePtr)(void *)GetProcAddress(wintab_lib, "WTClose");7099wintab_WTInfo = (WTInfoPtr)(void *)GetProcAddress(wintab_lib, "WTInfoW");7100wintab_WTPacket = (WTPacketPtr)(void *)GetProcAddress(wintab_lib, "WTPacket");7101wintab_WTEnable = (WTEnablePtr)(void *)GetProcAddress(wintab_lib, "WTEnable");71027103wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable;7104}71057106if (wintab_available) {7107tablet_drivers.push_back("wintab");7108}71097110tablet_drivers.push_back("dummy");71117112String wacom_cfg = OS::get_singleton()->get_config_path().path_join("WTablet").path_join("Wacom_Tablet.dat");7113if (FileAccess::exists(wacom_cfg)) {7114Ref<XMLParser> parser;7115parser.instantiate();7116if (parser->open(wacom_cfg) == OK) {7117while (parser->read() == OK) {7118if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {7119continue;7120}7121if (parser->get_node_name() == "WinUseInk") {7122parser->read();7123if (parser->get_node_type() == XMLParser::NODE_TEXT) {7124winink_disabled = (parser->get_node_data().to_lower().strip_edges() != "true");7125print_verbose(vformat("Wacom tablet config found at \"%s\", Windows Ink support is %s.", wacom_cfg, winink_disabled ? "disabled" : "enabled"));7126break;7127}7128}7129}7130}7131}71327133if (OS::get_singleton()->is_hidpi_allowed()) {7134SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE);7135}71367137HMODULE comctl32 = LoadLibraryW(L"comctl32.dll");7138if (comctl32) {7139typedef BOOL(WINAPI * InitCommonControlsExPtr)(_In_ const INITCOMMONCONTROLSEX *picce);7140InitCommonControlsExPtr init_common_controls_ex = (InitCommonControlsExPtr)(void *)GetProcAddress(comctl32, "InitCommonControlsEx");71417142// Fails if the incorrect version was loaded. Probably not a big enough deal to print an error about.7143if (init_common_controls_ex) {7144INITCOMMONCONTROLSEX icc = {};7145icc.dwICC = ICC_STANDARD_CLASSES;7146icc.dwSize = sizeof(INITCOMMONCONTROLSEX);7147if (!init_common_controls_ex(&icc)) {7148WARN_PRINT("Unable to initialize Windows common controls. Native dialogs may not work properly.");7149}7150}7151FreeLibrary(comctl32);7152}71537154OleInitialize(nullptr);71557156HICON default_icon = LoadIconW(GetModuleHandle(nullptr), L"GODOT_ICON");7157if (default_icon == nullptr) {7158default_icon = LoadIcon(nullptr, IDI_WINLOGO);7159}71607161memset(&wc, 0, sizeof(WNDCLASSEXW));7162wc.cbSize = sizeof(WNDCLASSEXW);7163wc.style = CS_OWNDC | CS_DBLCLKS;7164wc.lpfnWndProc = (WNDPROC)::WndProc;7165wc.cbClsExtra = 0;7166wc.cbWndExtra = 0;7167wc.hInstance = hInstance ? hInstance : GetModuleHandle(nullptr);7168wc.hIcon = default_icon;7169wc.hCursor = nullptr;7170wc.hbrBackground = nullptr;7171wc.lpszMenuName = nullptr;7172wc.lpszClassName = L"Engine";71737174if (!RegisterClassExW(&wc)) {7175r_error = ERR_UNAVAILABLE;7176return;7177}71787179_register_raw_input_devices(INVALID_WINDOW_ID);71807181String appname;7182if (Engine::get_singleton()->is_editor_hint()) {7183appname = "Godot.GodotEditor." + String(GODOT_VERSION_FULL_CONFIG);7184} else {7185String name = GLOBAL_GET("application/config/name");7186String version = GLOBAL_GET("application/config/version");7187if (version.is_empty()) {7188version = "0";7189}7190String clean_app_name = name.to_pascal_case();7191for (int i = 0; i < clean_app_name.length(); i++) {7192if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {7193clean_app_name[i] = '_';7194}7195}7196clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");7197appname = "Godot." + clean_app_name + "." + version;71987199#ifndef TOOLS_ENABLED7200// Set for exported projects only.7201HKEY key;7202if (RegOpenKeyW(HKEY_CURRENT_USER_LOCAL_SETTINGS, L"Software\\Microsoft\\Windows\\Shell\\MuiCache", &key) == ERROR_SUCCESS) {7203Char16String cs_name = name.utf16();7204String value_name = OS::get_singleton()->get_executable_path().replace_char('/', '\\') + ".FriendlyAppName";7205RegSetValueExW(key, (LPCWSTR)value_name.utf16().get_data(), 0, REG_SZ, (const BYTE *)cs_name.get_data(), cs_name.size() * sizeof(WCHAR));7206RegCloseKey(key);7207}7208#endif7209}7210SetCurrentProcessExplicitAppUserModelID((PCWSTR)appname.utf16().get_data());72117212mouse_monitor = SetWindowsHookEx(WH_MOUSE, ::MouseProc, nullptr, GetCurrentThreadId());72137214Point2i window_position;7215if (p_position != nullptr) {7216window_position = *p_position;7217} else {7218if (p_screen == SCREEN_OF_MAIN_WINDOW) {7219p_screen = SCREEN_PRIMARY;7220}7221Rect2i scr_rect = screen_get_usable_rect(p_screen);7222window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2;7223}72247225HWND parent_hwnd = NULL;7226if (p_parent_window) {7227// Parented window.7228parent_hwnd = (HWND)p_parent_window;7229}72307231// Init context and rendering device.7232if (rendering_driver == "dummy") {7233RasterizerDummy::make_current();7234}72357236#ifdef RD_ENABLED7237bool fallback_to_vulkan = GLOBAL_GET("rendering/rendering_device/fallback_to_vulkan");7238bool fallback_to_d3d12 = GLOBAL_GET("rendering/rendering_device/fallback_to_d3d12");72397240#ifndef VULKAN_ENABLED7241fallback_to_d3d12 = true; // Always enable fallback if engine was built w/o other driver support.7242#endif7243#ifndef D3D12_ENABLED7244fallback_to_vulkan = true; // Always enable fallback if engine was built w/o other driver support.7245#endif72467247String rendering_drivers[2];7248uint32_t rendering_driver_count = 0;72497250if (rendering_driver == "d3d12") {7251rendering_drivers[rendering_driver_count++] = rendering_driver;7252if (fallback_to_vulkan) {7253rendering_drivers[rendering_driver_count++] = "vulkan";7254}7255} else if (rendering_driver == "vulkan") {7256rendering_drivers[rendering_driver_count++] = rendering_driver;7257if (fallback_to_d3d12) {7258rendering_drivers[rendering_driver_count++] = "d3d12";7259}7260}72617262bool main_window_created = false;7263bool cur_no_redirection_bitmap_value = false;72647265for (uint32_t i = 0; i < rendering_driver_count; i++) {7266const String &tested_rendering_driver = rendering_drivers[i];72677268#ifdef VULKAN_ENABLED7269if (tested_rendering_driver == "vulkan") {7270rendering_context = memnew(RenderingContextDriverVulkanWindows);7271tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);7272}7273#endif7274#ifdef D3D12_ENABLED7275if (tested_rendering_driver == "d3d12") {7276rendering_context = memnew(RenderingContextDriverD3D12);7277tested_drivers.set_flag(DRIVER_ID_RD_D3D12);7278}7279#endif7280if (rendering_context != nullptr) {7281if (rendering_context->initialize() == OK) {7282// The window needs to be recreated when this value differs, because it cannot be added or removed after creation.7283#ifdef DCOMP_ENABLED7284bool new_no_redirection_bitmap_value = OS::get_singleton()->is_layered_allowed() && tested_rendering_driver == "d3d12";7285#else7286bool new_no_redirection_bitmap_value = false;7287#endif7288if (cur_no_redirection_bitmap_value != new_no_redirection_bitmap_value) {7289if (main_window_created) {7290_destroy_window(MAIN_WINDOW_ID);7291main_window_created = false;7292}7293cur_no_redirection_bitmap_value = new_no_redirection_bitmap_value;7294}72957296if (!main_window_created) {7297if (_create_window(MAIN_WINDOW_ID, p_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID, parent_hwnd, cur_no_redirection_bitmap_value) != OK) {7298r_error = ERR_UNAVAILABLE;7299ERR_FAIL_MSG("Failed to create main window.");7300}7301main_window_created = true;7302}73037304if (_create_rendering_context_window(MAIN_WINDOW_ID, tested_rendering_driver) == OK) {7305rendering_device = memnew(RenderingDevice);7306if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) == OK) {7307#ifdef VULKAN_ENABLED7308if (rendering_driver == "vulkan" && tested_rendering_driver == "d3d12") {7309WARN_PRINT("Your video card drivers seem not to support Vulkan, switching to Direct3D 12.");7310}7311#endif7312#ifdef D3D12_ENABLED7313if (rendering_driver == "d3d12" && tested_rendering_driver == "vulkan") {7314WARN_PRINT("Your video card drivers seem not to support Direct3D 12, switching to Vulkan.");7315}7316#endif7317rendering_driver = tested_rendering_driver;7318OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, i > 0 ? OS::RENDERING_SOURCE_FALLBACK : OS::get_singleton()->get_current_rendering_driver_name_source());73197320break;7321}73227323memdelete(rendering_device);7324rendering_device = nullptr;73257326_destroy_rendering_context_window(MAIN_WINDOW_ID);7327}7328}73297330memdelete(rendering_context);7331rendering_context = nullptr;7332}7333}73347335bool rendering_driver_failed = rendering_driver_count != 0 && rendering_context == nullptr;73367337#ifdef GLES3_ENABLED7338if (rendering_driver_failed) {7339bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");7340if (fallback_to_opengl3) {7341tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);7342WARN_PRINT("Your video card drivers seem not to support Direct3D 12 or Vulkan, switching to OpenGL 3.");7343rendering_driver = "opengl3";7344OS::get_singleton()->set_current_rendering_method("gl_compatibility", OS::RENDERING_SOURCE_FALLBACK);7345OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, OS::RENDERING_SOURCE_FALLBACK);7346rendering_driver_failed = false;7347}7348}7349#endif73507351if (rendering_driver_failed) {7352r_error = ERR_UNAVAILABLE;7353return;7354}7355#endif73567357#if defined(GLES3_ENABLED)73587359bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_angle");7360bool show_warning = true;73617362if (rendering_driver == "opengl3") {7363// There's no native OpenGL drivers on Windows for ARM, always enable fallback.7364#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)7365fallback = true;7366show_warning = false;7367#else7368typedef BOOL(WINAPI * IsWow64Process2Ptr)(HANDLE, USHORT *, USHORT *);73697370IsWow64Process2Ptr IsWow64Process2 = (IsWow64Process2Ptr)(void *)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process2");7371if (IsWow64Process2) {7372USHORT process_arch = 0;7373USHORT machine_arch = 0;7374if (!IsWow64Process2(GetCurrentProcess(), &process_arch, &machine_arch)) {7375machine_arch = 0;7376}7377if (machine_arch == 0xAA64) {7378fallback = true;7379show_warning = false;7380}7381}7382#endif7383}73847385bool gl_supported = true;7386if (fallback && !is_wine && (rendering_driver == "opengl3")) {7387Dictionary gl_info = detect_wgl();73887389bool force_angle = false;7390gl_supported = gl_info["version"].operator int() >= 30003;73917392Vector2i device_id = Vector2i(-1, -1);7393Array device_list = GLOBAL_GET("rendering/gl_compatibility/force_angle_on_devices");7394for (int i = 0; i < device_list.size(); i++) {7395const Dictionary &device = device_list[i];7396if (device.has("vendor") && device.has("name")) {7397const String &vendor = device["vendor"];7398const String &name = device["name"];7399if (gl_info["vendor"].operator String().containsn(vendor) && (name == "*" || gl_info["name"].operator String().containsn(name))) {7400// Check vendor/device names.7401force_angle = true;7402break;7403} else if (vendor.begins_with("0x") && name.begins_with("0x")) {7404if (device_id == Vector2i(-1, -1)) {7405// Load device IDs.7406device_id = _get_device_ids(gl_info["name"]);7407}7408if (device_id.x == vendor.lstrip("0x").hex_to_int() && device_id.y == name.lstrip("0x").hex_to_int()) {7409// Check vendor/device IDs.7410force_angle = true;7411break;7412}7413}7414}7415}74167417if (force_angle || (gl_info["version"].operator int() < 30003)) {7418tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);7419if (show_warning) {7420if (gl_info["version"].operator int() < 30003) {7421WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE.");7422} else {7423WARN_PRINT("Your video card drivers are known to have low quality OpenGL 3.3 support, switching to ANGLE.");7424}7425}7426rendering_driver = "opengl3_angle";7427OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, OS::RENDERING_SOURCE_FALLBACK);7428}7429}74307431if (rendering_driver == "opengl3_angle") {7432gl_manager_angle = memnew(GLManagerANGLE_Windows);7433tested_drivers.set_flag(DRIVER_ID_COMPAT_ANGLE_D3D11);74347435if (gl_manager_angle->initialize() != OK) {7436memdelete(gl_manager_angle);7437gl_manager_angle = nullptr;7438bool fallback_to_native = GLOBAL_GET("rendering/gl_compatibility/fallback_to_native");7439if (fallback_to_native && gl_supported) {7440#ifdef EGL_STATIC7441WARN_PRINT("Your video card drivers seem not to support GLES3 / ANGLE, switching to native OpenGL.");7442#else7443WARN_PRINT("Your video card drivers seem not to support GLES3 / ANGLE or ANGLE dynamic libraries (libEGL.dll and libGLESv2.dll) are missing, switching to native OpenGL.");7444#endif7445rendering_driver = "opengl3";7446} else {7447r_error = ERR_UNAVAILABLE;7448ERR_FAIL_MSG("Could not initialize ANGLE OpenGL.");7449}7450}7451}7452if (rendering_driver == "opengl3") {7453gl_manager_native = memnew(GLManagerNative_Windows);7454tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);74557456if (gl_manager_native->initialize() != OK) {7457memdelete(gl_manager_native);7458gl_manager_native = nullptr;7459r_error = ERR_UNAVAILABLE;7460ERR_FAIL_MSG("Could not initialize native OpenGL.");7461}7462}7463#endif74647465bool should_create_main_window = true;7466bool no_redirection_bitmap = false;74677468#ifdef RD_ENABLED7469#ifdef DCOMP_ENABLED7470no_redirection_bitmap = OS::get_singleton()->is_layered_allowed() && rendering_driver == "d3d12";7471#endif74727473// The window may still need to be recreated when all RD backends fail and it falls back to OpenGL.7474if (main_window_created) {7475if (no_redirection_bitmap != cur_no_redirection_bitmap_value) {7476DEV_ASSERT(rendering_context == nullptr);7477_destroy_window(MAIN_WINDOW_ID);7478} else {7479should_create_main_window = false;7480}7481}7482#endif74837484if (should_create_main_window) {7485if (_create_window(MAIN_WINDOW_ID, p_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID, parent_hwnd, no_redirection_bitmap) != OK) {7486r_error = ERR_UNAVAILABLE;7487ERR_FAIL_MSG("Failed to create main window.");7488}7489}7490++window_id_counter;74917492#ifdef GLES3_ENABLED7493if (rendering_driver == "opengl3") {7494if (_create_gl_window(MAIN_WINDOW_ID) != OK) {7495memdelete(gl_manager_native);7496gl_manager_native = nullptr;7497windows.erase(MAIN_WINDOW_ID);7498r_error = ERR_UNAVAILABLE;7499return;7500}7501RasterizerGLES3::make_current(true);7502}7503if (rendering_driver == "opengl3_angle") {7504if (_create_gl_window(MAIN_WINDOW_ID) != OK) {7505memdelete(gl_manager_angle);7506gl_manager_angle = nullptr;7507windows.erase(MAIN_WINDOW_ID);7508r_error = ERR_UNAVAILABLE;7509return;7510}7511RasterizerGLES3::make_current(false);7512}7513#endif75147515window_set_vsync_mode(p_vsync_mode, MAIN_WINDOW_ID);75167517#ifdef SDL_ENABLED7518joypad_sdl = memnew(JoypadSDL);7519if (joypad_sdl->initialize() != OK) {7520ERR_PRINT("Couldn't initialize SDL joypad input driver.");7521memdelete(joypad_sdl);7522joypad_sdl = nullptr;7523}7524#endif75257526for (int i = 0; i < WINDOW_FLAG_MAX; i++) {7527if (p_flags & (1 << i)) {7528window_set_flag(WindowFlags(i), true, MAIN_WINDOW_ID);7529}7530}75317532windows[MAIN_WINDOW_ID].initialized = true;75337534#ifdef ACCESSKIT_ENABLED7535if (accessibility_screen_reader_active()) {7536_THREAD_SAFE_LOCK_7537uint64_t time_wait = OS::get_singleton()->get_ticks_msec();7538while (true) {7539MSG msg = {};7540while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {7541TranslateMessage(&msg);7542DispatchMessageW(&msg);7543}75447545uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_wait;7546if (delta > 500 || get_object_received) {7547break;7548}7549}7550_THREAD_SAFE_UNLOCK_7551}7552#endif75537554#if defined(RD_ENABLED)7555if (rendering_context) {7556DEV_ASSERT(rendering_device != nullptr);75577558rendering_device->screen_create(MAIN_WINDOW_ID);75597560RendererCompositorRD::make_current();7561}7562#endif75637564if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) {7565// Increase priority for projects that are not in low-processor mode (typically games)7566// to reduce the risk of frame stuttering.7567// This is not done for the editor to prevent importers or resource bakers7568// from making the system unresponsive.7569SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);7570DWORD index = 0;7571HANDLE handle = AvSetMmThreadCharacteristicsW(L"Games", &index);7572if (handle) {7573AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);7574}75757576// This is needed to make sure that background work does not starve the main thread.7577// This is only setting the priority of this thread, not the whole process.7578SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);7579}75807581cursor_shape = CURSOR_ARROW;75827583_update_real_mouse_position(MAIN_WINDOW_ID);75847585r_error = OK;75867587static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);7588Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);7589}75907591Vector<String> DisplayServerWindows::get_rendering_drivers_func() {7592Vector<String> drivers;75937594#ifdef VULKAN_ENABLED7595drivers.push_back("vulkan");7596#endif7597#ifdef D3D12_ENABLED7598drivers.push_back("d3d12");7599#endif7600#ifdef GLES3_ENABLED7601drivers.push_back("opengl3");7602drivers.push_back("opengl3_angle");7603#endif7604drivers.push_back("dummy");76057606return drivers;7607}76087609DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {7610DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));7611if (r_error != OK) {7612if (tested_drivers == 0) {7613OS::get_singleton()->alert("Failed to register the window class.", "Unable to initialize DisplayServer");7614} else if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN) || tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {7615Vector<String> drivers;7616if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN)) {7617drivers.push_back("Vulkan");7618}7619if (tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {7620drivers.push_back("Direct3D 12");7621}7622String executable_name = OS::get_singleton()->get_executable_path().get_file();7623OS::get_singleton()->alert(7624vformat("Your video card drivers seem not to support the required %s version.\n\n"7625"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"7626"You can enable the OpenGL 3 driver by starting the engine from the\n"7627"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"7628"If you have recently updated your video card drivers, try rebooting.",7629String(" or ").join(drivers),7630executable_name),7631"Unable to initialize video driver");7632} else {7633Vector<String> drivers;7634if (tested_drivers.has_flag(DRIVER_ID_COMPAT_OPENGL3)) {7635drivers.push_back("OpenGL 3.3");7636}7637if (tested_drivers.has_flag(DRIVER_ID_COMPAT_ANGLE_D3D11)) {7638drivers.push_back("Direct3D 11");7639}7640OS::get_singleton()->alert(7641vformat(7642"Your video card drivers seem not to support the required %s version.\n\n"7643"If possible, consider updating your video card drivers.\n\n"7644"If you have recently updated your video card drivers, try rebooting.",7645String(" or ").join(drivers)),7646"Unable to initialize video driver");7647}7648}7649return ds;7650}76517652void DisplayServerWindows::register_windows_driver() {7653register_create_function("windows", create_func, get_rendering_drivers_func);7654}76557656DisplayServerWindows::~DisplayServerWindows() {7657LocalVector<List<FileDialogData *>::Element *> to_remove;7658for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {7659FileDialogData *fd = E->get();7660if (fd->listener_thread.is_started()) {7661fd->close_requested.set();7662fd->listener_thread.wait_to_finish();7663}7664to_remove.push_back(E);7665}7666for (List<FileDialogData *>::Element *E : to_remove) {7667memdelete(E->get());7668E->erase();7669}76707671#ifdef SDL_ENABLED7672if (joypad_sdl) {7673memdelete(joypad_sdl);7674}7675#endif7676touch_state.clear();76777678cursors_cache.clear();76797680// Destroy all status indicators.7681for (HashMap<IndicatorID, IndicatorData>::Iterator E = indicators.begin(); E; ++E) {7682NOTIFYICONDATAW ndat;7683ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));7684ndat.cbSize = sizeof(NOTIFYICONDATAW);7685ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;7686ndat.uID = E->key;7687ndat.uVersion = NOTIFYICON_VERSION;76887689if (E->value.icon) {7690DestroyIcon(E->value.icon);7691E->value.icon_buffer.clear();7692E->value.icon = nullptr;7693}76947695Shell_NotifyIconW(NIM_DELETE, &ndat);7696}76977698if (mouse_monitor) {7699UnhookWindowsHookEx(mouse_monitor);7700}77017702if (user_proc) {7703SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);7704}77057706// Close power request handle.7707screen_set_keep_on(false);77087709if (native_menu) {7710memdelete(native_menu);7711native_menu = nullptr;7712}77137714#ifdef GLES3_ENABLED7715// destroy windows .. NYI?7716// FIXME wglDeleteContext is never called7717#endif77187719if (windows.has(MAIN_WINDOW_ID)) {7720#ifdef RD_ENABLED7721if (rendering_device) {7722rendering_device->screen_free(MAIN_WINDOW_ID);7723}77247725if (rendering_context) {7726rendering_context->window_destroy(MAIN_WINDOW_ID);7727}7728#endif7729_destroy_window(MAIN_WINDOW_ID);7730}77317732#ifdef RD_ENABLED7733if (rendering_device) {7734memdelete(rendering_device);7735rendering_device = nullptr;7736}77377738if (rendering_context) {7739memdelete(rendering_context);7740rendering_context = nullptr;7741}7742#endif77437744if (icon_big) {7745DestroyIcon(icon_big);7746icon_buffer_big.clear();7747icon_big = nullptr;7748}7749if (icon_small) {7750DestroyIcon(icon_small);7751icon_buffer_small.clear();7752icon_small = nullptr;7753}77547755if (restore_mouse_trails > 1) {7756SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);7757}7758#ifdef GLES3_ENABLED7759if (gl_manager_angle) {7760memdelete(gl_manager_angle);7761gl_manager_angle = nullptr;7762}7763if (gl_manager_native) {7764memdelete(gl_manager_native);7765gl_manager_native = nullptr;7766}7767#endif7768#ifdef ACCESSKIT_ENABLED7769if (accessibility_driver) {7770memdelete(accessibility_driver);7771}7772#endif7773if (tts) {7774memdelete(tts);7775}77767777OleUninitialize();7778}777977807781