Path: blob/master/platform/windows/display_server_windows.cpp
10277 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/io/marshalls.h"39#include "core/io/xml_parser.h"40#include "core/version.h"41#include "drivers/png/png_driver_common.h"42#include "main/main.h"43#include "scene/resources/texture.h"4445#ifdef SDL_ENABLED46#include "drivers/sdl/joypad_sdl.h"47#endif4849#include "servers/rendering/dummy/rasterizer_dummy.h"5051#if defined(VULKAN_ENABLED)52#include "rendering_context_driver_vulkan_windows.h"53#endif54#if defined(D3D12_ENABLED)55#include "drivers/d3d12/rendering_context_driver_d3d12.h"56#endif57#if defined(GLES3_ENABLED)58#include "drivers/gles3/rasterizer_gles3.h"59#endif6061#if defined(ACCESSKIT_ENABLED)62#include "drivers/accesskit/accessibility_driver_accesskit.h"63#endif6465#include <avrt.h>66#include <dwmapi.h>67#include <propkey.h>68#include <propvarutil.h>69#include <shellapi.h>70#include <shellscalingapi.h>71#include <shlwapi.h>72#include <shobjidl.h>73#include <wbemcli.h>7475#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE76#define DWMWA_USE_IMMERSIVE_DARK_MODE 2077#endif7879#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H180#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 1981#endif8283#ifndef DWMWA_WINDOW_CORNER_PREFERENCE84#define DWMWA_WINDOW_CORNER_PREFERENCE 3385#endif8687#ifndef DWMWCP_DEFAULT88#define DWMWCP_DEFAULT 089#endif9091#ifndef DWMWCP_DONOTROUND92#define DWMWCP_DONOTROUND 193#endif9495#define WM_INDICATOR_CALLBACK_MESSAGE (WM_USER + 1)9697int constexpr FS_TRANSP_BORDER = 2;9899static String format_error_message(DWORD id) {100LPWSTR messageBuffer = nullptr;101size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,102nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);103104String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size);105106LocalFree(messageBuffer);107108return msg;109}110111static void track_mouse_leave_event(HWND hWnd) {112TRACKMOUSEEVENT tme;113tme.cbSize = sizeof(TRACKMOUSEEVENT);114tme.dwFlags = TME_LEAVE;115tme.hwndTrack = hWnd;116tme.dwHoverTime = HOVER_DEFAULT;117TrackMouseEvent(&tme);118}119120bool DisplayServerWindows::has_feature(Feature p_feature) const {121switch (p_feature) {122#ifndef DISABLE_DEPRECATED123case FEATURE_GLOBAL_MENU: {124return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));125} break;126#endif127case FEATURE_SUBWINDOWS:128case FEATURE_TOUCHSCREEN:129case FEATURE_MOUSE:130case FEATURE_MOUSE_WARP:131case FEATURE_CLIPBOARD:132case FEATURE_CURSOR_SHAPE:133case FEATURE_CUSTOM_CURSOR_SHAPE:134case FEATURE_IME:135case FEATURE_WINDOW_TRANSPARENCY:136case FEATURE_HIDPI:137case FEATURE_ICON:138case FEATURE_NATIVE_ICON:139case FEATURE_NATIVE_DIALOG:140case FEATURE_NATIVE_DIALOG_INPUT:141case FEATURE_NATIVE_DIALOG_FILE:142case FEATURE_NATIVE_DIALOG_FILE_EXTRA:143//case FEATURE_NATIVE_DIALOG_FILE_MIME:144case FEATURE_SWAP_BUFFERS:145case FEATURE_KEEP_SCREEN_ON:146case FEATURE_TEXT_TO_SPEECH:147case FEATURE_SCREEN_CAPTURE:148case FEATURE_STATUS_INDICATOR:149case FEATURE_WINDOW_EMBEDDING:150case FEATURE_WINDOW_DRAG:151return true;152case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:153return (os_ver.dwBuildNumber >= 19041); // Fully supported on Windows 10 Vibranium R1 (2004)+ only, captured as black rect on older versions.154case FEATURE_EMOJI_AND_SYMBOL_PICKER:155return (os_ver.dwBuildNumber >= 17134); // Windows 10 Redstone 4 (1803)+ only.156#ifdef ACCESSKIT_ENABLED157case FEATURE_ACCESSIBILITY_SCREEN_READER: {158return (accessibility_driver != nullptr);159} break;160#endif161default:162return false;163}164}165166String DisplayServerWindows::get_name() const {167return "Windows";168}169170void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {171if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {172// Hide cursor before moving.173if (hCursor == nullptr) {174hCursor = SetCursor(nullptr);175} else {176SetCursor(nullptr);177}178}179180if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {181// Mouse is grabbed (captured or confined).182WindowID window_id = _get_focused_window_or_popup();183if (!windows.has(window_id)) {184window_id = MAIN_WINDOW_ID;185}186187WindowData &wd = windows[window_id];188189int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;190191RECT clipRect;192GetClientRect(wd.hWnd, &clipRect);193clipRect.right -= off_x;194ClientToScreen(wd.hWnd, (POINT *)&clipRect.left);195ClientToScreen(wd.hWnd, (POINT *)&clipRect.right);196ClipCursor(&clipRect);197if (p_mode == MOUSE_MODE_CAPTURED) {198center = window_get_size() / 2;199POINT pos = { (int)center.x, (int)center.y };200ClientToScreen(wd.hWnd, &pos);201SetCursorPos(pos.x, pos.y);202SetCapture(wd.hWnd);203204_register_raw_input_devices(window_id);205}206} else {207// Mouse is free to move around (not captured or confined).208// When the user is moving a window, it's important to not ReleaseCapture because it will cause209// the window movement to stop and if the user tries to move the Windows when it's not activated,210// it will prevent the window movement. It's probably impossible to move the Window while it's captured anyway.211if (!_has_moving_window()) {212ReleaseCapture();213}214ClipCursor(nullptr);215216_register_raw_input_devices(INVALID_WINDOW_ID);217}218219if (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED) {220// Show cursor.221CursorShape c = cursor_shape;222cursor_shape = CURSOR_MAX;223cursor_set_shape(c);224}225}226227DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() const {228const List<WindowID>::Element *E = popup_list.back();229if (E) {230return E->get();231}232233return last_focused_window;234}235236bool DisplayServerWindows::_has_moving_window() const {237for (const KeyValue<WindowID, WindowData> &E : windows) {238if (E.value.move_timer_id) {239return true;240}241}242return false;243}244245void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) {246use_raw_input = true;247248RAWINPUTDEVICE rid[2] = {};249rid[0].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC250rid[0].usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE251rid[0].dwFlags = 0;252253rid[1].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC254rid[1].usUsage = 0x06; // HID_USAGE_GENERIC_KEYBOARD255rid[1].dwFlags = 0;256257if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) {258// Follow the defined window259rid[0].hwndTarget = windows[p_target_window].hWnd;260rid[1].hwndTarget = windows[p_target_window].hWnd;261} else {262// Follow the keyboard focus263rid[0].hwndTarget = nullptr;264rid[1].hwndTarget = nullptr;265}266267if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) {268// Registration failed.269use_raw_input = false;270}271}272273void DisplayServerWindows::initialize_tts() const {274const_cast<DisplayServerWindows *>(this)->tts = memnew(TTS_Windows);275}276277bool DisplayServerWindows::tts_is_speaking() const {278if (unlikely(!tts)) {279initialize_tts();280}281ERR_FAIL_NULL_V(tts, false);282return tts->is_speaking();283}284285bool DisplayServerWindows::tts_is_paused() const {286if (unlikely(!tts)) {287initialize_tts();288}289ERR_FAIL_NULL_V(tts, false);290return tts->is_paused();291}292293TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const {294if (unlikely(!tts)) {295initialize_tts();296}297ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());298return tts->get_voices();299}300301void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {302if (unlikely(!tts)) {303initialize_tts();304}305ERR_FAIL_NULL(tts);306tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);307}308309void DisplayServerWindows::tts_pause() {310if (unlikely(!tts)) {311initialize_tts();312}313ERR_FAIL_NULL(tts);314tts->pause();315}316317void DisplayServerWindows::tts_resume() {318if (unlikely(!tts)) {319initialize_tts();320}321ERR_FAIL_NULL(tts);322tts->resume();323}324325void DisplayServerWindows::tts_stop() {326if (unlikely(!tts)) {327initialize_tts();328}329ERR_FAIL_NULL(tts);330tts->stop();331}332333Error 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) {334return _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);335}336337Error 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) {338return _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);339}340341GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Wnon-virtual-dtor") // Silence warning due to a COM API weirdness.342343class FileDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents {344LONG ref_count = 1;345int ctl_id = 1;346347HashMap<int, String> ctls;348Dictionary selected;349String root;350351public:352// IUnknown methods353HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) {354static const QITAB qit[] = {355#ifdef __MINGW32__356{ &__uuidof(IFileDialogEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogEvents, FileDialogEventHandler)) },357{ &__uuidof(IFileDialogControlEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogControlEvents, FileDialogEventHandler)) },358#else359QITABENT(FileDialogEventHandler, IFileDialogEvents),360QITABENT(FileDialogEventHandler, IFileDialogControlEvents),361#endif362{ nullptr, 0 },363};364return QISearch(this, qit, riid, ppv);365}366367ULONG STDMETHODCALLTYPE AddRef() {368return InterlockedIncrement(&ref_count);369}370371ULONG STDMETHODCALLTYPE Release() {372long ref = InterlockedDecrement(&ref_count);373if (!ref) {374delete this;375}376return ref;377}378379// IFileDialogEvents methods380HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *) { return S_OK; }381HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialog *) { return S_OK; }382383HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog *p_pfd, IShellItem *p_item) {384if (root.is_empty()) {385return S_OK;386}387388LPWSTR lpw_path = nullptr;389p_item->GetDisplayName(SIGDN_FILESYSPATH, &lpw_path);390if (!lpw_path) {391return S_FALSE;392}393String path = String::utf16((const char16_t *)lpw_path).replace_char('\\', '/').trim_prefix(R"(\\?\)").simplify_path();394if (!path.begins_with(root.simplify_path())) {395return S_FALSE;396}397return S_OK;398}399400HRESULT STDMETHODCALLTYPE OnHelp(IFileDialog *) { return S_OK; }401HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialog *) { return S_OK; }402HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; }403HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog *pfd) { return S_OK; }404HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; }405406// IFileDialogControlEvents methods407HRESULT STDMETHODCALLTYPE OnItemSelected(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, DWORD p_item_idx) {408if (ctls.has(p_ctl_id)) {409selected[ctls[p_ctl_id]] = (int)p_item_idx;410}411return S_OK;412}413414HRESULT STDMETHODCALLTYPE OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; }415HRESULT STDMETHODCALLTYPE OnCheckButtonToggled(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, BOOL p_checked) {416if (ctls.has(p_ctl_id)) {417selected[ctls[p_ctl_id]] = (bool)p_checked;418}419return S_OK;420}421HRESULT STDMETHODCALLTYPE OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; }422423Dictionary get_selected() {424return selected;425}426427void set_root(const String &p_root) {428root = p_root;429}430431void add_option(IFileDialogCustomize *p_pfdc, const String &p_name, const Vector<String> &p_options, int p_default) {432int gid = ctl_id++;433int cid = ctl_id++;434435if (p_options.is_empty()) {436// Add check box.437p_pfdc->StartVisualGroup(gid, L"");438p_pfdc->AddCheckButton(cid, (LPCWSTR)p_name.utf16().get_data(), p_default);439p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);440p_pfdc->EndVisualGroup();441selected[p_name] = (bool)p_default;442} else {443// Add combo box.444p_pfdc->StartVisualGroup(gid, (LPCWSTR)p_name.utf16().get_data());445p_pfdc->AddComboBox(cid);446p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);447for (int i = 0; i < p_options.size(); i++) {448p_pfdc->AddControlItem(cid, i, (LPCWSTR)p_options[i].utf16().get_data());449}450p_pfdc->SetSelectedControlItem(cid, p_default);451p_pfdc->EndVisualGroup();452selected[p_name] = p_default;453}454ctls[cid] = p_name;455}456457virtual ~FileDialogEventHandler() {}458};459460GODOT_GCC_WARNING_POP461462LRESULT CALLBACK WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {463DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());464if (ds_win) {465return ds_win->WndProcFileDialog(hWnd, uMsg, wParam, lParam);466} else {467return DefWindowProcW(hWnd, uMsg, wParam, lParam);468}469}470471LRESULT DisplayServerWindows::WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {472MutexLock lock(file_dialog_mutex);473if (file_dialog_wnd.has(hWnd)) {474if (file_dialog_wnd[hWnd]->close_requested.is_set()) {475IPropertyStore *prop_store;476HRESULT hr = SHGetPropertyStoreForWindow(hWnd, IID_IPropertyStore, (void **)&prop_store);477if (hr == S_OK) {478PROPVARIANT val;479PropVariantInit(&val);480prop_store->SetValue(PKEY_AppUserModel_ID, val);481prop_store->Release();482}483DestroyWindow(hWnd);484file_dialog_wnd.erase(hWnd);485}486}487return DefWindowProcW(hWnd, uMsg, wParam, lParam);488}489490void DisplayServerWindows::_thread_fd_monitor(void *p_ud) {491DisplayServerWindows *ds = static_cast<DisplayServerWindows *>(get_singleton());492FileDialogData *fd = (FileDialogData *)p_ud;493494if (fd->mode < 0 && fd->mode >= DisplayServer::FILE_DIALOG_MODE_SAVE_MAX) {495fd->finished.set();496return;497}498CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);499500int64_t x = fd->wrect.position.x;501int64_t y = fd->wrect.position.y;502int64_t w = fd->wrect.size.x;503int64_t h = fd->wrect.size.y;504505WNDCLASSW wc = {};506wc.lpfnWndProc = (WNDPROC)::WndProcFileDialog;507wc.hInstance = GetModuleHandle(nullptr);508wc.lpszClassName = L"Engine File Dialog";509RegisterClassW(&wc);510511HWND hwnd_dialog = CreateWindowExW(WS_EX_APPWINDOW, L"Engine File Dialog", L"", WS_OVERLAPPEDWINDOW, x, y, w, h, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);512if (hwnd_dialog) {513{514MutexLock lock(ds->file_dialog_mutex);515ds->file_dialog_wnd[hwnd_dialog] = fd;516}517518HICON mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_SMALL, 0);519if (mainwindow_icon) {520SendMessage(hwnd_dialog, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);521}522mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_BIG, 0);523if (mainwindow_icon) {524SendMessage(hwnd_dialog, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);525}526IPropertyStore *prop_store;527HRESULT hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);528if (hr == S_OK) {529PROPVARIANT val;530InitPropVariantFromString((PCWSTR)fd->appid.utf16().get_data(), &val);531prop_store->SetValue(PKEY_AppUserModel_ID, val);532prop_store->Release();533}534}535536SetCurrentProcessExplicitAppUserModelID((PCWSTR)fd->appid.utf16().get_data());537538Vector<Char16String> filter_names;539Vector<Char16String> filter_exts;540for (const String &E : fd->filters) {541Vector<String> tokens = E.split(";");542if (tokens.size() >= 1) {543String flt = tokens[0].strip_edges();544int filter_slice_count = flt.get_slice_count(",");545Vector<String> exts;546for (int j = 0; j < filter_slice_count; j++) {547String str = (flt.get_slicec(',', j).strip_edges());548if (!str.is_empty()) {549exts.push_back(str);550}551}552if (!exts.is_empty()) {553String str = String(";").join(exts);554filter_exts.push_back(str.utf16());555if (tokens.size() == 2) {556filter_names.push_back(tokens[1].strip_edges().utf16());557} else {558filter_names.push_back(str.utf16());559}560}561}562}563if (filter_names.is_empty()) {564filter_exts.push_back(String("*.*").utf16());565filter_names.push_back((RTR("All Files") + " (*.*)").utf16());566}567568Vector<COMDLG_FILTERSPEC> filters;569for (int i = 0; i < filter_names.size(); i++) {570filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });571}572573HRESULT hr = S_OK;574IFileDialog *pfd = nullptr;575if (fd->mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {576hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd);577} else {578hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd);579}580if (SUCCEEDED(hr)) {581IFileDialogEvents *pfde = nullptr;582FileDialogEventHandler *event_handler = new FileDialogEventHandler();583hr = event_handler->QueryInterface(IID_PPV_ARGS(&pfde));584585DWORD cookie = 0;586hr = pfd->Advise(pfde, &cookie);587588IFileDialogCustomize *pfdc = nullptr;589hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));590591for (int i = 0; i < fd->options.size(); i++) {592const Dictionary &item = fd->options[i];593if (!item.has("name") || !item.has("values") || !item.has("default")) {594continue;595}596event_handler->add_option(pfdc, item["name"], item["values"], item["default"]);597}598event_handler->set_root(fd->root);599600pfdc->Release();601602DWORD flags;603pfd->GetOptions(&flags);604if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {605flags |= FOS_ALLOWMULTISELECT;606}607if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR) {608flags |= FOS_PICKFOLDERS;609}610if (fd->show_hidden) {611flags |= FOS_FORCESHOWHIDDEN;612}613pfd->SetOptions(flags | FOS_FORCEFILESYSTEM);614pfd->SetTitle((LPCWSTR)fd->title.utf16().get_data());615616String dir = ProjectSettings::get_singleton()->globalize_path(fd->current_directory);617if (dir == ".") {618dir = OS::get_singleton()->get_executable_path().get_base_dir();619}620if (dir.is_relative_path() || dir == ".") {621Char16String current_dir_name;622size_t str_len = GetCurrentDirectoryW(0, nullptr);623current_dir_name.resize_uninitialized(str_len + 1);624GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw());625if (dir == ".") {626dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/');627} else {628dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/').path_join(dir);629}630}631dir = dir.simplify_path();632dir = dir.trim_prefix(R"(\\?\)").replace_char('/', '\\');633634IShellItem *shellitem = nullptr;635hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem);636if (SUCCEEDED(hr)) {637pfd->SetDefaultFolder(shellitem);638pfd->SetFolder(shellitem);639}640641pfd->SetFileName((LPCWSTR)fd->filename.utf16().get_data());642pfd->SetFileTypes(filters.size(), filters.ptr());643pfd->SetFileTypeIndex(0);644645hr = pfd->Show(hwnd_dialog);646pfd->Unadvise(cookie);647648Dictionary options = event_handler->get_selected();649650pfde->Release();651event_handler->Release();652653UINT index = 0;654pfd->GetFileTypeIndex(&index);655if (index > 0) {656index = index - 1;657}658659if (SUCCEEDED(hr)) {660Vector<String> file_names;661662if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {663IShellItemArray *results;664hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results);665if (SUCCEEDED(hr)) {666DWORD count = 0;667results->GetCount(&count);668for (DWORD i = 0; i < count; i++) {669IShellItem *result;670results->GetItemAt(i, &result);671672PWSTR file_path = nullptr;673hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);674if (SUCCEEDED(hr)) {675file_names.push_back(String::utf16((const char16_t *)file_path).replace_char('\\', '/').trim_prefix(R"(\\?\)"));676CoTaskMemFree(file_path);677}678result->Release();679}680results->Release();681}682} else {683IShellItem *result;684hr = pfd->GetResult(&result);685if (SUCCEEDED(hr)) {686PWSTR file_path = nullptr;687hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);688if (SUCCEEDED(hr)) {689file_names.push_back(String::utf16((const char16_t *)file_path).replace_char('\\', '/').trim_prefix(R"(\\?\)"));690CoTaskMemFree(file_path);691}692result->Release();693}694}695if (fd->callback.is_valid()) {696MutexLock lock(ds->file_dialog_mutex);697FileDialogCallback cb;698cb.callback = fd->callback;699cb.status = true;700cb.files = file_names;701cb.index = index;702cb.options = options;703cb.opt_in_cb = fd->options_in_cb;704ds->pending_cbs.push_back(cb);705}706} else {707if (fd->callback.is_valid()) {708MutexLock lock(ds->file_dialog_mutex);709FileDialogCallback cb;710cb.callback = fd->callback;711cb.status = false;712cb.files = Vector<String>();713cb.index = index;714cb.options = options;715cb.opt_in_cb = fd->options_in_cb;716ds->pending_cbs.push_back(cb);717}718}719pfd->Release();720} else {721if (fd->callback.is_valid()) {722MutexLock lock(ds->file_dialog_mutex);723FileDialogCallback cb;724cb.callback = fd->callback;725cb.status = false;726cb.files = Vector<String>();727cb.index = 0;728cb.options = Dictionary();729cb.opt_in_cb = fd->options_in_cb;730ds->pending_cbs.push_back(cb);731}732}733{734MutexLock lock(ds->file_dialog_mutex);735if (hwnd_dialog && ds->file_dialog_wnd.has(hwnd_dialog)) {736IPropertyStore *prop_store;737hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);738if (hr == S_OK) {739PROPVARIANT val;740PropVariantInit(&val);741prop_store->SetValue(PKEY_AppUserModel_ID, val);742prop_store->Release();743}744DestroyWindow(hwnd_dialog);745ds->file_dialog_wnd.erase(hwnd_dialog);746}747}748UnregisterClassW(L"Engine File Dialog", GetModuleHandle(nullptr));749CoUninitialize();750751fd->finished.set();752753if (fd->window_id != INVALID_WINDOW_ID) {754callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd->window_id);755}756}757758Error 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) {759_THREAD_SAFE_METHOD_760761ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);762763String appname;764if (Engine::get_singleton()->is_editor_hint()) {765appname = "Godot.GodotEditor." + String(GODOT_VERSION_BRANCH);766} else {767String name = GLOBAL_GET("application/config/name");768String version = GLOBAL_GET("application/config/version");769if (version.is_empty()) {770version = "0";771}772String clean_app_name = name.to_pascal_case();773for (int i = 0; i < clean_app_name.length(); i++) {774if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {775clean_app_name[i] = '_';776}777}778clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");779appname = "Godot." + clean_app_name + "." + version;780}781782FileDialogData *fd = memnew(FileDialogData);783if (windows.has(p_window_id) && !windows[p_window_id].is_popup) {784fd->hwnd_owner = windows[p_window_id].hWnd;785RECT crect;786GetWindowRect(fd->hwnd_owner, &crect);787fd->wrect = Rect2i(crect.left, crect.top, crect.right - crect.left, crect.bottom - crect.top);788} else {789fd->hwnd_owner = nullptr;790fd->wrect = Rect2i(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT);791}792fd->appid = appname;793fd->title = p_title;794fd->current_directory = p_current_directory;795fd->root = p_root;796fd->filename = p_filename;797fd->show_hidden = p_show_hidden;798fd->mode = p_mode;799fd->window_id = p_window_id;800fd->filters = p_filters;801fd->options = p_options;802fd->callback = p_callback;803fd->options_in_cb = p_options_in_cb;804fd->finished.clear();805fd->close_requested.clear();806807fd->listener_thread.start(DisplayServerWindows::_thread_fd_monitor, fd);808809file_dialogs.push_back(fd);810811return OK;812}813814void DisplayServerWindows::process_file_dialog_callbacks() {815MutexLock lock(file_dialog_mutex);816while (!pending_cbs.is_empty()) {817FileDialogCallback cb = pending_cbs.front()->get();818pending_cbs.pop_front();819820if (cb.opt_in_cb) {821Variant ret;822Callable::CallError ce;823const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options };824825cb.callback.callp(args, 4, ret, ce);826if (ce.error != Callable::CallError::CALL_OK) {827ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce)));828}829} else {830Variant ret;831Callable::CallError ce;832const Variant *args[3] = { &cb.status, &cb.files, &cb.index };833834cb.callback.callp(args, 3, ret, ce);835if (ce.error != Callable::CallError::CALL_OK) {836ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce)));837}838}839}840}841842void DisplayServerWindows::beep() const {843MessageBeep(MB_OK);844}845846void DisplayServerWindows::_mouse_update_mode() {847_THREAD_SAFE_METHOD_848849MouseMode wanted_mouse_mode = mouse_mode_override_enabled850? mouse_mode_override851: mouse_mode_base;852853if (mouse_mode == wanted_mouse_mode) {854// Already in the same mode; do nothing.855return;856}857858mouse_mode = wanted_mouse_mode;859860_set_mouse_mode_impl(wanted_mouse_mode);861}862863void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {864ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);865if (p_mode == mouse_mode_base) {866return;867}868mouse_mode_base = p_mode;869_mouse_update_mode();870}871872DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {873return mouse_mode;874}875876void DisplayServerWindows::mouse_set_mode_override(MouseMode p_mode) {877ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);878if (p_mode == mouse_mode_override) {879return;880}881mouse_mode_override = p_mode;882_mouse_update_mode();883}884885DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode_override() const {886return mouse_mode_override;887}888889void DisplayServerWindows::mouse_set_mode_override_enabled(bool p_override_enabled) {890if (p_override_enabled == mouse_mode_override_enabled) {891return;892}893mouse_mode_override_enabled = p_override_enabled;894_mouse_update_mode();895}896897bool DisplayServerWindows::mouse_is_mode_override_enabled() const {898return mouse_mode_override_enabled;899}900901void DisplayServerWindows::warp_mouse(const Point2i &p_position) {902_THREAD_SAFE_METHOD_903904WindowID window_id = _get_focused_window_or_popup();905906if (!windows.has(window_id)) {907return; // No focused window?908}909910if (mouse_mode == MOUSE_MODE_CAPTURED) {911old_x = p_position.x;912old_y = p_position.y;913} else {914POINT p;915p.x = p_position.x;916p.y = p_position.y;917ClientToScreen(windows[window_id].hWnd, &p);918919SetCursorPos(p.x, p.y);920}921}922923Point2i DisplayServerWindows::mouse_get_position() const {924POINT p;925GetCursorPos(&p);926return Point2i(p.x, p.y) - _get_screens_origin();927}928929BitField<MouseButtonMask> DisplayServerWindows::mouse_get_button_state() const {930BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;931932if (GetKeyState(VK_LBUTTON) & (1 << 15)) {933last_button_state.set_flag(MouseButtonMask::LEFT);934}935if (GetKeyState(VK_RBUTTON) & (1 << 15)) {936last_button_state.set_flag(MouseButtonMask::RIGHT);937}938if (GetKeyState(VK_MBUTTON) & (1 << 15)) {939last_button_state.set_flag(MouseButtonMask::MIDDLE);940}941if (GetKeyState(VK_XBUTTON1) & (1 << 15)) {942last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);943}944if (GetKeyState(VK_XBUTTON2) & (1 << 15)) {945last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);946}947948return last_button_state;949}950951void DisplayServerWindows::clipboard_set(const String &p_text) {952_THREAD_SAFE_METHOD_953954if (!windows.has(MAIN_WINDOW_ID)) {955return;956}957958// Convert LF line endings to CRLF in clipboard content.959// Otherwise, line endings won't be visible when pasted in other software.960String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // Avoid \r\r\n.961962if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {963ERR_FAIL_MSG("Unable to open clipboard.");964}965EmptyClipboard();966967Char16String utf16 = text.utf16();968HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR));969ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");970971LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);972memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR));973GlobalUnlock(mem);974975SetClipboardData(CF_UNICODETEXT, mem);976977// Set the CF_TEXT version (not needed?).978CharString utf8 = text.utf8();979mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);980ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");981982LPTSTR ptr = (LPTSTR)GlobalLock(mem);983memcpy(ptr, utf8.get_data(), utf8.length());984ptr[utf8.length()] = 0;985GlobalUnlock(mem);986987SetClipboardData(CF_TEXT, mem);988989CloseClipboard();990}991992String DisplayServerWindows::clipboard_get() const {993_THREAD_SAFE_METHOD_994995if (!windows.has(MAIN_WINDOW_ID)) {996return String();997}998999String ret;1000if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {1001ERR_FAIL_V_MSG("", "Unable to open clipboard.");1002}10031004if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {1005HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);1006if (mem != nullptr) {1007LPWSTR ptr = (LPWSTR)GlobalLock(mem);1008if (ptr != nullptr) {1009ret = String::utf16((const char16_t *)ptr);1010GlobalUnlock(mem);1011}1012}10131014} else if (IsClipboardFormatAvailable(CF_TEXT)) {1015HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);1016if (mem != nullptr) {1017LPTSTR ptr = (LPTSTR)GlobalLock(mem);1018if (ptr != nullptr) {1019ret.append_utf8((const char *)ptr);1020GlobalUnlock(mem);1021}1022}1023}10241025CloseClipboard();10261027return ret;1028}10291030Ref<Image> DisplayServerWindows::clipboard_get_image() const {1031Ref<Image> image;1032if (!windows.has(last_focused_window)) {1033return image; // No focused window?1034}1035if (!OpenClipboard(windows[last_focused_window].hWnd)) {1036ERR_FAIL_V_MSG(image, "Unable to open clipboard.");1037}1038UINT png_format = RegisterClipboardFormatA("PNG");1039if (png_format && IsClipboardFormatAvailable(png_format)) {1040HANDLE png_handle = GetClipboardData(png_format);1041if (png_handle) {1042size_t png_size = GlobalSize(png_handle);1043uint8_t *png_data = (uint8_t *)GlobalLock(png_handle);1044image.instantiate();10451046PNGDriverCommon::png_to_image(png_data, png_size, false, image);10471048GlobalUnlock(png_handle);1049}1050} else if (IsClipboardFormatAvailable(CF_DIB)) {1051HGLOBAL mem = GetClipboardData(CF_DIB);1052if (mem != nullptr) {1053BITMAPINFO *ptr = static_cast<BITMAPINFO *>(GlobalLock(mem));10541055if (ptr != nullptr) {1056BITMAPINFOHEADER *info = &ptr->bmiHeader;1057void *dib_bits = (void *)(ptr->bmiColors);10581059// Draw DIB image to temporary DC surface and read it back as BGRA8.1060HDC dc = GetDC(nullptr);1061if (dc) {1062HDC hdc = CreateCompatibleDC(dc);1063if (hdc) {1064HBITMAP hbm = CreateCompatibleBitmap(dc, info->biWidth, std::abs(info->biHeight));1065if (hbm) {1066SelectObject(hdc, hbm);1067SetDIBitsToDevice(hdc, 0, 0, info->biWidth, std::abs(info->biHeight), 0, 0, 0, std::abs(info->biHeight), dib_bits, ptr, DIB_RGB_COLORS);10681069BITMAPINFO bmp_info = {};1070bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1071bmp_info.bmiHeader.biWidth = info->biWidth;1072bmp_info.bmiHeader.biHeight = -std::abs(info->biHeight);1073bmp_info.bmiHeader.biPlanes = 1;1074bmp_info.bmiHeader.biBitCount = 32;1075bmp_info.bmiHeader.biCompression = BI_RGB;10761077Vector<uint8_t> img_data;1078img_data.resize(info->biWidth * std::abs(info->biHeight) * 4);1079GetDIBits(hdc, hbm, 0, std::abs(info->biHeight), img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);10801081uint8_t *wr = (uint8_t *)img_data.ptrw();1082for (int i = 0; i < info->biWidth * std::abs(info->biHeight); i++) {1083SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1084if (info->biBitCount != 32) {1085wr[i * 4 + 3] = 255; // Set A to solid if it's not in the source image.1086}1087}1088image = Image::create_from_data(info->biWidth, std::abs(info->biHeight), false, Image::Format::FORMAT_RGBA8, img_data);10891090DeleteObject(hbm);1091}1092DeleteDC(hdc);1093}1094ReleaseDC(nullptr, dc);1095}1096GlobalUnlock(mem);1097}1098}1099}1100CloseClipboard();11011102return image;1103}11041105bool DisplayServerWindows::clipboard_has() const {1106return (IsClipboardFormatAvailable(CF_TEXT) ||1107IsClipboardFormatAvailable(CF_UNICODETEXT) ||1108IsClipboardFormatAvailable(CF_OEMTEXT));1109}11101111bool DisplayServerWindows::clipboard_has_image() const {1112UINT png_format = RegisterClipboardFormatA("PNG");1113return ((png_format && IsClipboardFormatAvailable(png_format)) || IsClipboardFormatAvailable(CF_DIB));1114}11151116typedef struct {1117int count;1118int screen;1119HMONITOR monitor;1120} EnumScreenData;11211122static BOOL CALLBACK _MonitorEnumProcPrim(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1123EnumScreenData *data = (EnumScreenData *)dwData;1124if ((lprcMonitor->left == 0) && (lprcMonitor->top == 0)) {1125data->screen = data->count;1126return FALSE;1127}11281129data->count++;1130return TRUE;1131}11321133static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1134EnumScreenData *data = (EnumScreenData *)dwData;1135if (data->monitor == hMonitor) {1136data->screen = data->count;1137}11381139data->count++;1140return TRUE;1141}11421143static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1144int *data = (int *)dwData;1145(*data)++;1146return TRUE;1147}11481149int DisplayServerWindows::get_screen_count() const {1150_THREAD_SAFE_METHOD_11511152int data = 0;1153EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcCount, (LPARAM)&data);1154return data;1155}11561157int DisplayServerWindows::get_primary_screen() const {1158EnumScreenData data = { 0, 0, nullptr };1159EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPrim, (LPARAM)&data);1160return data.screen;1161}11621163int DisplayServerWindows::get_keyboard_focus_screen() const {1164HWND hwnd = GetForegroundWindow();1165if (hwnd) {1166EnumScreenData data = { 0, 0, MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) };1167EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);1168return data.screen;1169} else {1170return get_primary_screen();1171}1172}11731174typedef struct {1175int count;1176int screen;1177Point2 pos;1178} EnumPosData;11791180static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1181EnumPosData *data = (EnumPosData *)dwData;1182if (data->count == data->screen) {1183data->pos.x = lprcMonitor->left;1184data->pos.y = lprcMonitor->top;1185}11861187data->count++;1188return TRUE;1189}11901191static BOOL CALLBACK _MonitorEnumProcOrigin(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1192EnumPosData *data = (EnumPosData *)dwData;1193data->pos = data->pos.min(Point2(lprcMonitor->left, lprcMonitor->top));11941195return TRUE;1196}11971198Point2i DisplayServerWindows::_get_screens_origin() const {1199_THREAD_SAFE_METHOD_12001201EnumPosData data = { 0, 0, Point2() };1202EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcOrigin, (LPARAM)&data);1203return data.pos;1204}12051206Point2i DisplayServerWindows::screen_get_position(int p_screen) const {1207_THREAD_SAFE_METHOD_12081209p_screen = _get_screen_index(p_screen);1210int screen_count = get_screen_count();1211ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());12121213EnumPosData data = { 0, p_screen, Point2() };1214EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPos, (LPARAM)&data);1215return data.pos - _get_screens_origin();1216}12171218typedef struct {1219int count;1220int screen;1221Size2 size;1222} EnumSizeData;12231224typedef struct {1225int count;1226int screen;1227Rect2i rect;1228} EnumRectData;12291230typedef struct {1231Vector<DISPLAYCONFIG_PATH_INFO> paths;1232Vector<DISPLAYCONFIG_MODE_INFO> modes;1233int count;1234int screen;1235float rate;1236} EnumRefreshRateData;12371238static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1239EnumSizeData *data = (EnumSizeData *)dwData;1240if (data->count == data->screen) {1241data->size.x = lprcMonitor->right - lprcMonitor->left;1242data->size.y = lprcMonitor->bottom - lprcMonitor->top;1243}12441245data->count++;1246return TRUE;1247}12481249Size2i DisplayServerWindows::screen_get_size(int p_screen) const {1250_THREAD_SAFE_METHOD_12511252p_screen = _get_screen_index(p_screen);1253int screen_count = get_screen_count();1254ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());12551256EnumSizeData data = { 0, p_screen, Size2() };1257EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcSize, (LPARAM)&data);1258return data.size;1259}12601261static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1262EnumRectData *data = (EnumRectData *)dwData;1263if (data->count == data->screen) {1264MONITORINFO minfo;1265memset(&minfo, 0, sizeof(MONITORINFO));1266minfo.cbSize = sizeof(MONITORINFO);1267GetMonitorInfoA(hMonitor, &minfo);12681269data->rect.position.x = minfo.rcWork.left;1270data->rect.position.y = minfo.rcWork.top;1271data->rect.size.x = minfo.rcWork.right - minfo.rcWork.left;1272data->rect.size.y = minfo.rcWork.bottom - minfo.rcWork.top;1273}12741275data->count++;1276return TRUE;1277}12781279static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1280EnumRefreshRateData *data = (EnumRefreshRateData *)dwData;1281if (data->count == data->screen) {1282MONITORINFOEXW minfo;1283memset(&minfo, 0, sizeof(minfo));1284minfo.cbSize = sizeof(minfo);1285GetMonitorInfoW(hMonitor, &minfo);12861287bool found = false;1288for (const DISPLAYCONFIG_PATH_INFO &path : data->paths) {1289DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name;1290memset(&source_name, 0, sizeof(source_name));1291source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;1292source_name.header.size = sizeof(source_name);1293source_name.header.adapterId = path.sourceInfo.adapterId;1294source_name.header.id = path.sourceInfo.id;1295if (DisplayConfigGetDeviceInfo(&source_name.header) == ERROR_SUCCESS) {1296if (wcscmp(minfo.szDevice, source_name.viewGdiDeviceName) == 0 && path.targetInfo.refreshRate.Numerator != 0 && path.targetInfo.refreshRate.Denominator != 0) {1297data->rate = (double)path.targetInfo.refreshRate.Numerator / (double)path.targetInfo.refreshRate.Denominator;1298found = true;1299break;1300}1301}1302}1303if (!found) {1304DEVMODEW dm;1305memset(&dm, 0, sizeof(dm));1306dm.dmSize = sizeof(dm);1307EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);13081309data->rate = dm.dmDisplayFrequency;1310}1311}13121313data->count++;1314return TRUE;1315}13161317Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const {1318_THREAD_SAFE_METHOD_13191320p_screen = _get_screen_index(p_screen);1321int screen_count = get_screen_count();1322ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());13231324EnumRectData data = { 0, p_screen, Rect2i() };1325EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcUsableSize, (LPARAM)&data);1326data.rect.position -= _get_screens_origin();1327return data.rect;1328}13291330typedef struct {1331int count;1332int screen;1333int dpi;1334} EnumDpiData;13351336static int QueryDpiForMonitor(HMONITOR hmon, MONITOR_DPI_TYPE dpiType = MDT_DEFAULT) {1337int dpiX = 96, dpiY = 96;13381339UINT x = 0, y = 0;1340if (hmon) {1341HRESULT hr = GetDpiForMonitor(hmon, dpiType, &x, &y);1342if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {1343dpiX = (int)x;1344dpiY = (int)y;1345}1346} else {1347static int overallX = 0, overallY = 0;1348if (overallX <= 0 || overallY <= 0) {1349HDC hdc = GetDC(nullptr);1350if (hdc) {1351overallX = GetDeviceCaps(hdc, LOGPIXELSX);1352overallY = GetDeviceCaps(hdc, LOGPIXELSY);1353ReleaseDC(nullptr, hdc);1354}1355}1356if (overallX > 0 && overallY > 0) {1357dpiX = overallX;1358dpiY = overallY;1359}1360}13611362return (dpiX + dpiY) / 2;1363}13641365static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1366EnumDpiData *data = (EnumDpiData *)dwData;1367if (data->count == data->screen) {1368data->dpi = QueryDpiForMonitor(hMonitor);1369}13701371data->count++;1372return TRUE;1373}13741375int DisplayServerWindows::screen_get_dpi(int p_screen) const {1376_THREAD_SAFE_METHOD_13771378p_screen = _get_screen_index(p_screen);1379int screen_count = get_screen_count();1380ERR_FAIL_INDEX_V(p_screen, screen_count, 72);13811382EnumDpiData data = { 0, p_screen, 72 };1383EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);1384return data.dpi;1385}13861387Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const {1388Point2i pos = p_position + _get_screens_origin();13891390POINT p;1391p.x = pos.x;1392p.y = pos.y;1393LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p);13941395HDC dc = GetDC(nullptr);1396if (dc) {1397COLORREF col = GetPixel(dc, p.x, p.y);1398if (col != CLR_INVALID) {1399ReleaseDC(nullptr, dc);1400return Color(float(col & 0x000000FF) / 255.0f, float((col & 0x0000FF00) >> 8) / 255.0f, float((col & 0x00FF0000) >> 16) / 255.0f, 1.0f);1401}1402ReleaseDC(nullptr, dc);1403}14041405return Color();1406}14071408Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {1409p_screen = _get_screen_index(p_screen);1410int screen_count = get_screen_count();1411ERR_FAIL_INDEX_V(p_screen, screen_count, Ref<Image>());14121413Point2i pos = screen_get_position(p_screen) + _get_screens_origin();1414Size2i size = screen_get_size(p_screen);14151416POINT p1;1417p1.x = pos.x;1418p1.y = pos.y;14191420POINT p2;1421p2.x = pos.x + size.x;1422p2.y = pos.y + size.y;1423LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p1);1424LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p2);14251426Ref<Image> img;1427HDC dc = GetDC(nullptr);1428if (dc) {1429HDC hdc = CreateCompatibleDC(dc);1430int width = p2.x - p1.x;1431int height = p2.y - p1.y;1432if (hdc) {1433HBITMAP hbm = CreateCompatibleBitmap(dc, width, height);1434if (hbm) {1435SelectObject(hdc, hbm);1436BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY);14371438BITMAPINFO bmp_info = {};1439bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1440bmp_info.bmiHeader.biWidth = width;1441bmp_info.bmiHeader.biHeight = -height;1442bmp_info.bmiHeader.biPlanes = 1;1443bmp_info.bmiHeader.biBitCount = 32;1444bmp_info.bmiHeader.biCompression = BI_RGB;14451446Vector<uint8_t> img_data;1447img_data.resize(width * height * 4);1448GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);14491450uint8_t *wr = (uint8_t *)img_data.ptrw();1451for (int i = 0; i < width * height; i++) {1452SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1453}1454img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);14551456DeleteObject(hbm);1457}1458DeleteDC(hdc);1459}1460ReleaseDC(nullptr, dc);1461}14621463return img;1464}14651466Ref<Image> DisplayServerWindows::screen_get_image_rect(const Rect2i &p_rect) const {1467Point2i pos = p_rect.position + _get_screens_origin();1468Size2i size = p_rect.size;14691470POINT p1;1471p1.x = pos.x;1472p1.y = pos.y;14731474POINT p2;1475p2.x = pos.x + size.x;1476p2.y = pos.y + size.y;1477LogicalToPhysicalPointForPerMonitorDPI(0, &p1);1478LogicalToPhysicalPointForPerMonitorDPI(0, &p2);14791480Ref<Image> img;1481HDC dc = GetDC(0);1482if (dc) {1483HDC hdc = CreateCompatibleDC(dc);1484int width = p2.x - p1.x;1485int height = p2.y - p1.y;1486if (hdc) {1487HBITMAP hbm = CreateCompatibleBitmap(dc, width, height);1488if (hbm) {1489SelectObject(hdc, hbm);1490BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY);14911492BITMAPINFO bmp_info = {};1493bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1494bmp_info.bmiHeader.biWidth = width;1495bmp_info.bmiHeader.biHeight = -height;1496bmp_info.bmiHeader.biPlanes = 1;1497bmp_info.bmiHeader.biBitCount = 32;1498bmp_info.bmiHeader.biCompression = BI_RGB;14991500Vector<uint8_t> img_data;1501img_data.resize(width * height * 4);1502GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);15031504uint8_t *wr = (uint8_t *)img_data.ptrw();1505for (int i = 0; i < width * height; i++) {1506SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1507}1508img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);15091510DeleteObject(hbm);1511}1512DeleteDC(hdc);1513}1514ReleaseDC(NULL, dc);1515}15161517return img;1518}15191520float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {1521_THREAD_SAFE_METHOD_15221523p_screen = _get_screen_index(p_screen);1524int screen_count = get_screen_count();1525ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);15261527EnumRefreshRateData data = { Vector<DISPLAYCONFIG_PATH_INFO>(), Vector<DISPLAYCONFIG_MODE_INFO>(), 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK };15281529uint32_t path_count = 0;1530uint32_t mode_count = 0;1531if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) {1532data.paths.resize(path_count);1533data.modes.resize(mode_count);1534if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, data.paths.ptrw(), &mode_count, data.modes.ptrw(), nullptr) != ERROR_SUCCESS) {1535data.paths.clear();1536data.modes.clear();1537}1538}15391540EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data);1541return data.rate;1542}15431544void DisplayServerWindows::screen_set_keep_on(bool p_enable) {1545if (keep_screen_on == p_enable) {1546return;1547}15481549if (p_enable) {1550const String reason = "Godot Engine running with display/window/energy_saving/keep_screen_on = true";1551Char16String reason_utf16 = reason.utf16();1552REASON_CONTEXT context;1553context.Version = POWER_REQUEST_CONTEXT_VERSION;1554context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;1555context.Reason.SimpleReasonString = (LPWSTR)(reason_utf16.ptrw());1556power_request = PowerCreateRequest(&context);1557if (power_request == INVALID_HANDLE_VALUE) {1558print_error("Failed to enable screen_keep_on.");1559return;1560}1561if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired) == 0) {1562print_error("Failed to request system sleep override.");1563return;1564}1565if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired) == 0) {1566print_error("Failed to request display timeout override.");1567return;1568}1569} else {1570PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired);1571PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired);1572CloseHandle(power_request);1573power_request = nullptr;1574}15751576keep_screen_on = p_enable;1577}15781579bool DisplayServerWindows::screen_is_kept_on() const {1580return keep_screen_on;1581}15821583Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {1584_THREAD_SAFE_METHOD_15851586Vector<DisplayServer::WindowID> ret;1587for (const KeyValue<WindowID, WindowData> &E : windows) {1588ret.push_back(E.key);1589}1590return ret;1591}15921593DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(const Point2i &p_position) const {1594Point2i offset = _get_screens_origin();1595POINT p;1596p.x = p_position.x + offset.x;1597p.y = p_position.y + offset.y;1598HWND hwnd = WindowFromPoint(p);1599for (const KeyValue<WindowID, WindowData> &E : windows) {1600if (E.value.hWnd == hwnd) {1601return E.key;1602}1603}16041605return INVALID_WINDOW_ID;1606}16071608DisplayServer::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) {1609_THREAD_SAFE_METHOD_16101611WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_exclusive, p_transient_parent, NULL);1612ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window.");16131614WindowData &wd = windows[window_id];16151616if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {1617wd.resizable = false;1618}1619if (p_flags & WINDOW_FLAG_MINIMIZE_DISABLED_BIT) {1620wd.no_min_btn = true;1621}1622if (p_flags & WINDOW_FLAG_MAXIMIZE_DISABLED_BIT) {1623wd.no_max_btn = true;1624}1625if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {1626wd.borderless = true;1627}1628if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {1629wd.always_on_top = true;1630}1631if (p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT) {1632wd.sharp_corners = true;1633}1634if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {1635wd.no_focus = true;1636}1637if (p_flags & WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT) {1638wd.mpass = true;1639}1640if (p_flags & WINDOW_FLAG_EXCLUDE_FROM_CAPTURE_BIT) {1641wd.hide_from_capture = true;1642if (os_ver.dwBuildNumber >= 19041) {1643SetWindowDisplayAffinity(wd.hWnd, WDA_EXCLUDEFROMCAPTURE);1644} else {1645SetWindowDisplayAffinity(wd.hWnd, WDA_MONITOR);1646}1647}1648if (p_flags & WINDOW_FLAG_POPUP_BIT) {1649wd.is_popup = true;1650}1651if (p_flags & WINDOW_FLAG_TRANSPARENT_BIT) {1652if (OS::get_singleton()->is_layered_allowed()) {1653DWM_BLURBEHIND bb;1654ZeroMemory(&bb, sizeof(bb));1655HRGN hRgn = CreateRectRgn(0, 0, -1, -1);1656bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;1657bb.hRgnBlur = hRgn;1658bb.fEnable = TRUE;1659DwmEnableBlurBehindWindow(wd.hWnd, &bb);1660}16611662wd.layered_window = true;1663}16641665// Inherit icons from MAIN_WINDOW for all sub windows.1666HICON mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_SMALL, 0);1667if (mainwindow_icon) {1668SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);1669}1670mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_BIG, 0);1671if (mainwindow_icon) {1672SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);1673}1674#ifdef RD_ENABLED1675if (rendering_device) {1676rendering_device->screen_create(window_id);1677}1678#endif1679return window_id;1680}16811682bool DisplayServerWindows::_is_always_on_top_recursive(WindowID p_window) const {1683ERR_FAIL_COND_V(!windows.has(p_window), false);16841685const WindowData &wd = windows[p_window];1686if (wd.always_on_top) {1687return true;1688}16891690if (wd.transient_parent != INVALID_WINDOW_ID) {1691return _is_always_on_top_recursive(wd.transient_parent);1692}16931694return false;1695}16961697void DisplayServerWindows::show_window(WindowID p_id) {1698ERR_FAIL_COND(!windows.has(p_id));16991700WindowData &wd = windows[p_id];1701popup_open(p_id);17021703if (p_id != MAIN_WINDOW_ID) {1704_update_window_style(p_id);1705}1706wd.initialized = true;17071708if (wd.maximized) {1709ShowWindow(wd.hWnd, SW_SHOWMAXIMIZED);1710SetForegroundWindow(wd.hWnd); // Slightly higher priority.1711SetFocus(wd.hWnd); // Set keyboard focus.1712} else if (wd.minimized) {1713ShowWindow(wd.hWnd, SW_SHOWMINIMIZED);1714} else if (wd.no_focus) {1715// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow1716ShowWindow(wd.hWnd, SW_SHOWNA);1717} else if (wd.is_popup) {1718ShowWindow(wd.hWnd, SW_SHOWNA);1719SetFocus(wd.hWnd); // Set keyboard focus.1720} else {1721ShowWindow(wd.hWnd, SW_SHOW);1722SetForegroundWindow(wd.hWnd); // Slightly higher priority.1723SetFocus(wd.hWnd); // Set keyboard focus.1724}1725if (_is_always_on_top_recursive(p_id)) {1726SetWindowPos(wd.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));1727}1728}17291730void DisplayServerWindows::delete_sub_window(WindowID p_window) {1731_THREAD_SAFE_METHOD_17321733ERR_FAIL_COND(!windows.has(p_window));1734ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted.");17351736popup_close(p_window);17371738WindowData &wd = windows[p_window];17391740IPropertyStore *prop_store;1741HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);1742if (hr == S_OK) {1743PROPVARIANT val;1744PropVariantInit(&val);1745prop_store->SetValue(PKEY_AppUserModel_ID, val);1746prop_store->Release();1747}17481749while (wd.transient_children.size()) {1750window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);1751}17521753if (wd.transient_parent != INVALID_WINDOW_ID) {1754window_set_transient(p_window, INVALID_WINDOW_ID);1755}17561757#ifdef RD_ENABLED1758if (rendering_device) {1759rendering_device->screen_free(p_window);1760}17611762if (rendering_context) {1763rendering_context->window_destroy(p_window);1764}1765#endif1766#ifdef GLES3_ENABLED1767if (gl_manager_angle) {1768gl_manager_angle->window_destroy(p_window);1769}1770if (gl_manager_native) {1771gl_manager_native->window_destroy(p_window);1772}1773#endif17741775if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {1776wintab_WTClose(wd.wtctx);1777wd.wtctx = nullptr;1778}17791780if (wd.drop_target != nullptr) {1781RevokeDragDrop(wd.hWnd);1782wd.drop_target->Release();1783}17841785DestroyWindow(wd.hWnd);1786windows.erase(p_window);17871788if (last_focused_window == p_window) {1789last_focused_window = INVALID_WINDOW_ID;1790}1791}17921793void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) {1794#if defined(GLES3_ENABLED)1795if (gl_manager_angle) {1796gl_manager_angle->window_make_current(p_window_id);1797}1798if (gl_manager_native) {1799gl_manager_native->window_make_current(p_window_id);1800}1801#endif1802}18031804int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {1805ERR_FAIL_COND_V(!windows.has(p_window), 0);1806switch (p_handle_type) {1807case DISPLAY_HANDLE: {1808return 0; // Not supported.1809}1810case WINDOW_HANDLE: {1811return (int64_t)windows[p_window].hWnd;1812}1813#if defined(GLES3_ENABLED)1814case WINDOW_VIEW: {1815if (gl_manager_native) {1816return (int64_t)gl_manager_native->get_hdc(p_window);1817} else {1818return (int64_t)GetDC(windows[p_window].hWnd);1819}1820}1821case OPENGL_CONTEXT: {1822if (gl_manager_native) {1823return (int64_t)gl_manager_native->get_hglrc(p_window);1824}1825if (gl_manager_angle) {1826return (int64_t)gl_manager_angle->get_context(p_window);1827}1828return 0;1829}1830case EGL_DISPLAY: {1831if (gl_manager_angle) {1832return (int64_t)gl_manager_angle->get_display(p_window);1833}1834return 0;1835}1836case EGL_CONFIG: {1837if (gl_manager_angle) {1838return (int64_t)gl_manager_angle->get_config(p_window);1839}1840return 0;1841}1842#endif1843default: {1844return 0;1845}1846}1847}18481849void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {1850_THREAD_SAFE_METHOD_18511852ERR_FAIL_COND(!windows.has(p_window));1853windows[p_window].instance_id = p_instance;1854}18551856ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window) const {1857_THREAD_SAFE_METHOD_18581859ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());1860return windows[p_window].instance_id;1861}18621863void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {1864_THREAD_SAFE_METHOD_18651866ERR_FAIL_COND(!windows.has(p_window));1867windows[p_window].rect_changed_callback = p_callable;1868}18691870void DisplayServerWindows::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {1871_THREAD_SAFE_METHOD_18721873ERR_FAIL_COND(!windows.has(p_window));1874windows[p_window].event_callback = p_callable;1875}18761877void DisplayServerWindows::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {1878_THREAD_SAFE_METHOD_18791880ERR_FAIL_COND(!windows.has(p_window));1881windows[p_window].input_event_callback = p_callable;1882}18831884void DisplayServerWindows::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {1885_THREAD_SAFE_METHOD_18861887ERR_FAIL_COND(!windows.has(p_window));1888windows[p_window].input_text_callback = p_callable;1889}18901891void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {1892_THREAD_SAFE_METHOD_18931894ERR_FAIL_COND(!windows.has(p_window));1895WindowData &window_data = windows[p_window];18961897window_data.drop_files_callback = p_callable;18981899if (window_data.drop_target == nullptr) {1900window_data.drop_target = memnew(DropTargetWindows(&window_data));1901ERR_FAIL_COND(RegisterDragDrop(window_data.hWnd, window_data.drop_target) != S_OK);1902}1903}19041905void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) {1906_THREAD_SAFE_METHOD_19071908ERR_FAIL_COND(!windows.has(p_window));1909SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data()));1910}19111912Size2i DisplayServerWindows::window_get_title_size(const String &p_title, WindowID p_window) const {1913_THREAD_SAFE_METHOD_19141915Size2i size;1916ERR_FAIL_COND_V(!windows.has(p_window), size);19171918const WindowData &wd = windows[p_window];1919if (wd.fullscreen || wd.minimized || wd.borderless) {1920return size;1921}19221923HDC hdc = GetDCEx(wd.hWnd, nullptr, DCX_WINDOW);1924if (hdc) {1925Char16String s = p_title.utf16();1926SIZE text_size;1927if (GetTextExtentPoint32W(hdc, (LPCWSTR)(s.get_data()), s.length(), &text_size)) {1928size.x = text_size.cx;1929size.y = text_size.cy;1930}19311932ReleaseDC(wd.hWnd, hdc);1933}1934RECT rect;1935if (DwmGetWindowAttribute(wd.hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rect, sizeof(RECT)) == S_OK) {1936if (rect.right - rect.left > 0) {1937ClientToScreen(wd.hWnd, (POINT *)&rect.left);1938ClientToScreen(wd.hWnd, (POINT *)&rect.right);19391940PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.left);1941PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.right);19421943size.x += (rect.right - rect.left);1944size.y = MAX(size.y, rect.bottom - rect.top);1945}1946}1947if (icon.is_valid()) {1948size.x += 32;1949} else {1950size.x += 16;1951}1952return size;1953}19541955void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {1956_THREAD_SAFE_METHOD_19571958ERR_FAIL_COND(!windows.has(p_window));1959windows[p_window].mpath = p_region;1960_update_window_mouse_passthrough(p_window);1961}19621963void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {1964ERR_FAIL_COND(!windows.has(p_window));19651966const WindowData &wd = windows[p_window];1967bool clip_pixel = (wd.multiwindow_fs || (wd.borderless && wd.maximized));1968bool pass_set = (wd.mpath.size() > 0);1969if (!clip_pixel && !pass_set) {1970SetWindowRgn(wd.hWnd, nullptr, TRUE);1971} else {1972HRGN region = nullptr;1973if (pass_set) {1974Vector<POINT> points;1975points.resize(wd.mpath.size());1976POINT *points_ptr = points.ptrw();1977for (int i = 0; i < wd.mpath.size(); i++) {1978if (wd.borderless) {1979points_ptr[i].x = wd.mpath[i].x;1980points_ptr[i].y = wd.mpath[i].y;1981} else {1982points_ptr[i].x = wd.mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME);1983points_ptr[i].y = wd.mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);1984}1985}1986region = CreatePolygonRgn(points.ptr(), points.size(), ALTERNATE);1987} else {1988region = CreateRectRgn(0, 0, wd.width, wd.height);1989}1990if (clip_pixel) {1991HRGN region_clip = CreateRectRgn(0, 0, wd.width, wd.height);1992CombineRgn(region, region, region_clip, RGN_AND);1993DeleteObject(region_clip);1994}1995SetWindowRgn(wd.hWnd, region, FALSE);1996}1997}19981999int DisplayServerWindows::window_get_current_screen(WindowID p_window) const {2000_THREAD_SAFE_METHOD_20012002ERR_FAIL_COND_V(!windows.has(p_window), INVALID_SCREEN);20032004EnumScreenData data = { 0, 0, MonitorFromWindow(windows[p_window].hWnd, MONITOR_DEFAULTTONEAREST) };2005EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);2006return data.screen;2007}20082009void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) {2010_THREAD_SAFE_METHOD_20112012ERR_FAIL_COND(!windows.has(p_window));20132014p_screen = _get_screen_index(p_screen);2015int screen_count = get_screen_count();2016ERR_FAIL_INDEX(p_screen, screen_count);20172018if (window_get_current_screen(p_window) == p_screen) {2019return;2020}2021const WindowData &wd = windows[p_window];20222023if (wd.parent_hwnd) {2024print_line("Embedded window can't be moved to another screen.");2025return;2026}2027if (wd.fullscreen) {2028Point2 pos = screen_get_position(p_screen) + _get_screens_origin();2029Size2 size = screen_get_size(p_screen);2030int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;20312032MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);2033} else if (wd.maximized) {2034Point2 pos = screen_get_position(p_screen) + _get_screens_origin();2035Size2 size = screen_get_size(p_screen);2036int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;20372038ShowWindow(wd.hWnd, SW_RESTORE);2039MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);2040ShowWindow(wd.hWnd, SW_MAXIMIZE);2041} else {2042Rect2i srect = screen_get_usable_rect(p_screen);2043Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));2044Size2i wsize = window_get_size(p_window);2045wpos += srect.position;20462047wpos = wpos.clamp(srect.position, srect.position + srect.size - wsize / 3);2048window_set_position(wpos, p_window);2049}2050}20512052Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {2053_THREAD_SAFE_METHOD_20542055ERR_FAIL_COND_V(!windows.has(p_window), Point2i());2056const WindowData &wd = windows[p_window];20572058if (wd.minimized) {2059return wd.last_pos;2060}20612062POINT point;2063point.x = 0;2064point.y = 0;20652066ClientToScreen(wd.hWnd, &point);20672068return Point2i(point.x, point.y) - _get_screens_origin();2069}20702071Point2i DisplayServerWindows::window_get_position_with_decorations(WindowID p_window) const {2072_THREAD_SAFE_METHOD_20732074ERR_FAIL_COND_V(!windows.has(p_window), Point2i());2075const WindowData &wd = windows[p_window];20762077if (wd.minimized) {2078return wd.last_pos;2079}20802081RECT r;2082if (GetWindowRect(wd.hWnd, &r)) {2083return Point2i(r.left, r.top) - _get_screens_origin();2084}20852086return Point2i();2087}20882089void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) {2090ERR_FAIL_COND(!windows.has(p_window));20912092POINT mouse_pos;2093if (GetCursorPos(&mouse_pos) && ScreenToClient(windows[p_window].hWnd, &mouse_pos)) {2094if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= windows[p_window].width && mouse_pos.y <= windows[p_window].height) {2095old_x = mouse_pos.x;2096old_y = mouse_pos.y;2097old_invalid = false;2098Input::get_singleton()->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y));2099}2100}2101}21022103void DisplayServerWindows::window_set_position(const Point2i &p_position, WindowID p_window) {2104_THREAD_SAFE_METHOD_21052106ERR_FAIL_COND(!windows.has(p_window));2107WindowData &wd = windows[p_window];21082109if (wd.parent_hwnd) {2110print_line("Embedded window can't be moved.");2111return;2112}21132114if (wd.fullscreen || wd.maximized) {2115return;2116}21172118Point2i offset = _get_screens_origin();21192120RECT rc;2121rc.left = p_position.x + offset.x;2122rc.right = p_position.x + wd.width + offset.x;2123rc.bottom = p_position.y + wd.height + offset.y;2124rc.top = p_position.y + offset.y;21252126const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);2127const DWORD exStyle = GetWindowLongPtr(wd.hWnd, GWL_EXSTYLE);21282129AdjustWindowRectEx(&rc, style, false, exStyle);2130MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);21312132wd.last_pos = p_position;2133_update_real_mouse_position(p_window);2134}21352136void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclusive) {2137_THREAD_SAFE_METHOD_2138ERR_FAIL_COND(!windows.has(p_window));2139WindowData &wd = windows[p_window];2140if (wd.exclusive != p_exclusive) {2141wd.exclusive = p_exclusive;2142if (wd.transient_parent != INVALID_WINDOW_ID) {2143if (wd.exclusive) {2144WindowData &wd_parent = windows[wd.transient_parent];2145SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);2146} else {2147SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);2148}2149}2150}2151}21522153void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) {2154_THREAD_SAFE_METHOD_21552156ERR_FAIL_COND(p_window == p_parent);2157ERR_FAIL_COND(!windows.has(p_window));21582159WindowData &wd_window = windows[p_window];21602161ERR_FAIL_COND(wd_window.transient_parent == p_parent);2162ERR_FAIL_COND_MSG(wd_window.always_on_top, "Windows with the 'on top' can't become transient.");21632164if (p_parent == INVALID_WINDOW_ID) {2165// Remove transient.21662167ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);2168ERR_FAIL_COND(!windows.has(wd_window.transient_parent));21692170WindowData &wd_parent = windows[wd_window.transient_parent];21712172wd_window.transient_parent = INVALID_WINDOW_ID;2173wd_parent.transient_children.erase(p_window);21742175if (wd_window.exclusive) {2176SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);2177}2178} else {2179ERR_FAIL_COND(!windows.has(p_parent));2180ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");2181WindowData &wd_parent = windows[p_parent];21822183wd_window.transient_parent = p_parent;2184wd_parent.transient_children.insert(p_window);21852186if (wd_window.exclusive) {2187SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);2188}2189}2190}21912192void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_window) {2193_THREAD_SAFE_METHOD_21942195ERR_FAIL_COND(!windows.has(p_window));2196WindowData &wd = windows[p_window];21972198if (wd.parent_hwnd) {2199print_line("Embedded windows can't have a maximum size.");2200return;2201}22022203if ((p_size != Size2()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {2204ERR_PRINT("Maximum window size can't be smaller than minimum window size!");2205return;2206}2207wd.max_size = p_size;2208}22092210Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const {2211_THREAD_SAFE_METHOD_22122213ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2214const WindowData &wd = windows[p_window];2215return wd.max_size;2216}22172218void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_window) {2219_THREAD_SAFE_METHOD_22202221ERR_FAIL_COND(!windows.has(p_window));2222WindowData &wd = windows[p_window];22232224if (wd.parent_hwnd) {2225print_line("Embedded windows can't have a minimum size.");2226return;2227}22282229if ((p_size != Size2()) && (wd.max_size != Size2()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {2230ERR_PRINT("Minimum window size can't be larger than maximum window size!");2231return;2232}2233wd.min_size = p_size;2234}22352236Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const {2237_THREAD_SAFE_METHOD_22382239ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2240const WindowData &wd = windows[p_window];2241return wd.min_size;2242}22432244void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_window) {2245_THREAD_SAFE_METHOD_22462247ERR_FAIL_COND(!windows.has(p_window));2248WindowData &wd = windows[p_window];22492250if (wd.parent_hwnd) {2251print_line("Embedded window can't be resized.");2252return;2253}22542255if (wd.fullscreen || wd.maximized) {2256return;2257}22582259int w = p_size.width;2260int h = p_size.height;2261RECT rect;2262GetWindowRect(wd.hWnd, &rect);22632264if (!wd.borderless) {2265RECT crect;2266GetClientRect(wd.hWnd, &crect);22672268w += (rect.right - rect.left) - (crect.right - crect.left);2269h += (rect.bottom - rect.top) - (crect.bottom - crect.top);2270}22712272MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE);2273}22742275Size2i DisplayServerWindows::window_get_size(WindowID p_window) const {2276_THREAD_SAFE_METHOD_22772278ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2279const WindowData &wd = windows[p_window];22802281// GetClientRect() returns a zero rect for a minimized window, so we need to get the size in another way.2282if (wd.minimized) {2283return Size2(wd.width, wd.height);2284}22852286RECT r;2287if (GetClientRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.2288int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;2289return Size2(r.right - r.left - off_x, r.bottom - r.top);2290}2291return Size2();2292}22932294Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window) const {2295_THREAD_SAFE_METHOD_22962297ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2298const WindowData &wd = windows[p_window];22992300RECT r;2301if (GetWindowRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.2302int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;2303return Size2(r.right - r.left - off_x, r.bottom - r.top);2304}2305return Size2();2306}23072308void 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, DWORD &r_style, DWORD &r_style_ex) {2309// Windows docs for window styles:2310// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles2311// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles23122313r_style = 0;2314r_style_ex = WS_EX_WINDOWEDGE;2315if (p_main_window) {2316// When embedded, we don't want the window to have WS_EX_APPWINDOW because it will2317// show the embedded process in the taskbar and Alt-Tab.2318if (!p_embed_child) {2319r_style_ex |= WS_EX_APPWINDOW;2320}2321if (p_initialized) {2322r_style |= WS_VISIBLE;2323}2324}23252326if (p_embed_child) {2327r_style |= WS_POPUP;2328} else if (p_fullscreen || p_borderless) {2329r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past.2330if (p_minimized) {2331r_style |= WS_MINIMIZE;2332} else if (p_maximized) {2333r_style |= WS_MAXIMIZE;2334}2335if (!p_fullscreen) {2336r_style |= WS_SYSMENU;2337if (!p_no_min_btn) {2338r_style |= WS_MINIMIZEBOX;2339}2340if (!p_no_max_btn) {2341r_style |= WS_MAXIMIZEBOX;2342}2343}2344} else {2345if (p_resizable) {2346if (p_minimized) {2347r_style = WS_OVERLAPPEDWINDOW | WS_MINIMIZE;2348} else if (p_maximized) {2349r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE;2350} else {2351r_style = WS_OVERLAPPEDWINDOW;2352}2353if (p_no_min_btn) {2354r_style &= ~WS_MINIMIZEBOX;2355}2356if (p_no_max_btn) {2357r_style &= ~WS_MAXIMIZEBOX;2358}2359} else {2360if (p_minimized) {2361r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZE;2362} else {2363r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;2364}2365if (!p_no_min_btn) {2366r_style |= WS_MINIMIZEBOX;2367}2368if (!p_no_max_btn) {2369r_style |= WS_MAXIMIZEBOX;2370}2371}2372}23732374if (p_no_activate_focus && !p_embed_child) {2375r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;2376}23772378if (!p_borderless && !p_no_activate_focus && p_initialized) {2379r_style |= WS_VISIBLE;2380}23812382r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;2383r_style_ex |= WS_EX_ACCEPTFILES;23842385if (OS::get_singleton()->get_current_rendering_driver_name() == "d3d12") {2386r_style_ex |= WS_EX_NOREDIRECTIONBITMAP;2387}2388}23892390void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint) {2391_THREAD_SAFE_METHOD_23922393ERR_FAIL_COND(!windows.has(p_window));2394WindowData &wd = windows[p_window];23952396DWORD style = 0;2397DWORD style_ex = 0;23982399_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, style, style_ex);24002401SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);2402SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);24032404if (icon.is_valid()) {2405set_icon(icon);2406}24072408SetWindowPos(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));24092410if (p_repaint) {2411RECT rect;2412GetWindowRect(wd.hWnd, &rect);2413int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;2414MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left + off_x, rect.bottom - rect.top, TRUE);2415}2416}24172418void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) {2419_THREAD_SAFE_METHOD_24202421ERR_FAIL_COND(!windows.has(p_window));2422WindowData &wd = windows[p_window];24232424if (p_mode != WINDOW_MODE_WINDOWED && wd.parent_hwnd) {2425print_line("Embedded window only supports Windowed mode.");2426return;2427}24282429bool was_fullscreen = wd.fullscreen;2430wd.was_fullscreen_pre_min = false;24312432if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {2433int cs = window_get_current_screen(p_window);2434Rect2i full = Rect2i(screen_get_position(cs), screen_get_size(cs));2435Rect2i usable = screen_get_usable_rect(cs);2436if (full == usable) {2437p_mode = WINDOW_MODE_FULLSCREEN;2438}2439}24402441if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {2442RECT rect;24432444wd.fullscreen = false;2445wd.multiwindow_fs = false;24462447// Restore previous maximized state.2448wd.maximized = wd.was_maximized_pre_fs;24492450_update_window_style(p_window, false);24512452// Restore window rect after exiting fullscreen.2453if (wd.pre_fs_valid) {2454rect = wd.pre_fs_rect;2455} else {2456rect.left = 0;2457rect.right = wd.width;2458rect.top = 0;2459rect.bottom = wd.height;2460}24612462ShowWindow(wd.hWnd, SW_RESTORE);2463MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);24642465if (restore_mouse_trails > 1) {2466SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);2467restore_mouse_trails = 0;2468}2469}24702471if ((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) {2472RECT rect;2473if (wd.pre_fs_valid) {2474rect = wd.pre_fs_rect;2475} else {2476rect.left = 0;2477rect.right = wd.width;2478rect.top = 0;2479rect.bottom = wd.height;2480}24812482ShowWindow(wd.hWnd, SW_RESTORE);2483MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);2484}24852486if (p_mode == WINDOW_MODE_WINDOWED) {2487ShowWindow(wd.hWnd, SW_NORMAL);2488wd.maximized = false;2489wd.minimized = false;2490}24912492if (p_mode == WINDOW_MODE_MAXIMIZED && !wd.borderless) {2493ShowWindow(wd.hWnd, SW_MAXIMIZE);2494wd.maximized = true;2495wd.minimized = false;2496}24972498if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {2499if (!was_fullscreen && !(wd.maximized && wd.borderless)) {2500// Save non-fullscreen rect before entering fullscreen.2501GetWindowRect(wd.hWnd, &wd.pre_fs_rect);2502wd.pre_fs_valid = true;2503}2504ShowWindow(wd.hWnd, SW_NORMAL);2505wd.maximized = true;2506wd.minimized = false;25072508int cs = window_get_current_screen(p_window);2509Rect2i usable = screen_get_usable_rect(cs);2510Point2 pos = usable.position + _get_screens_origin();2511Size2 size = usable.size;2512MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);2513}25142515if (p_mode == WINDOW_MODE_MINIMIZED) {2516ShowWindow(wd.hWnd, SW_MINIMIZE);2517wd.maximized = false;2518wd.minimized = true;2519wd.was_fullscreen_pre_min = was_fullscreen;2520}25212522if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {2523wd.multiwindow_fs = false;2524} else if (p_mode == WINDOW_MODE_FULLSCREEN) {2525wd.multiwindow_fs = true;2526}2527_update_window_style(p_window, false);25282529if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) {2530if (wd.minimized || wd.maximized) {2531ShowWindow(wd.hWnd, SW_RESTORE);2532}25332534// Save previous maximized stare.2535wd.was_maximized_pre_fs = wd.maximized;25362537if (!was_fullscreen && !(wd.maximized && wd.borderless)) {2538// Save non-fullscreen rect before entering fullscreen.2539GetWindowRect(wd.hWnd, &wd.pre_fs_rect);2540wd.pre_fs_valid = true;2541}25422543int cs = window_get_current_screen(p_window);2544Point2 pos = screen_get_position(cs) + _get_screens_origin();2545Size2 size = screen_get_size(cs);25462547wd.fullscreen = true;2548wd.maximized = false;2549wd.minimized = false;25502551_update_window_style(p_window, false);25522553int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;2554MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);25552556// If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode.2557// Save number of trails so we can restore when exiting, then turn off mouse trails2558SystemParametersInfoA(SPI_GETMOUSETRAILS, 0, &restore_mouse_trails, 0);2559if (restore_mouse_trails > 1) {2560SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, nullptr, 0);2561}2562}2563_update_window_mouse_passthrough(p_window);2564}25652566DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {2567_THREAD_SAFE_METHOD_25682569ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);2570const WindowData &wd = windows[p_window];25712572if (wd.fullscreen) {2573if (wd.multiwindow_fs) {2574return WINDOW_MODE_FULLSCREEN;2575} else {2576return WINDOW_MODE_EXCLUSIVE_FULLSCREEN;2577}2578} else if (wd.minimized) {2579return WINDOW_MODE_MINIMIZED;2580} else if (wd.maximized) {2581return WINDOW_MODE_MAXIMIZED;2582} else {2583return WINDOW_MODE_WINDOWED;2584}2585}25862587bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const {2588_THREAD_SAFE_METHOD_25892590ERR_FAIL_COND_V(!windows.has(p_window), false);2591const WindowData &wd = windows[p_window];25922593const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);2594return (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX;2595}25962597void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {2598_THREAD_SAFE_METHOD_25992600ERR_FAIL_COND(!windows.has(p_window));2601WindowData &wd = windows[p_window];2602switch (p_flag) {2603case WINDOW_FLAG_MINIMIZE_DISABLED: {2604wd.no_min_btn = p_enabled;2605_update_window_style(p_window);2606} break;2607case WINDOW_FLAG_MAXIMIZE_DISABLED: {2608wd.no_max_btn = p_enabled;2609_update_window_style(p_window);2610} break;2611case WINDOW_FLAG_RESIZE_DISABLED: {2612if (p_enabled && wd.parent_hwnd) {2613print_line("Embedded window resize can't be disabled.");2614return;2615}2616wd.resizable = !p_enabled;2617_update_window_style(p_window);2618} break;2619case WINDOW_FLAG_BORDERLESS: {2620wd.borderless = p_enabled;2621_update_window_mouse_passthrough(p_window);2622_update_window_style(p_window);2623ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.2624} break;2625case WINDOW_FLAG_ALWAYS_ON_TOP: {2626ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top.");2627if (p_enabled && wd.parent_hwnd) {2628print_line("Embedded window can't become on top.");2629return;2630}2631wd.always_on_top = p_enabled;2632_update_window_style(p_window);2633} break;2634case WINDOW_FLAG_SHARP_CORNERS: {2635wd.sharp_corners = p_enabled;2636DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;2637::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));2638_update_window_style(p_window);2639} break;2640case WINDOW_FLAG_TRANSPARENT: {2641if (p_enabled) {2642// Enable per-pixel alpha.2643if (OS::get_singleton()->is_layered_allowed()) {2644DWM_BLURBEHIND bb;2645ZeroMemory(&bb, sizeof(bb));2646HRGN hRgn = CreateRectRgn(0, 0, -1, -1);2647bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;2648bb.hRgnBlur = hRgn;2649bb.fEnable = TRUE;2650DwmEnableBlurBehindWindow(wd.hWnd, &bb);2651}2652wd.layered_window = true;2653} else {2654// Disable per-pixel alpha.2655wd.layered_window = false;2656if (OS::get_singleton()->is_layered_allowed()) {2657DWM_BLURBEHIND bb;2658ZeroMemory(&bb, sizeof(bb));2659HRGN hRgn = CreateRectRgn(0, 0, -1, -1);2660bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;2661bb.hRgnBlur = hRgn;2662bb.fEnable = FALSE;2663DwmEnableBlurBehindWindow(wd.hWnd, &bb);2664}2665}2666} break;2667case WINDOW_FLAG_NO_FOCUS: {2668wd.no_focus = p_enabled;2669_update_window_style(p_window);2670} break;2671case WINDOW_FLAG_MOUSE_PASSTHROUGH: {2672wd.mpass = p_enabled;2673} break;2674case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {2675wd.hide_from_capture = p_enabled;2676if (p_enabled) {2677if (os_ver.dwBuildNumber >= 19041) {2678SetWindowDisplayAffinity(wd.hWnd, WDA_EXCLUDEFROMCAPTURE);2679} else {2680SetWindowDisplayAffinity(wd.hWnd, WDA_MONITOR);2681}2682} else {2683SetWindowDisplayAffinity(wd.hWnd, WDA_NONE);2684}2685} break;2686case WINDOW_FLAG_POPUP: {2687ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");2688ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");2689if (p_enabled && wd.parent_hwnd) {2690print_line("Embedded window can't be popup.");2691return;2692}2693wd.is_popup = p_enabled;2694} break;2695default:2696break;2697}2698}26992700bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window) const {2701_THREAD_SAFE_METHOD_27022703ERR_FAIL_COND_V(!windows.has(p_window), false);2704const WindowData &wd = windows[p_window];2705switch (p_flag) {2706case WINDOW_FLAG_MAXIMIZE_DISABLED: {2707return wd.no_max_btn;2708} break;2709case WINDOW_FLAG_MINIMIZE_DISABLED: {2710return wd.no_min_btn;2711} break;2712case WINDOW_FLAG_RESIZE_DISABLED: {2713return !wd.resizable;2714} break;2715case WINDOW_FLAG_BORDERLESS: {2716return wd.borderless;2717} break;2718case WINDOW_FLAG_ALWAYS_ON_TOP: {2719return wd.always_on_top;2720} break;2721case WINDOW_FLAG_SHARP_CORNERS: {2722return wd.sharp_corners;2723} break;2724case WINDOW_FLAG_TRANSPARENT: {2725return wd.layered_window;2726} break;2727case WINDOW_FLAG_NO_FOCUS: {2728return wd.no_focus;2729} break;2730case WINDOW_FLAG_MOUSE_PASSTHROUGH: {2731return wd.mpass;2732} break;2733case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {2734return wd.hide_from_capture;2735} break;2736case WINDOW_FLAG_POPUP: {2737return wd.is_popup;2738} break;2739default:2740break;2741}27422743return false;2744}27452746void DisplayServerWindows::window_request_attention(WindowID p_window) {2747_THREAD_SAFE_METHOD_27482749ERR_FAIL_COND(!windows.has(p_window));2750const WindowData &wd = windows[p_window];27512752FLASHWINFO info;2753info.cbSize = sizeof(FLASHWINFO);2754info.hwnd = wd.hWnd;2755info.dwFlags = FLASHW_ALL;2756info.dwTimeout = 0;2757info.uCount = 2;2758FlashWindowEx(&info);2759}27602761void DisplayServerWindows::window_move_to_foreground(WindowID p_window) {2762_THREAD_SAFE_METHOD_27632764ERR_FAIL_COND(!windows.has(p_window));2765WindowData &wd = windows[p_window];27662767if (!wd.no_focus && !wd.is_popup) {2768SetForegroundWindow(wd.hWnd);2769}2770}27712772bool DisplayServerWindows::window_is_focused(WindowID p_window) const {2773_THREAD_SAFE_METHOD_27742775ERR_FAIL_COND_V(!windows.has(p_window), false);2776const WindowData &wd = windows[p_window];27772778return wd.window_focused;2779}27802781DisplayServerWindows::WindowID DisplayServerWindows::get_focused_window() const {2782return last_focused_window;2783}27842785bool DisplayServerWindows::window_can_draw(WindowID p_window) const {2786_THREAD_SAFE_METHOD_27872788ERR_FAIL_COND_V(!windows.has(p_window), false);2789const WindowData &wd = windows[p_window];2790return !wd.minimized;2791}27922793bool DisplayServerWindows::can_any_window_draw() const {2794_THREAD_SAFE_METHOD_27952796for (const KeyValue<WindowID, WindowData> &E : windows) {2797if (!E.value.minimized) {2798return true;2799}2800}28012802return false;2803}28042805int DisplayServerWindows::accessibility_should_increase_contrast() const {2806HIGHCONTRASTA hc;2807hc.cbSize = sizeof(HIGHCONTRAST);2808if (!SystemParametersInfoA(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &hc, 0)) {2809return -1;2810}2811return (hc.dwFlags & HCF_HIGHCONTRASTON);2812}28132814int DisplayServerWindows::accessibility_should_reduce_animation() const {2815BOOL anim_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2816if (!SystemParametersInfoA(SPI_GETCLIENTAREAANIMATION, 0, &anim_enabled, 0)) {2817return -1;2818}2819return (!anim_enabled);2820}28212822int DisplayServerWindows::accessibility_should_reduce_transparency() const {2823BOOL tr_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2824if (!SystemParametersInfoA(SPI_GETDISABLEOVERLAPPEDCONTENT, 0, &tr_enabled, 0)) {2825return -1;2826}2827return tr_enabled;2828}28292830int DisplayServerWindows::accessibility_screen_reader_active() const {2831BOOL sr_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2832if (SystemParametersInfoA(SPI_GETSCREENREADER, 0, &sr_enabled, 0) && sr_enabled) {2833return true;2834}28352836static const WCHAR *narrator_mutex_name = L"NarratorRunning";2837HANDLE narrator_mutex = OpenMutexW(MUTEX_ALL_ACCESS, false, narrator_mutex_name);2838if (narrator_mutex) {2839CloseHandle(narrator_mutex);2840return true;2841}2842return false;2843}28442845Vector2i DisplayServerWindows::ime_get_selection() const {2846_THREAD_SAFE_METHOD_28472848DisplayServer::WindowID window_id = _get_focused_window_or_popup();2849const WindowData &wd = windows[window_id];2850if (!wd.ime_active) {2851return Vector2i();2852}28532854int cursor = ImmGetCompositionStringW(wd.im_himc, GCS_CURSORPOS, nullptr, 0);28552856int32_t length = ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, nullptr, 0);2857wchar_t *string = reinterpret_cast<wchar_t *>(memalloc(length));2858ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);28592860int32_t utf32_cursor = 0;2861for (int32_t i = 0; i < length / int32_t(sizeof(wchar_t)); i++) {2862if ((string[i] & 0xfffffc00) == 0xd800) {2863i++;2864}2865if (i < cursor) {2866utf32_cursor++;2867} else {2868break;2869}2870}28712872memdelete(string);28732874return Vector2i(utf32_cursor, 0);2875}28762877String DisplayServerWindows::ime_get_text() const {2878_THREAD_SAFE_METHOD_28792880DisplayServer::WindowID window_id = _get_focused_window_or_popup();2881const WindowData &wd = windows[window_id];2882if (!wd.ime_active) {2883return String();2884}28852886String ret;2887int32_t length = ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, nullptr, 0);2888wchar_t *string = reinterpret_cast<wchar_t *>(memalloc(length));2889ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);2890ret.append_utf16((char16_t *)string, length / sizeof(wchar_t));28912892memdelete(string);28932894return ret;2895}28962897void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p_window) {2898_THREAD_SAFE_METHOD_28992900ERR_FAIL_COND(!windows.has(p_window));2901WindowData &wd = windows[p_window];29022903if (p_active) {2904wd.ime_active = true;2905ImmAssociateContext(wd.hWnd, wd.im_himc);2906CreateCaret(wd.hWnd, nullptr, 1, 1);2907window_set_ime_position(wd.im_position, p_window);2908} else {2909ImmAssociateContext(wd.hWnd, (HIMC) nullptr);2910DestroyCaret();2911wd.ime_active = false;2912}2913}29142915void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {2916_THREAD_SAFE_METHOD_29172918ERR_FAIL_COND(!windows.has(p_window));2919WindowData &wd = windows[p_window];29202921wd.im_position = p_pos;29222923HIMC himc = ImmGetContext(wd.hWnd);2924if (himc == (HIMC) nullptr) {2925return;2926}29272928COMPOSITIONFORM cps;2929cps.dwStyle = CFS_POINT;2930cps.ptCurrentPos.x = wd.im_position.x;2931cps.ptCurrentPos.y = wd.im_position.y;2932ImmSetCompositionWindow(himc, &cps);2933ImmReleaseContext(wd.hWnd, himc);2934}29352936void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {2937_THREAD_SAFE_METHOD_29382939ERR_FAIL_INDEX(p_shape, CURSOR_MAX);29402941if (cursor_shape == p_shape) {2942return;2943}29442945if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {2946cursor_shape = p_shape;2947return;2948}29492950static const LPCTSTR win_cursors[CURSOR_MAX] = {2951IDC_ARROW,2952IDC_IBEAM,2953IDC_HAND, // Finger.2954IDC_CROSS,2955IDC_WAIT,2956IDC_APPSTARTING,2957IDC_SIZEALL,2958IDC_ARROW,2959IDC_NO,2960IDC_SIZENS,2961IDC_SIZEWE,2962IDC_SIZENESW,2963IDC_SIZENWSE,2964IDC_SIZEALL,2965IDC_SIZENS,2966IDC_SIZEWE,2967IDC_HELP2968};29692970if (cursors_cache.has(p_shape)) {2971SetCursor(cursors[p_shape]);2972} else {2973SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));2974}29752976cursor_shape = p_shape;2977}29782979DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {2980return cursor_shape;2981}29822983void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {2984_THREAD_SAFE_METHOD_29852986ERR_FAIL_INDEX(p_shape, CURSOR_MAX);29872988if (p_cursor.is_valid()) {2989RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);29902991if (cursor_c) {2992if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {2993cursor_set_shape(p_shape);2994return;2995}29962997cursors_cache.erase(p_shape);2998}29993000Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);3001ERR_FAIL_COND(image.is_null());3002Vector2i texture_size = image->get_size();30033004UINT image_size = texture_size.width * texture_size.height;30053006// Create the BITMAP with alpha channel.3007COLORREF *buffer = nullptr;30083009BITMAPV5HEADER bi;3010ZeroMemory(&bi, sizeof(bi));3011bi.bV5Size = sizeof(bi);3012bi.bV5Width = texture_size.width;3013bi.bV5Height = -texture_size.height;3014bi.bV5Planes = 1;3015bi.bV5BitCount = 32;3016bi.bV5Compression = BI_BITFIELDS;3017bi.bV5RedMask = 0x00ff0000;3018bi.bV5GreenMask = 0x0000ff00;3019bi.bV5BlueMask = 0x000000ff;3020bi.bV5AlphaMask = 0xff000000;30213022HDC dc = GetDC(nullptr);3023HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);3024HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr);30253026bool fully_transparent = true;3027for (UINT index = 0; index < image_size; index++) {3028int row_index = std::floor(index / texture_size.width);3029int column_index = index % int(texture_size.width);30303031const Color &c = image->get_pixel(column_index, row_index);3032fully_transparent = fully_transparent && (c.a == 0.f);30333034*(buffer + index) = c.to_argb32();3035}30363037// Finally, create the icon.3038if (cursors[p_shape]) {3039DestroyIcon(cursors[p_shape]);3040}30413042if (fully_transparent) {3043cursors[p_shape] = nullptr;3044} else {3045ICONINFO iconinfo;3046iconinfo.fIcon = FALSE;3047iconinfo.xHotspot = p_hotspot.x;3048iconinfo.yHotspot = p_hotspot.y;3049iconinfo.hbmMask = mask;3050iconinfo.hbmColor = bitmap;3051cursors[p_shape] = CreateIconIndirect(&iconinfo);3052}30533054Vector<Variant> params;3055params.push_back(p_cursor);3056params.push_back(p_hotspot);3057cursors_cache.insert(p_shape, params);30583059if (p_shape == cursor_shape) {3060if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {3061SetCursor(cursors[p_shape]);3062}3063}30643065DeleteObject(mask);3066DeleteObject(bitmap);3067ReleaseDC(nullptr, dc);3068} else {3069// Reset to default system cursor.3070if (cursors[p_shape]) {3071DestroyIcon(cursors[p_shape]);3072}3073cursors[p_shape] = nullptr;30743075cursors_cache.erase(p_shape);30763077CursorShape c = cursor_shape;3078cursor_shape = CURSOR_MAX;3079cursor_set_shape(c);3080}3081}30823083bool DisplayServerWindows::get_swap_cancel_ok() {3084return true;3085}30863087void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {3088_THREAD_SAFE_METHOD_30893090AllowSetForegroundWindow(pid);3091}30923093struct WindowEnumData {3094DWORD process_id;3095HWND parent_hWnd;3096HWND hWnd;3097};30983099static BOOL CALLBACK _enum_proc_find_window_from_process_id_callback(HWND hWnd, LPARAM lParam) {3100WindowEnumData &ed = *(WindowEnumData *)lParam;3101DWORD process_id = 0x0;31023103GetWindowThreadProcessId(hWnd, &process_id);3104if (ed.process_id == process_id) {3105if (GetParent(hWnd) != ed.parent_hWnd) {3106return TRUE;3107}31083109// Found it.3110ed.hWnd = hWnd;3111SetLastError(ERROR_SUCCESS);3112return FALSE;3113}3114// Continue enumeration.3115return TRUE;3116}31173118HWND DisplayServerWindows::_find_window_from_process_id(OS::ProcessID p_pid, HWND p_current_hwnd) {3119DWORD pid = p_pid;3120WindowEnumData ed = { pid, p_current_hwnd, NULL };31213122// First, check our own child, maybe it's already embedded.3123if (!EnumChildWindows(p_current_hwnd, _enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {3124if (ed.hWnd) {3125return ed.hWnd;3126}3127}31283129// Then check all the opened windows on the computer.3130if (!EnumWindows(_enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {3131return ed.hWnd;3132}31333134return NULL;3135}31363137Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {3138_THREAD_SAFE_METHOD_31393140ERR_FAIL_COND_V(!windows.has(p_window), FAILED);31413142const WindowData &wd = windows[p_window];31433144EmbeddedProcessData *ep = nullptr;3145if (embedded_processes.has(p_pid)) {3146ep = embedded_processes.get(p_pid);3147} else {3148// New process, trying to find the window.3149HWND handle_to_embed = _find_window_from_process_id(p_pid, wd.hWnd);3150if (!handle_to_embed) {3151return ERR_DOES_NOT_EXIST;3152}31533154const DWORD style = GetWindowLongPtr(handle_to_embed, GWL_STYLE);31553156ep = memnew(EmbeddedProcessData);3157ep->window_handle = handle_to_embed;3158ep->parent_window_handle = wd.hWnd;3159ep->is_visible = (style & WS_VISIBLE) == WS_VISIBLE;31603161embedded_processes.insert(p_pid, ep);3162}31633164if (p_rect.size.x <= 100 || p_rect.size.y <= 100) {3165p_visible = false;3166}31673168// In Godot, the window position is offset by the screen's origin coordinates.3169// We need to adjust for this when a screen is positioned in the negative space3170// (e.g., a screen to the left of the main screen).3171const Rect2i adjusted_rect = Rect2i(p_rect.position + _get_screens_origin(), p_rect.size);31723173// Use HWND_BOTTOM to prevent reordering of the embedded window over another popup.3174SetWindowPos(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);31753176if (ep->is_visible != p_visible) {3177if (p_visible) {3178ShowWindow(ep->window_handle, SW_SHOWNA);3179} else {3180ShowWindow(ep->window_handle, SW_HIDE);3181}3182ep->is_visible = p_visible;3183}31843185if (p_grab_focus) {3186SetForegroundWindow(ep->window_handle);3187SetFocus(ep->window_handle);3188}31893190return OK;3191}31923193Error DisplayServerWindows::request_close_embedded_process(OS::ProcessID p_pid) {3194_THREAD_SAFE_METHOD_31953196if (!embedded_processes.has(p_pid)) {3197return ERR_DOES_NOT_EXIST;3198}31993200EmbeddedProcessData *ep = embedded_processes.get(p_pid);32013202// Send a close message to gracefully close the process.3203PostMessage(ep->window_handle, WM_CLOSE, 0, 0);32043205return OK;3206}32073208Error DisplayServerWindows::remove_embedded_process(OS::ProcessID p_pid) {3209_THREAD_SAFE_METHOD_32103211if (!embedded_processes.has(p_pid)) {3212return ERR_DOES_NOT_EXIST;3213}32143215EmbeddedProcessData *ep = embedded_processes.get(p_pid);32163217request_close_embedded_process(p_pid);32183219// This is a workaround to ensure the parent window correctly regains focus after the3220// embedded window is closed. When the embedded window is closed while it has focus,3221// the parent window (the editor) does not become active. It appears focused but is not truly activated.3222// Opening a new window and closing it forces Windows to set the focus and activation correctly.3223DWORD style = WS_POPUP | WS_VISIBLE;3224DWORD style_ex = WS_EX_TOPMOST;32253226WNDCLASSW wcTemp = {};3227wcTemp.lpfnWndProc = DefWindowProcW;3228wcTemp.hInstance = GetModuleHandle(nullptr);3229wcTemp.lpszClassName = L"Engine temp window";3230RegisterClassW(&wcTemp);32313232HWND hWnd = CreateWindowExW(3233style_ex,3234L"Engine temp window", L"",3235style,32360,32370,32381,32391,3240ep->parent_window_handle,3241nullptr,3242GetModuleHandle(nullptr),3243nullptr);32443245SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);32463247DestroyWindow(hWnd);3248UnregisterClassW(L"Engine temp window", GetModuleHandle(nullptr));32493250SetForegroundWindow(ep->parent_window_handle);32513252embedded_processes.erase(p_pid);3253memdelete(ep);32543255return OK;3256}32573258OS::ProcessID DisplayServerWindows::get_focused_process_id() {3259HWND hwnd = GetForegroundWindow();3260if (!hwnd) {3261return 0;3262}32633264// Get the process ID of the window.3265DWORD processID;3266GetWindowThreadProcessId(hwnd, &processID);32673268return processID;3269}32703271static HRESULT CALLBACK win32_task_dialog_callback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) {3272if (msg == TDN_CREATED) {3273// To match the input text dialog.3274SendMessageW(hwnd, WM_SETICON, ICON_BIG, 0);3275SendMessageW(hwnd, WM_SETICON, ICON_SMALL, 0);3276}32773278return 0;3279}32803281Error DisplayServerWindows::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {3282_THREAD_SAFE_METHOD_32833284TASKDIALOGCONFIG config;3285ZeroMemory(&config, sizeof(TASKDIALOGCONFIG));3286config.cbSize = sizeof(TASKDIALOGCONFIG);32873288Char16String title = p_title.utf16();3289Char16String message = p_description.utf16();3290LocalVector<Char16String> buttons;3291for (String s : p_buttons) {3292buttons.push_back(s.utf16());3293}32943295WindowID window_id = _get_focused_window_or_popup();3296if (!windows.has(window_id)) {3297window_id = MAIN_WINDOW_ID;3298}32993300config.pszWindowTitle = (LPCWSTR)(title.get_data());3301config.pszContent = (LPCWSTR)(message.get_data());3302config.hwndParent = windows[window_id].hWnd;33033304const int button_count = buttons.size();3305config.cButtons = button_count;33063307// No dynamic stack array size :(3308TASKDIALOG_BUTTON *tbuttons = button_count != 0 ? (TASKDIALOG_BUTTON *)alloca(sizeof(TASKDIALOG_BUTTON) * button_count) : nullptr;3309if (tbuttons) {3310for (int i = 0; i < button_count; i++) {3311tbuttons[i].nButtonID = i + 100;3312tbuttons[i].pszButtonText = (LPCWSTR)(buttons[i].get_data());3313}3314}3315config.pButtons = tbuttons;3316config.pfCallback = win32_task_dialog_callback;33173318Error result = FAILED;3319HMODULE comctl = LoadLibraryW(L"comctl32.dll");3320if (comctl) {3321typedef HRESULT(WINAPI * TaskDialogIndirectPtr)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);33223323TaskDialogIndirectPtr task_dialog_indirect = (TaskDialogIndirectPtr)(void *)GetProcAddress(comctl, "TaskDialogIndirect");3324int button_pressed;33253326if (task_dialog_indirect && SUCCEEDED(task_dialog_indirect(&config, &button_pressed, nullptr, nullptr))) {3327if (p_callback.is_valid()) {3328Variant button = button_pressed - 100;3329const Variant *args[1] = { &button };3330Variant ret;3331Callable::CallError ce;3332p_callback.callp(args, 1, ret, ce);3333if (ce.error != Callable::CallError::CALL_OK) {3334ERR_PRINT(vformat("Failed to execute dialog callback: %s.", Variant::get_callable_error_text(p_callback, args, 1, ce)));3335}3336}33373338result = OK;3339}3340FreeLibrary(comctl);3341} else {3342ERR_PRINT("Unable to create native dialog.");3343}33443345return result;3346}33473348struct Win32InputTextDialogInit {3349const char16_t *title;3350const char16_t *description;3351const char16_t *partial;3352const Callable &callback;3353};33543355static int scale_with_dpi(int p_pos, int p_dpi) {3356return IsProcessDPIAware() ? (p_pos * p_dpi / 96) : p_pos;3357}33583359static INT_PTR input_text_dialog_init(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3360Win32InputTextDialogInit init = *(Win32InputTextDialogInit *)lParam;3361SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)&init.callback); // Set dialog callback.33623363SetWindowTextW(hWnd, (LPCWSTR)init.title);33643365const int dpi = DisplayServerWindows::get_singleton()->screen_get_dpi();33663367const int margin = scale_with_dpi(7, dpi);3368const SIZE dlg_size = { scale_with_dpi(300, dpi), scale_with_dpi(50, dpi) };33693370int str_len = lstrlenW((LPCWSTR)init.description);3371SIZE str_size = { dlg_size.cx, 0 };3372if (str_len > 0) {3373HDC hdc = GetDC(nullptr);3374RECT trect = { margin, margin, margin + dlg_size.cx, margin + dlg_size.cy };3375SelectObject(hdc, (HFONT)SendMessageW(hWnd, WM_GETFONT, 0, 0));33763377// `+ margin` adds some space between the static text and the edit field.3378// Don't scale this with DPI because DPI is already handled by DrawText.3379str_size.cy = DrawTextW(hdc, (LPCWSTR)init.description, str_len, &trect, DT_LEFT | DT_WORDBREAK | DT_CALCRECT) + margin;33803381ReleaseDC(nullptr, hdc);3382}33833384RECT crect, wrect;3385GetClientRect(hWnd, &crect);3386GetWindowRect(hWnd, &wrect);3387int sw = GetSystemMetrics(SM_CXSCREEN);3388int sh = GetSystemMetrics(SM_CYSCREEN);3389int new_width = dlg_size.cx + margin * 2 + wrect.right - wrect.left - crect.right;3390int new_height = dlg_size.cy + margin * 2 + wrect.bottom - wrect.top - crect.bottom + str_size.cy;33913392MoveWindow(hWnd, (sw - new_width) / 2, (sh - new_height) / 2, new_width, new_height, true);33933394HWND ok_button = GetDlgItem(hWnd, 1);3395MoveWindow(ok_button,3396dlg_size.cx + margin - scale_with_dpi(65, dpi),3397dlg_size.cy + str_size.cy + margin - scale_with_dpi(20, dpi),3398scale_with_dpi(65, dpi), scale_with_dpi(20, dpi), true);33993400HWND description = GetDlgItem(hWnd, 3);3401MoveWindow(description, margin, margin, dlg_size.cx, str_size.cy, true);3402SetWindowTextW(description, (LPCWSTR)init.description);34033404HWND text_edit = GetDlgItem(hWnd, 2);3405MoveWindow(text_edit, margin, str_size.cy + margin, dlg_size.cx, scale_with_dpi(20, dpi), true);3406SetWindowTextW(text_edit, (LPCWSTR)init.partial);34073408return TRUE;3409}34103411static INT_PTR input_text_dialog_cmd_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3412if (LOWORD(wParam) == 1) {3413HWND text_edit = GetDlgItem(hWnd, 2);3414ERR_FAIL_NULL_V(text_edit, false);34153416Char16String text;3417text.resize_uninitialized(GetWindowTextLengthW(text_edit) + 1);3418GetWindowTextW(text_edit, (LPWSTR)text.get_data(), text.size());34193420const Callable *callback = (const Callable *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);3421if (callback && callback->is_valid()) {3422Variant v_result = String((const wchar_t *)text.get_data());3423Variant ret;3424Callable::CallError ce;3425const Variant *args[1] = { &v_result };34263427callback->callp(args, 1, ret, ce);3428if (ce.error != Callable::CallError::CALL_OK) {3429ERR_PRINT(vformat("Failed to execute input dialog callback: %s.", Variant::get_callable_error_text(*callback, args, 1, ce)));3430}3431}34323433return EndDialog(hWnd, 0);3434}34353436return false;3437}34383439static INT_PTR CALLBACK input_text_dialog_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3440switch (code) {3441case WM_INITDIALOG:3442return input_text_dialog_init(hWnd, code, wParam, lParam);34433444case WM_COMMAND:3445return input_text_dialog_cmd_proc(hWnd, code, wParam, lParam);34463447default:3448return FALSE;3449}3450}34513452Error DisplayServerWindows::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {3453#pragma pack(push, 1)34543455// NOTE: Use default/placeholder coordinates here. Windows uses its own coordinate system3456// specifically for dialogs which relies on font sizes instead of pixels.3457const struct {3458WORD dlgVer; // must be 13459WORD signature; // must be 0xFFFF3460DWORD helpID;3461DWORD exStyle;3462DWORD style;3463WORD cDlgItems;3464short x;3465short y;3466short cx;3467short cy;3468WCHAR menu[1]; // must be 03469WCHAR windowClass[7]; // must be "#32770" -- the default window class for dialogs3470WCHAR title[1]; // must be 03471WORD pointsize;3472WORD weight;3473BYTE italic;3474BYTE charset;3475WCHAR font[13]; // must be "MS Shell Dlg"3476} template_base = {34771, 0xFFFF, 0, 0,3478DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION,34793, 0, 0, 20, 20, L"", L"#32770", L"", 8, FW_NORMAL, 0, DEFAULT_CHARSET, L"MS Shell Dlg"3480};34813482const struct {3483DWORD helpID;3484DWORD exStyle;3485DWORD style;3486short x;3487short y;3488short cx;3489short cy;3490DWORD id;3491WCHAR windowClass[7]; // must be "Button"3492WCHAR title[3]; // must be "OK"3493WORD extraCount;3494} ok_button = {34950, 0, WS_VISIBLE | BS_DEFPUSHBUTTON, 0, 0, 50, 14, 1, WC_BUTTONW, L"OK", 03496};3497const struct {3498DWORD helpID;3499DWORD exStyle;3500DWORD style;3501short x;3502short y;3503short cx;3504short cy;3505DWORD id;3506WCHAR windowClass[5]; // must be "Edit"3507WCHAR title[1]; // must be 03508WORD extraCount;3509} text_field = {35100, 0, WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 0, 0, 250, 14, 2, WC_EDITW, L"", 03511};3512const struct {3513DWORD helpID;3514DWORD exStyle;3515DWORD style;3516short x;3517short y;3518short cx;3519short cy;3520DWORD id;3521WCHAR windowClass[7]; // must be "Static"3522WCHAR title[1]; // must be 03523WORD extraCount;3524} static_text = {35250, 0, WS_VISIBLE, 0, 0, 250, 14, 3, WC_STATICW, L"", 03526};35273528#pragma pack(pop)35293530// Dialog template3531const size_t data_size = sizeof(template_base) + (sizeof(template_base) % 4) +3532sizeof(ok_button) + (sizeof(ok_button) % 4) +3533sizeof(text_field) + (sizeof(text_field) % 4) +3534sizeof(static_text) + (sizeof(static_text) % 4);35353536void *data_template = memalloc(data_size);3537ERR_FAIL_NULL_V_MSG(data_template, FAILED, "Unable to allocate memory for the dialog template.");3538ZeroMemory(data_template, data_size);35393540char *current_block = (char *)data_template;3541CopyMemory(current_block, &template_base, sizeof(template_base));3542current_block += sizeof(template_base) + (sizeof(template_base) % 4);3543CopyMemory(current_block, &ok_button, sizeof(ok_button));3544current_block += sizeof(ok_button) + (sizeof(ok_button) % 4);3545CopyMemory(current_block, &text_field, sizeof(text_field));3546current_block += sizeof(text_field) + (sizeof(text_field) % 4);3547CopyMemory(current_block, &static_text, sizeof(static_text));35483549Char16String title16 = p_title.utf16();3550Char16String description16 = p_description.utf16();3551Char16String partial16 = p_partial.utf16();35523553Win32InputTextDialogInit init = {3554title16.get_data(), description16.get_data(), partial16.get_data(), p_callback3555};35563557// No modal dialogs for specific windows? Assume main window here.3558INT_PTR ret = DialogBoxIndirectParamW(hInstance, (LPDLGTEMPLATEW)data_template, nullptr, (DLGPROC)input_text_dialog_proc, (LPARAM)(&init));35593560Error result = ret != -1 ? OK : FAILED;3561memfree(data_template);35623563if (result == FAILED) {3564ERR_PRINT("Unable to create native dialog.");3565}3566return result;3567}35683569int DisplayServerWindows::keyboard_get_layout_count() const {3570return GetKeyboardLayoutList(0, nullptr);3571}35723573int DisplayServerWindows::keyboard_get_current_layout() const {3574HKL cur_layout = GetKeyboardLayout(0);35753576int layout_count = GetKeyboardLayoutList(0, nullptr);3577HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3578GetKeyboardLayoutList(layout_count, layouts);35793580for (int i = 0; i < layout_count; i++) {3581if (cur_layout == layouts[i]) {3582memfree(layouts);3583return i;3584}3585}3586memfree(layouts);3587return -1;3588}35893590void DisplayServerWindows::keyboard_set_current_layout(int p_index) {3591int layout_count = GetKeyboardLayoutList(0, nullptr);35923593ERR_FAIL_INDEX(p_index, layout_count);35943595HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3596GetKeyboardLayoutList(layout_count, layouts);3597ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS);3598memfree(layouts);3599}36003601String DisplayServerWindows::keyboard_get_layout_language(int p_index) const {3602int layout_count = GetKeyboardLayoutList(0, nullptr);36033604ERR_FAIL_INDEX_V(p_index, layout_count, "");36053606HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3607GetKeyboardLayoutList(layout_count, layouts);36083609WCHAR buf[LOCALE_NAME_MAX_LENGTH];3610memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));3611LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);36123613memfree(layouts);36143615return String::utf16((const char16_t *)buf).substr(0, 2);3616}36173618Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) const {3619Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;3620Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);36213622if (keycode_no_mod == Key::PRINT ||3623keycode_no_mod == Key::KP_ADD ||3624keycode_no_mod == Key::KP_5 ||3625(keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {3626return p_keycode;3627}36283629unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);3630if (scancode == 0) {3631return p_keycode;3632}36333634HKL current_layout = GetKeyboardLayout(0);3635UINT vk = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK, current_layout);3636if (vk == 0) {3637return p_keycode;3638}36393640UINT char_code = MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, current_layout) & 0x7FFF;3641// Unlike a similar Linux/BSD check which matches full Latin-1 range,3642// we limit these to ASCII to fix some layouts, including Arabic ones3643if (char_code >= 32 && char_code <= 127) {3644// Godot uses 'braces' instead of 'brackets'3645if (char_code == (unsigned int)Key::BRACKETLEFT || char_code == (unsigned int)Key::BRACKETRIGHT) {3646char_code += 32;3647}3648return (Key)(char_code | (unsigned int)modifiers);3649}36503651return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers);3652}36533654Key DisplayServerWindows::keyboard_get_label_from_physical(Key p_keycode) const {3655Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;3656Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);36573658if (keycode_no_mod == Key::PRINT ||3659keycode_no_mod == Key::KP_ADD ||3660keycode_no_mod == Key::KP_5 ||3661(keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {3662return p_keycode;3663}36643665unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);3666if (scancode == 0) {3667return p_keycode;3668}36693670Key keycode = KeyMappingWindows::get_keysym(MapVirtualKey(scancode, MAPVK_VSC_TO_VK));36713672HKL current_layout = GetKeyboardLayout(0);3673static BYTE keyboard_state[256];3674memset(keyboard_state, 0, 256);3675wchar_t chars[256] = {};3676UINT extended_code = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);3677if (ToUnicodeEx(extended_code, scancode, keyboard_state, chars, 255, 4, current_layout) > 0) {3678String keysym = String::utf16((char16_t *)chars, 255);3679if (!keysym.is_empty()) {3680return fix_key_label(keysym[0], keycode) | modifiers;3681}3682}3683return p_keycode;3684}36853686void DisplayServerWindows::show_emoji_and_symbol_picker() const {3687// Send Win + Period shortcut, there's no non-WinRT public API.36883689INPUT input[4] = {};3690input[0].type = INPUT_KEYBOARD; // Win down.3691input[0].ki.wVk = VK_LWIN;36923693input[1].type = INPUT_KEYBOARD; // Period down.3694input[1].ki.wVk = VK_OEM_PERIOD;36953696input[2].type = INPUT_KEYBOARD; // Win up.3697input[2].ki.wVk = VK_LWIN;3698input[2].ki.dwFlags = KEYEVENTF_KEYUP;36993700input[3].type = INPUT_KEYBOARD; // Period up.3701input[3].ki.wVk = VK_OEM_PERIOD;3702input[3].ki.dwFlags = KEYEVENTF_KEYUP;37033704SendInput(4, input, sizeof(INPUT));3705}37063707String DisplayServerWindows::_get_keyboard_layout_display_name(const String &p_klid) const {3708String ret;3709HKEY key;3710if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {3711return String();3712}37133714WCHAR buffer[MAX_PATH] = {};3715DWORD buffer_size = MAX_PATH;3716if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Display Name", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {3717if (SHLoadIndirectString(buffer, buffer, buffer_size, nullptr) == S_OK) {3718ret = String::utf16((const char16_t *)buffer, buffer_size);3719}3720} else {3721if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Text", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {3722ret = String::utf16((const char16_t *)buffer, buffer_size);3723}3724}37253726RegCloseKey(key);3727return ret;3728}37293730String DisplayServerWindows::_get_klid(HKL p_hkl) const {3731String ret;37323733WORD device = HIWORD(p_hkl);3734if ((device & 0xf000) == 0xf000) {3735WORD layout_id = device & 0x0fff;37363737HKEY key;3738if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {3739return String();3740}37413742DWORD index = 0;3743wchar_t klid_buffer[KL_NAMELENGTH];3744DWORD klid_buffer_size = KL_NAMELENGTH;3745while (RegEnumKeyExW(key, index, klid_buffer, &klid_buffer_size, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) {3746wchar_t layout_id_buf[MAX_PATH] = {};3747DWORD layout_id_size = MAX_PATH;3748if (RegGetValueW(key, klid_buffer, L"Layout Id", RRF_RT_REG_SZ, nullptr, layout_id_buf, &layout_id_size) == ERROR_SUCCESS) {3749if (layout_id == String::utf16((char16_t *)layout_id_buf, layout_id_size).hex_to_int()) {3750ret = String::utf16((const char16_t *)klid_buffer, klid_buffer_size).lpad(8, "0");3751break;3752}3753}3754klid_buffer_size = KL_NAMELENGTH;3755++index;3756}37573758RegCloseKey(key);3759} else {3760if (device == 0) {3761device = LOWORD(p_hkl);3762}3763ret = (String::num_uint64((uint64_t)device, 16, false)).lpad(8, "0");3764}37653766return ret;3767}37683769String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {3770int layout_count = GetKeyboardLayoutList(0, nullptr);37713772ERR_FAIL_INDEX_V(p_index, layout_count, "");37733774HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3775GetKeyboardLayoutList(layout_count, layouts);37763777String 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).3778if (ret.is_empty()) {3779WCHAR buf[LOCALE_NAME_MAX_LENGTH];3780memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));3781LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);37823783WCHAR name[1024];3784memset(name, 0, 1024 * sizeof(WCHAR));3785GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024);37863787ret = String::utf16((const char16_t *)name);3788}3789memfree(layouts);37903791return ret;3792}37933794void DisplayServerWindows::process_events() {3795ERR_FAIL_COND(!Thread::is_main_thread());37963797if (!drop_events) {3798#ifdef SDL_ENABLED3799if (joypad_sdl) {3800joypad_sdl->process_events();3801}3802#endif3803}38043805_THREAD_SAFE_LOCK_3806MSG msg = {};3807while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {3808TranslateMessage(&msg);3809DispatchMessageW(&msg);3810}3811_THREAD_SAFE_UNLOCK_38123813if (tts) {3814tts->process_events();3815}38163817if (!drop_events) {3818_process_key_events();3819Input::get_singleton()->flush_buffered_events();3820}38213822LocalVector<List<FileDialogData *>::Element *> to_remove;3823for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {3824FileDialogData *fd = E->get();3825if (fd->finished.is_set()) {3826if (fd->listener_thread.is_started()) {3827fd->listener_thread.wait_to_finish();3828}3829to_remove.push_back(E);3830}3831}3832for (List<FileDialogData *>::Element *E : to_remove) {3833memdelete(E->get());3834E->erase();3835}3836process_file_dialog_callbacks();3837}38383839void DisplayServerWindows::force_process_and_drop_events() {3840ERR_FAIL_COND(!Thread::is_main_thread());38413842drop_events = true;3843process_events();3844drop_events = false;3845}38463847void DisplayServerWindows::release_rendering_thread() {3848#if defined(GLES3_ENABLED)3849if (gl_manager_angle) {3850gl_manager_angle->release_current();3851}3852if (gl_manager_native) {3853gl_manager_native->release_current();3854}3855#endif3856}38573858void DisplayServerWindows::swap_buffers() {3859#if defined(GLES3_ENABLED)3860if (gl_manager_angle) {3861gl_manager_angle->swap_buffers();3862}3863if (gl_manager_native) {3864gl_manager_native->swap_buffers();3865}3866#endif3867}38683869void DisplayServerWindows::set_native_icon(const String &p_filename) {3870_THREAD_SAFE_METHOD_38713872Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);3873ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'.");38743875ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));3876int pos = 0;38773878icon_dir->idReserved = f->get_32();3879pos += sizeof(WORD);3880f->seek(pos);38813882icon_dir->idType = f->get_32();3883pos += sizeof(WORD);3884f->seek(pos);38853886ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!");38873888icon_dir->idCount = f->get_32();3889pos += sizeof(WORD);3890f->seek(pos);38913892icon_dir = (ICONDIR *)memrealloc(icon_dir, sizeof(ICONDIR) - sizeof(ICONDIRENTRY) + icon_dir->idCount * sizeof(ICONDIRENTRY));3893f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY));38943895int small_icon_index = -1; // Select 16x16 with largest color count.3896int small_icon_cc = 0;3897int big_icon_index = -1; // Select largest.3898int big_icon_width = 16;3899int big_icon_cc = 0;39003901for (int i = 0; i < icon_dir->idCount; i++) {3902int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount;3903int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth;3904if (width == 16) {3905if (colors >= small_icon_cc) {3906small_icon_index = i;3907small_icon_cc = colors;3908}3909}3910if (width >= big_icon_width) {3911if (colors >= big_icon_cc) {3912big_icon_index = i;3913big_icon_width = width;3914big_icon_cc = colors;3915}3916}3917}39183919ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!");39203921if (small_icon_index == -1) {3922WARN_PRINT("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!");3923small_icon_index = big_icon_index;3924small_icon_cc = big_icon_cc;3925}39263927// Read the big icon.3928DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes;3929Vector<uint8_t> data_big;3930data_big.resize(bytecount_big);3931pos = icon_dir->idEntries[big_icon_index].dwImageOffset;3932f->seek(pos);3933f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big);3934HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000);3935ERR_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()) + ".");39363937// Read the small icon.3938DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;3939Vector<uint8_t> data_small;3940data_small.resize(bytecount_small);3941pos = icon_dir->idEntries[small_icon_index].dwImageOffset;3942f->seek(pos);3943f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small);3944HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000);3945ERR_FAIL_NULL_MSG(icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");39463947// Online tradition says to be sure last error is cleared and set the small icon first.3948int err = 0;3949SetLastError(err);39503951SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);3952err = GetLastError();3953ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + ".");39543955SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);3956err = GetLastError();3957ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");39583959memdelete(icon_dir);3960}39613962void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {3963_THREAD_SAFE_METHOD_39643965if (p_icon.is_valid()) {3966ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);39673968Ref<Image> img = p_icon;3969if (img != icon) {3970img = img->duplicate();3971img->convert(Image::FORMAT_RGBA8);3972}39733974int w = img->get_width();3975int h = img->get_height();39763977// Create temporary bitmap buffer.3978int icon_len = 40 + h * w * 4;3979Vector<BYTE> v;3980v.resize(icon_len);3981BYTE *icon_bmp = v.ptrw();39823983encode_uint32(40, &icon_bmp[0]);3984encode_uint32(w, &icon_bmp[4]);3985encode_uint32(h * 2, &icon_bmp[8]);3986encode_uint16(1, &icon_bmp[12]);3987encode_uint16(32, &icon_bmp[14]);3988encode_uint32(BI_RGB, &icon_bmp[16]);3989encode_uint32(w * h * 4, &icon_bmp[20]);3990encode_uint32(0, &icon_bmp[24]);3991encode_uint32(0, &icon_bmp[28]);3992encode_uint32(0, &icon_bmp[32]);3993encode_uint32(0, &icon_bmp[36]);39943995uint8_t *wr = &icon_bmp[40];3996const uint8_t *r = img->get_data().ptr();39973998for (int i = 0; i < h; i++) {3999for (int j = 0; j < w; j++) {4000const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4001uint8_t *wpx = &wr[(i * w + j) * 4];4002wpx[0] = rpx[2];4003wpx[1] = rpx[1];4004wpx[2] = rpx[0];4005wpx[3] = rpx[3];4006}4007}40084009HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);4010ERR_FAIL_NULL(hicon);40114012icon = img;40134014// Set the icon for the window.4015SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);40164017// Set the icon in the task manager (should we do this?).4018SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);4019} else {4020icon = Ref<Image>();4021SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, 0);4022SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, 0);4023}4024}40254026DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) {4027HICON hicon = nullptr;4028if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {4029Ref<Image> img = p_icon->get_image();4030img = img->duplicate();4031if (img->is_compressed()) {4032img->decompress();4033}4034img->convert(Image::FORMAT_RGBA8);40354036int w = img->get_width();4037int h = img->get_height();40384039// Create temporary bitmap buffer.4040int icon_len = 40 + h * w * 4;4041Vector<BYTE> v;4042v.resize(icon_len);4043BYTE *icon_bmp = v.ptrw();40444045encode_uint32(40, &icon_bmp[0]);4046encode_uint32(w, &icon_bmp[4]);4047encode_uint32(h * 2, &icon_bmp[8]);4048encode_uint16(1, &icon_bmp[12]);4049encode_uint16(32, &icon_bmp[14]);4050encode_uint32(BI_RGB, &icon_bmp[16]);4051encode_uint32(w * h * 4, &icon_bmp[20]);4052encode_uint32(0, &icon_bmp[24]);4053encode_uint32(0, &icon_bmp[28]);4054encode_uint32(0, &icon_bmp[32]);4055encode_uint32(0, &icon_bmp[36]);40564057uint8_t *wr = &icon_bmp[40];4058const uint8_t *r = img->get_data().ptr();40594060for (int i = 0; i < h; i++) {4061for (int j = 0; j < w; j++) {4062const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4063uint8_t *wpx = &wr[(i * w + j) * 4];4064wpx[0] = rpx[2];4065wpx[1] = rpx[1];4066wpx[2] = rpx[0];4067wpx[3] = rpx[3];4068}4069}40704071hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);4072}40734074IndicatorData idat;4075idat.callback = p_callback;40764077NOTIFYICONDATAW ndat;4078ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4079ndat.cbSize = sizeof(NOTIFYICONDATAW);4080ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4081ndat.uID = indicator_id_counter;4082ndat.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;4083ndat.uCallbackMessage = WM_INDICATOR_CALLBACK_MESSAGE;4084ndat.hIcon = hicon;4085memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));4086ndat.uVersion = NOTIFYICON_VERSION;40874088Shell_NotifyIconW(NIM_ADD, &ndat);4089Shell_NotifyIconW(NIM_SETVERSION, &ndat);40904091IndicatorID iid = indicator_id_counter++;4092indicators[iid] = idat;40934094return iid;4095}40964097void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon) {4098ERR_FAIL_COND(!indicators.has(p_id));40994100HICON hicon = nullptr;4101if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {4102Ref<Image> img = p_icon->get_image();4103img = img->duplicate();4104if (img->is_compressed()) {4105img->decompress();4106}4107img->convert(Image::FORMAT_RGBA8);41084109int w = img->get_width();4110int h = img->get_height();41114112// Create temporary bitmap buffer.4113int icon_len = 40 + h * w * 4;4114Vector<BYTE> v;4115v.resize(icon_len);4116BYTE *icon_bmp = v.ptrw();41174118encode_uint32(40, &icon_bmp[0]);4119encode_uint32(w, &icon_bmp[4]);4120encode_uint32(h * 2, &icon_bmp[8]);4121encode_uint16(1, &icon_bmp[12]);4122encode_uint16(32, &icon_bmp[14]);4123encode_uint32(BI_RGB, &icon_bmp[16]);4124encode_uint32(w * h * 4, &icon_bmp[20]);4125encode_uint32(0, &icon_bmp[24]);4126encode_uint32(0, &icon_bmp[28]);4127encode_uint32(0, &icon_bmp[32]);4128encode_uint32(0, &icon_bmp[36]);41294130uint8_t *wr = &icon_bmp[40];4131const uint8_t *r = img->get_data().ptr();41324133for (int i = 0; i < h; i++) {4134for (int j = 0; j < w; j++) {4135const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4136uint8_t *wpx = &wr[(i * w + j) * 4];4137wpx[0] = rpx[2];4138wpx[1] = rpx[1];4139wpx[2] = rpx[0];4140wpx[3] = rpx[3];4141}4142}41434144hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);4145}41464147NOTIFYICONDATAW ndat;4148ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4149ndat.cbSize = sizeof(NOTIFYICONDATAW);4150ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4151ndat.uID = p_id;4152ndat.uFlags = NIF_ICON;4153ndat.hIcon = hicon;4154ndat.uVersion = NOTIFYICON_VERSION;41554156Shell_NotifyIconW(NIM_MODIFY, &ndat);4157}41584159void DisplayServerWindows::status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) {4160ERR_FAIL_COND(!indicators.has(p_id));41614162NOTIFYICONDATAW ndat;4163ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4164ndat.cbSize = sizeof(NOTIFYICONDATAW);4165ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4166ndat.uID = p_id;4167ndat.uFlags = NIF_TIP;4168memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));4169ndat.uVersion = NOTIFYICON_VERSION;41704171Shell_NotifyIconW(NIM_MODIFY, &ndat);4172}41734174void DisplayServerWindows::status_indicator_set_menu(IndicatorID p_id, const RID &p_menu_rid) {4175ERR_FAIL_COND(!indicators.has(p_id));41764177indicators[p_id].menu_rid = p_menu_rid;4178}41794180void DisplayServerWindows::status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) {4181ERR_FAIL_COND(!indicators.has(p_id));41824183indicators[p_id].callback = p_callback;4184}41854186Rect2 DisplayServerWindows::status_indicator_get_rect(IndicatorID p_id) const {4187ERR_FAIL_COND_V(!indicators.has(p_id), Rect2());41884189NOTIFYICONIDENTIFIER nid;4190ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));4191nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);4192nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;4193nid.uID = p_id;4194nid.guidItem = GUID_NULL;41954196RECT rect;4197if (Shell_NotifyIconGetRect(&nid, &rect) != S_OK) {4198return Rect2();4199}4200Rect2 ind_rect = Rect2(Point2(rect.left, rect.top) - _get_screens_origin(), Size2(rect.right - rect.left, rect.bottom - rect.top));4201for (int i = 0; i < get_screen_count(); i++) {4202Rect2 screen_rect = Rect2(screen_get_position(i), screen_get_size(i));4203if (screen_rect.encloses(ind_rect)) {4204return ind_rect;4205}4206}4207return Rect2();4208}42094210void DisplayServerWindows::delete_status_indicator(IndicatorID p_id) {4211ERR_FAIL_COND(!indicators.has(p_id));42124213NOTIFYICONDATAW ndat;4214ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4215ndat.cbSize = sizeof(NOTIFYICONDATAW);4216ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4217ndat.uID = p_id;4218ndat.uVersion = NOTIFYICON_VERSION;42194220Shell_NotifyIconW(NIM_DELETE, &ndat);4221indicators.erase(p_id);4222}42234224void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {4225_THREAD_SAFE_METHOD_4226#if defined(RD_ENABLED)4227if (rendering_context) {4228rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);4229}4230#endif42314232#if defined(GLES3_ENABLED)4233if (gl_manager_native) {4234gl_manager_native->set_use_vsync(p_window, p_vsync_mode != DisplayServer::VSYNC_DISABLED);4235}4236if (gl_manager_angle) {4237gl_manager_angle->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);4238}4239#endif4240}42414242DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {4243_THREAD_SAFE_METHOD_4244#if defined(RD_ENABLED)4245if (rendering_context) {4246return rendering_context->window_get_vsync_mode(p_window);4247}4248#endif42494250#if defined(GLES3_ENABLED)4251if (gl_manager_native) {4252return gl_manager_native->is_using_vsync(p_window) ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;4253}4254if (gl_manager_angle) {4255return gl_manager_angle->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;4256}4257#endif4258return DisplayServer::VSYNC_ENABLED;4259}42604261void DisplayServerWindows::window_start_drag(WindowID p_window) {4262_THREAD_SAFE_METHOD_42634264ERR_FAIL_COND(!windows.has(p_window));4265WindowData &wd = windows[p_window];42664267if (wd.parent_hwnd) {4268return; // Embedded window.4269}42704271ReleaseCapture();42724273POINT coords;4274GetCursorPos(&coords);4275ScreenToClient(wd.hWnd, &coords);42764277SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(coords.x, coords.y));42784279for (int btn = (int)MouseButton::LEFT; btn <= (int)MouseButton::MB_XBUTTON2; btn++) {4280if (Input::get_singleton()->is_mouse_button_pressed(MouseButton(btn))) {4281Ref<InputEventMouseButton> mb;4282mb.instantiate();4283mb->set_window_id(p_window);4284mb->set_pressed(false);4285mb->set_button_index(MouseButton::LEFT);4286mb->set_position(Vector2(coords.x, coords.y));4287mb->set_global_position(mb->get_position());4288Input::get_singleton()->parse_input_event(mb);4289}4290}4291}42924293void DisplayServerWindows::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {4294_THREAD_SAFE_METHOD_42954296ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);4297ERR_FAIL_COND(!windows.has(p_window));4298WindowData &wd = windows[p_window];42994300if (wd.parent_hwnd) {4301return; // Embedded window.4302}43034304ReleaseCapture();43054306POINT coords;4307GetCursorPos(&coords);4308ScreenToClient(wd.hWnd, &coords);43094310DWORD op = 0;4311switch (p_edge) {4312case DisplayServer::WINDOW_EDGE_TOP_LEFT: {4313op = WMSZ_TOPLEFT;4314} break;4315case DisplayServer::WINDOW_EDGE_TOP: {4316op = WMSZ_TOP;4317} break;4318case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {4319op = WMSZ_TOPRIGHT;4320} break;4321case DisplayServer::WINDOW_EDGE_LEFT: {4322op = WMSZ_LEFT;4323} break;4324case DisplayServer::WINDOW_EDGE_RIGHT: {4325op = WMSZ_RIGHT;4326} break;4327case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {4328op = WMSZ_BOTTOMLEFT;4329} break;4330case DisplayServer::WINDOW_EDGE_BOTTOM: {4331op = WMSZ_BOTTOM;4332} break;4333case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {4334op = WMSZ_BOTTOMRIGHT;4335} break;4336default:4337break;4338}43394340SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_SIZE | op, MAKELPARAM(coords.x, coords.y));43414342for (int btn = (int)MouseButton::LEFT; btn <= (int)MouseButton::MB_XBUTTON2; btn++) {4343if (Input::get_singleton()->is_mouse_button_pressed(MouseButton(btn))) {4344Ref<InputEventMouseButton> mb;4345mb.instantiate();4346mb->set_window_id(p_window);4347mb->set_pressed(false);4348mb->set_button_index(MouseButton::LEFT);4349mb->set_position(Vector2(coords.x, coords.y));4350mb->set_global_position(mb->get_position());4351Input::get_singleton()->parse_input_event(mb);4352}4353}4354}43554356void DisplayServerWindows::set_context(Context p_context) {4357}43584359bool DisplayServerWindows::is_window_transparency_available() const {4360#if defined(RD_ENABLED)4361if (rendering_device && !rendering_device->is_composite_alpha_supported()) {4362return false;4363}4364#endif4365return OS::get_singleton()->is_layered_allowed();4366}43674368#define MI_WP_SIGNATURE 0xFF5157004369#define SIGNATURE_MASK 0xFFFFFF004370// Keeping the name suggested by Microsoft, but this macro really answers:4371// Is this mouse event emulated from touch or pen input?4372#define IsPenEvent(dw) (((dw) & SIGNATURE_MASK) == MI_WP_SIGNATURE)4373// This one tells whether the event comes from touchscreen (and not from pen).4374#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw) & 0x80))43754376void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx) {4377if (touch_state.has(idx) == p_pressed) {4378return;4379}43804381if (p_pressed) {4382touch_state.insert(idx, Vector2(p_x, p_y));4383} else {4384touch_state.erase(idx);4385}43864387Ref<InputEventScreenTouch> event;4388event.instantiate();4389event->set_index(idx);4390event->set_window_id(p_window);4391event->set_pressed(p_pressed);4392event->set_position(Vector2(p_x, p_y));43934394Input::get_singleton()->parse_input_event(event);4395}43964397void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) {4398RBMap<int, Vector2>::Element *curr = touch_state.find(idx);4399if (!curr) {4400return;4401}44024403if (curr->get() == Vector2(p_x, p_y)) {4404return;4405}44064407Ref<InputEventScreenDrag> event;4408event.instantiate();4409event->set_window_id(p_window);4410event->set_index(idx);4411event->set_position(Vector2(p_x, p_y));4412event->set_relative(Vector2(p_x, p_y) - curr->get());4413event->set_relative_screen_position(event->get_relative());44144415Input::get_singleton()->parse_input_event(event);44164417curr->get() = Vector2(p_x, p_y);4418}44194420void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent p_event) {4421if (wd.event_callback.is_valid()) {4422Variant event = int(p_event);4423wd.event_callback.call(event);4424}4425}44264427void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {4428static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event);4429}44304431void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {4432if (in_dispatch_input_event) {4433return;4434}4435in_dispatch_input_event = true;44364437{4438List<WindowID>::Element *E = popup_list.back();4439if (E && Object::cast_to<InputEventKey>(*p_event)) {4440// Redirect keyboard input to active popup.4441if (windows.has(E->get())) {4442Callable callable = windows[E->get()].input_event_callback;4443if (callable.is_valid()) {4444callable.call(p_event);4445}4446}4447in_dispatch_input_event = false;4448return;4449}4450}44514452Ref<InputEventFromWindow> event_from_window = p_event;4453if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {4454// Send to a single window.4455if (windows.has(event_from_window->get_window_id())) {4456Callable callable = windows[event_from_window->get_window_id()].input_event_callback;4457if (callable.is_valid()) {4458callable.call(p_event);4459}4460}4461} else {4462// Send to all windows. Copy all pending callbacks, since callback can erase window.4463Vector<Callable> cbs;4464for (KeyValue<WindowID, WindowData> &E : windows) {4465Callable callable = E.value.input_event_callback;4466if (callable.is_valid()) {4467cbs.push_back(callable);4468}4469}4470for (const Callable &cb : cbs) {4471cb.call(p_event);4472}4473}44744475in_dispatch_input_event = false;4476}44774478LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {4479DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());4480if (ds_win) {4481return ds_win->MouseProc(code, wParam, lParam);4482} else {4483return ::CallNextHookEx(nullptr, code, wParam, lParam);4484}4485}44864487DisplayServer::WindowID DisplayServerWindows::window_get_active_popup() const {4488const List<WindowID>::Element *E = popup_list.back();4489if (E) {4490return E->get();4491} else {4492return INVALID_WINDOW_ID;4493}4494}44954496void DisplayServerWindows::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {4497_THREAD_SAFE_METHOD_44984499ERR_FAIL_COND(!windows.has(p_window));4500WindowData &wd = windows[p_window];4501wd.parent_safe_rect = p_rect;4502}45034504Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const {4505_THREAD_SAFE_METHOD_45064507ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());4508const WindowData &wd = windows[p_window];4509return wd.parent_safe_rect;4510}45114512void DisplayServerWindows::popup_open(WindowID p_window) {4513_THREAD_SAFE_METHOD_45144515bool has_popup_ancestor = false;4516WindowID transient_root = p_window;4517while (true) {4518WindowID parent = windows[transient_root].transient_parent;4519if (parent == INVALID_WINDOW_ID) {4520break;4521} else {4522transient_root = parent;4523if (windows[parent].is_popup) {4524has_popup_ancestor = true;4525break;4526}4527}4528}45294530// Detect tooltips and other similar popups that shouldn't block input to their parent.4531bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);45324533WindowData &wd = windows[p_window];4534if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {4535// Find current popup parent, or root popup if new window is not transient.4536List<WindowID>::Element *C = nullptr;4537List<WindowID>::Element *E = popup_list.back();4538while (E) {4539if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {4540C = E;4541E = E->prev();4542} else {4543break;4544}4545}4546if (C) {4547_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4548}45494550time_since_popup = OS::get_singleton()->get_ticks_msec();4551popup_list.push_back(p_window);4552}4553}45544555void DisplayServerWindows::popup_close(WindowID p_window) {4556_THREAD_SAFE_METHOD_45574558List<WindowID>::Element *E = popup_list.find(p_window);4559while (E) {4560List<WindowID>::Element *F = E->next();4561WindowID win_id = E->get();4562popup_list.erase(E);45634564if (win_id != p_window) {4565// Only request close on related windows, not this window. We are already processing it.4566_send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4567}4568E = F;4569}4570}45714572BitField<DisplayServerWindows::WinKeyModifierMask> DisplayServerWindows::_get_mods() const {4573BitField<WinKeyModifierMask> mask = {};4574static unsigned char keyboard_state[256];4575if (GetKeyboardState((PBYTE)&keyboard_state)) {4576if ((keyboard_state[VK_LSHIFT] & 0x80) || (keyboard_state[VK_RSHIFT] & 0x80)) {4577mask.set_flag(WinKeyModifierMask::SHIFT);4578}4579if ((keyboard_state[VK_LCONTROL] & 0x80) || (keyboard_state[VK_RCONTROL] & 0x80)) {4580mask.set_flag(WinKeyModifierMask::CTRL);4581}4582if ((keyboard_state[VK_LMENU] & 0x80) || (keyboard_state[VK_RMENU] & 0x80)) {4583mask.set_flag(WinKeyModifierMask::ALT);4584}4585if ((keyboard_state[VK_RMENU] & 0x80)) {4586mask.set_flag(WinKeyModifierMask::ALT_GR);4587}4588if ((keyboard_state[VK_LWIN] & 0x80) || (keyboard_state[VK_RWIN] & 0x80)) {4589mask.set_flag(WinKeyModifierMask::META);4590}4591}45924593return mask;4594}45954596LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) {4597_THREAD_SAFE_METHOD_45984599uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;4600if (delta > 250) {4601switch (wParam) {4602case WM_NCLBUTTONDOWN:4603case WM_NCRBUTTONDOWN:4604case WM_NCMBUTTONDOWN:4605case WM_LBUTTONDOWN:4606case WM_RBUTTONDOWN:4607case WM_MBUTTONDOWN: {4608MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam;4609Point2i pos = Point2i(ms->pt.x, ms->pt.y) - _get_screens_origin();4610List<WindowID>::Element *C = nullptr;4611List<WindowID>::Element *E = popup_list.back();4612// Find top popup to close.4613while (E) {4614// Popup window area.4615Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));4616// Area of the parent window, which responsible for opening sub-menu.4617Rect2i safe_rect = window_get_popup_safe_rect(E->get());4618if (win_rect.has_point(pos)) {4619break;4620} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {4621break;4622} else {4623C = E;4624E = E->prev();4625}4626}4627if (C) {4628_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4629return 1;4630}4631} break;4632}4633}4634return ::CallNextHookEx(mouse_monitor, code, wParam, lParam);4635}46364637// Handle a single window message received while CreateWindowEx is still on the stack and our data4638// structures are not fully initialized.4639LRESULT DisplayServerWindows::_handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {4640switch (uMsg) {4641case WM_GETMINMAXINFO: {4642// We receive this during CreateWindowEx and we haven't initialized the window4643// struct, so let Windows figure out the maximized size.4644// Silently forward to user/default.4645} break;4646case WM_NCCREATE: {4647// We tunnel an unowned pointer to our window context (WindowData) through the4648// first possible message (WM_NCCREATE) to fix up our window context collection.4649CREATESTRUCTW *pCreate = (CREATESTRUCTW *)lParam;4650WindowData *pWindowData = reinterpret_cast<WindowData *>(pCreate->lpCreateParams);46514652// Fix this up so we can recognize the remaining messages.4653pWindowData->hWnd = hWnd;46544655#ifdef ACCESSKIT_ENABLED4656if (accessibility_driver && !accessibility_driver->window_create(pWindowData->id, (void *)hWnd)) {4657if (OS::get_singleton()->is_stdout_verbose()) {4658ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");4659}4660memdelete(accessibility_driver);4661accessibility_driver = nullptr;4662}4663#endif4664} break;4665default: {4666// Additional messages during window creation should happen after we fixed4667// up the data structures on WM_NCCREATE, but this might change in the future,4668// so report an error here and then we can implement them.4669ERR_PRINT_ONCE(vformat("Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error.", uMsg));4670} break;4671}46724673if (user_proc) {4674return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);4675}4676return DefWindowProcW(hWnd, uMsg, wParam, lParam);4677}46784679// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events.4680// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures4681LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {4682if (drop_events) {4683if (user_proc) {4684return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);4685} else {4686return DefWindowProcW(hWnd, uMsg, wParam, lParam);4687}4688}46894690WindowID window_id = INVALID_WINDOW_ID;4691bool window_created = false;46924693// Check whether window exists4694// FIXME this is O(n), where n is the set of currently open windows and subwindows4695// we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below4696for (const KeyValue<WindowID, WindowData> &E : windows) {4697if (E.value.hWnd == hWnd) {4698window_id = E.key;4699window_created = true;4700break;4701}4702}47034704// WARNING: We get called with events before the window is registered in our collection4705// specifically, even the call to CreateWindowEx already calls here while still on the stack,4706// so there is no way to store the window handle in our collection before we get here.4707if (!window_created) {4708// don't let code below operate on incompletely initialized window objects or missing window_id4709return _handle_early_window_message(hWnd, uMsg, wParam, lParam);4710}47114712// Process window messages.4713switch (uMsg) {4714case WM_GETOBJECT: {4715get_object_received = true;4716} break;4717case WM_MENUCOMMAND: {4718native_menu->_menu_activate(HMENU(lParam), (int)wParam);4719} break;4720case WM_CREATE: {4721{4722DWORD value = windows[window_id].sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;4723::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));4724}4725if (is_dark_mode_supported() && dark_title_available) {4726BOOL value = is_dark_mode();47274728::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));4729SendMessageW(windows[window_id].hWnd, WM_PAINT, 0, 0);4730}4731} break;4732case WM_NCHITTEST: {4733if (windows[window_id].mpass) {4734return HTTRANSPARENT;4735}4736} break;4737case WM_MOUSEACTIVATE: {4738if (windows[window_id].no_focus || windows[window_id].is_popup) {4739return MA_NOACTIVATE; // Do not activate, but process mouse messages.4740}4741// When embedded, the window is a child of the parent and is not activated4742// by default because it lacks native controls.4743if (windows[window_id].parent_hwnd) {4744SetFocus(windows[window_id].hWnd);4745return MA_ACTIVATE;4746}4747} break;4748case WM_ACTIVATEAPP: {4749bool new_app_focused = (bool)wParam;4750if (new_app_focused == app_focused) {4751break;4752}4753app_focused = new_app_focused;4754if (OS::get_singleton()->get_main_loop()) {4755OS::get_singleton()->get_main_loop()->notification(app_focused ? MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN : MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);4756}4757} break;4758case WM_ACTIVATE: {4759// Activation can happen just after the window has been created, even before the callbacks are set.4760// Therefore, it's safer to defer the delivery of the event.4761// It's important to set an nIDEvent different from the SetTimer for move_timer_id because4762// if the same nIDEvent is passed, the timer is replaced and the same timer_id is returned.4763// The problem with the timer is that the window cannot be resized or the buttons cannot be used correctly4764// if the window is not activated first. This happens because the code in the activation process runs4765// after the mouse click is handled. To address this, the timer is now used only when the window is created.4766windows[window_id].activate_state = GET_WM_ACTIVATE_STATE(wParam, lParam);4767if (windows[window_id].first_activation_done) {4768_process_activate_event(window_id);4769} else {4770windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_WINDOW_ACTIVATION, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);4771}4772return 0;4773} break;4774case WM_GETMINMAXINFO: {4775if (windows[window_id].resizable && !windows[window_id].fullscreen) {4776// Size of window decorations.4777Size2 decor = window_get_size_with_decorations(window_id) - window_get_size(window_id);47784779MINMAXINFO *min_max_info = (MINMAXINFO *)lParam;4780if (windows[window_id].min_size != Size2()) {4781min_max_info->ptMinTrackSize.x = windows[window_id].min_size.x + decor.x;4782min_max_info->ptMinTrackSize.y = windows[window_id].min_size.y + decor.y;4783}4784if (windows[window_id].max_size != Size2()) {4785min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x;4786min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y;4787}4788if (windows[window_id].borderless) {4789Rect2i screen_rect = screen_get_usable_rect(window_get_current_screen(window_id));47904791// Set the size of (borderless) maximized mode to exclude taskbar (or any other panel) if present.4792min_max_info->ptMaxPosition.x = screen_rect.position.x;4793min_max_info->ptMaxPosition.y = screen_rect.position.y;4794min_max_info->ptMaxSize.x = screen_rect.size.x;4795min_max_info->ptMaxSize.y = screen_rect.size.y;4796}4797return 0;4798}4799} break;4800case WM_ERASEBKGND: {4801Color early_color;4802if (!_get_window_early_clear_override(early_color)) {4803break;4804}4805bool must_recreate_brush = !window_bkg_brush || window_bkg_brush_color != early_color.to_argb32();4806if (must_recreate_brush) {4807if (window_bkg_brush) {4808DeleteObject(window_bkg_brush);4809}4810window_bkg_brush = CreateSolidBrush(RGB(early_color.get_r8(), early_color.get_g8(), early_color.get_b8()));4811}4812HDC hdc = (HDC)wParam;4813RECT rect = {};4814if (GetUpdateRect(hWnd, &rect, true)) {4815FillRect(hdc, &rect, window_bkg_brush);4816}4817return 1;4818} break;4819case WM_PAINT: {4820Main::force_redraw();4821} break;4822case WM_SETTINGCHANGE:4823case WM_SYSCOLORCHANGE: {4824if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {4825if (is_dark_mode_supported() && dark_title_available) {4826BOOL value = is_dark_mode();4827::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));4828}4829}4830if (system_theme_changed.is_valid()) {4831Variant ret;4832Callable::CallError ce;4833system_theme_changed.callp(nullptr, 0, ret, ce);4834if (ce.error != Callable::CallError::CALL_OK) {4835ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));4836}4837}4838} break;4839case WM_THEMECHANGED: {4840if (is_dark_mode_supported() && dark_title_available) {4841BOOL value = is_dark_mode();4842::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));4843}4844} break;4845case WM_SYSCOMMAND: // Intercept system commands.4846{4847switch (wParam) // Check system calls.4848{4849case SC_SCREENSAVE: // Screensaver trying to start?4850case SC_MONITORPOWER: // Monitor trying to enter powersave?4851return 0; // Prevent from happening.4852case SC_KEYMENU:4853Engine *engine = Engine::get_singleton();4854if (((lParam >> 16) <= 0) && !engine->is_project_manager_hint() && !engine->is_editor_hint() && !GLOBAL_GET_CACHED(bool, "application/run/enable_alt_space_menu")) {4855return 0;4856}4857if (!_get_mods().has_flag(WinKeyModifierMask::ALT) || !(GetAsyncKeyState(VK_SPACE) & (1 << 15))) {4858return 0;4859}4860SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_SPACE, 0);4861SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_MENU, 0);4862}4863} break;4864case WM_INDICATOR_CALLBACK_MESSAGE: {4865if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN || lParam == WM_MBUTTONDOWN || lParam == WM_XBUTTONDOWN) {4866IndicatorID iid = (IndicatorID)wParam;4867MouseButton mb = MouseButton::LEFT;4868if (lParam == WM_RBUTTONDOWN) {4869mb = MouseButton::RIGHT;4870} else if (lParam == WM_MBUTTONDOWN) {4871mb = MouseButton::MIDDLE;4872} else if (lParam == WM_XBUTTONDOWN) {4873mb = MouseButton::MB_XBUTTON1;4874}4875if (indicators.has(iid)) {4876if (lParam == WM_RBUTTONDOWN && indicators[iid].menu_rid.is_valid() && native_menu->has_menu(indicators[iid].menu_rid)) {4877NOTIFYICONIDENTIFIER nid;4878ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));4879nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);4880nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;4881nid.uID = iid;4882nid.guidItem = GUID_NULL;48834884RECT rect;4885if (Shell_NotifyIconGetRect(&nid, &rect) == S_OK) {4886native_menu->popup(indicators[iid].menu_rid, Vector2i((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2));4887}4888} else if (indicators[iid].callback.is_valid()) {4889Variant v_button = mb;4890Variant v_pos = mouse_get_position();4891const Variant *v_args[2] = { &v_button, &v_pos };4892Variant ret;4893Callable::CallError ce;4894indicators[iid].callback.callp((const Variant **)&v_args, 2, ret, ce);4895if (ce.error != Callable::CallError::CALL_OK) {4896ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(indicators[iid].callback, v_args, 2, ce)));4897}4898}4899}4900return 0;4901}4902} break;4903case WM_CLOSE: {4904if (windows[window_id].activate_timer_id) {4905KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);4906windows[window_id].activate_timer_id = 0;4907}4908_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);4909return 0;4910}4911case WM_MOUSELEAVE: {4912if (window_mouseover_id == window_id) {4913old_invalid = true;4914window_mouseover_id = INVALID_WINDOW_ID;49154916_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);4917} else if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {4918// This is reached during drag and drop, after dropping in a different window.4919// Once-off notification, must call again.4920track_mouse_leave_event(windows[window_mouseover_id].hWnd);4921}49224923} break;4924case WM_INPUT: {4925if (!use_raw_input) {4926break;4927}49284929UINT dwSize;49304931GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER));4932LPBYTE lpb = new BYTE[dwSize];4933if (lpb == nullptr) {4934return 0;4935}49364937if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {4938OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));4939}49404941RAWINPUT *raw = (RAWINPUT *)lpb;49424943const BitField<WinKeyModifierMask> &mods = _get_mods();4944if (raw->header.dwType == RIM_TYPEKEYBOARD) {4945if (raw->data.keyboard.VKey == VK_SHIFT) {4946// If multiple Shifts are held down at the same time,4947// Windows natively only sends a KEYUP for the last one to be released.4948if (raw->data.keyboard.Flags & RI_KEY_BREAK) {4949// Make sure to check the latest key state since4950// we're in the middle of the message queue.4951if (GetAsyncKeyState(VK_SHIFT) < 0) {4952// A Shift is released, but another Shift is still held4953ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);49544955KeyEvent ke;4956ke.shift = false;4957ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);4958ke.alt = mods.has_flag(WinKeyModifierMask::ALT);4959ke.control = mods.has_flag(WinKeyModifierMask::CTRL);4960ke.meta = mods.has_flag(WinKeyModifierMask::META);4961ke.uMsg = WM_KEYUP;4962ke.window_id = window_id;49634964ke.wParam = VK_SHIFT;4965// data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.4966// Bit 30 -> key was previously down, bit 31 -> key is being released.4967ke.lParam = raw->data.keyboard.MakeCode << 16 | 1 << 30 | 1 << 31;4968key_event_buffer[key_event_pos++] = ke;4969}4970}4971}4972} else if (mouse_mode == MOUSE_MODE_CAPTURED && raw->header.dwType == RIM_TYPEMOUSE) {4973Ref<InputEventMouseMotion> mm;4974mm.instantiate();49754976mm->set_window_id(window_id);4977mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));4978mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));4979mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));4980mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));49814982mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f);49834984mm->set_button_mask(mouse_get_button_state());49854986Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);49874988// Centering just so it works as before.4989POINT pos = { (int)c.x, (int)c.y };4990ClientToScreen(windows[window_id].hWnd, &pos);4991SetCursorPos(pos.x, pos.y);49924993mm->set_position(c);4994mm->set_global_position(c);4995mm->set_velocity(Vector2(0, 0));4996mm->set_screen_velocity(Vector2(0, 0));49974998if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {4999mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));50005001} else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {5002int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);5003int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);5004int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);5005int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);50065007Vector2 abs_pos(5008(double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,5009(double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);50105011POINT coords; // Client coords.5012coords.x = abs_pos.x;5013coords.y = abs_pos.y;50145015ScreenToClient(hWnd, &coords);50165017mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));5018old_x = coords.x;5019old_y = coords.y;5020}5021mm->set_relative_screen_position(mm->get_relative());50225023if ((windows[window_id].window_focused || windows[window_id].is_popup) && mm->get_relative() != Vector2()) {5024Input::get_singleton()->parse_input_event(mm);5025}5026}5027delete[] lpb;5028} break;5029case WT_CSRCHANGE:5030case WT_PROXIMITY: {5031if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5032AXIS pressure;5033if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {5034windows[window_id].min_pressure = int(pressure.axMin);5035windows[window_id].max_pressure = int(pressure.axMax);5036}5037AXIS orientation[3];5038if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {5039windows[window_id].tilt_supported = orientation[0].axResolution && orientation[1].axResolution;5040}5041return 0;5042}5043} break;5044case WT_PACKET: {5045if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5046PACKET packet;5047if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) {5048POINT coords;5049GetCursorPos(&coords);5050ScreenToClient(windows[window_id].hWnd, &coords);50515052windows[window_id].last_pressure_update = 0;50535054float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);5055double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math::PI / 180);5056double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math::PI / 180));5057bool inverted = packet.pkStatus & TPS_INVERT;50585059Vector2 tilt = (windows[window_id].tilt_supported) ? Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)) : Vector2();50605061// Nothing changed, ignore event.5062if (!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) {5063break;5064}50655066windows[window_id].last_pressure = pressure;5067windows[window_id].last_tilt = tilt;5068windows[window_id].last_pen_inverted = inverted;50695070// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5071if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5072break;5073}50745075const BitField<WinKeyModifierMask> &mods = _get_mods();5076Ref<InputEventMouseMotion> mm;5077mm.instantiate();5078mm->set_window_id(window_id);5079mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5080mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5081mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5082mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));50835084mm->set_pressure(windows[window_id].last_pressure);5085mm->set_tilt(windows[window_id].last_tilt);5086mm->set_pen_inverted(windows[window_id].last_pen_inverted);50875088mm->set_button_mask(mouse_get_button_state());50895090mm->set_position(Vector2(coords.x, coords.y));5091mm->set_global_position(Vector2(coords.x, coords.y));50925093if (mouse_mode == MOUSE_MODE_CAPTURED) {5094Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5095old_x = c.x;5096old_y = c.y;50975098if (mm->get_position() == c) {5099center = c;5100return 0;5101}51025103Point2i ncenter = mm->get_position();5104center = ncenter;5105POINT pos = { (int)c.x, (int)c.y };5106ClientToScreen(windows[window_id].hWnd, &pos);5107SetCursorPos(pos.x, pos.y);5108}51095110mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5111mm->set_screen_velocity(mm->get_velocity());51125113if (old_invalid) {5114old_x = mm->get_position().x;5115old_y = mm->get_position().y;5116old_invalid = false;5117}51185119mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5120mm->set_relative_screen_position(mm->get_relative());5121old_x = mm->get_position().x;5122old_y = mm->get_position().y;51235124if (windows[window_id].window_focused || window_get_active_popup() == window_id) {5125Input::get_singleton()->parse_input_event(mm);5126}5127}5128return 0;5129}5130} break;5131case WM_POINTERENTER: {5132if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5133break;5134}51355136if (tablet_get_current_driver() != "winink") {5137break;5138}51395140uint32_t pointer_id = LOWORD(wParam);5141POINTER_INPUT_TYPE pointer_type = PT_POINTER;5142if (!GetPointerType(pointer_id, &pointer_type)) {5143break;5144}51455146if (pointer_type != PT_PEN) {5147break;5148}51495150pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;5151windows[window_id].block_mm = true;5152return 0;5153} break;5154case WM_POINTERLEAVE: {5155pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;5156windows[window_id].block_mm = false;5157return 0;5158} break;5159case WM_POINTERDOWN:5160case WM_POINTERUP: {5161if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5162break;5163}51645165if (tablet_get_current_driver() != "winink") {5166break;5167}51685169uint32_t pointer_id = LOWORD(wParam);5170POINTER_INPUT_TYPE pointer_type = PT_POINTER;5171if (!GetPointerType(pointer_id, &pointer_type)) {5172break;5173}51745175if (pointer_type != PT_PEN) {5176break;5177}51785179Ref<InputEventMouseButton> mb;5180mb.instantiate();5181mb->set_window_id(window_id);51825183BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;5184if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {5185last_button_state.set_flag(MouseButtonMask::LEFT);5186mb->set_button_index(MouseButton::LEFT);5187}5188if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {5189last_button_state.set_flag(MouseButtonMask::RIGHT);5190mb->set_button_index(MouseButton::RIGHT);5191}5192if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {5193last_button_state.set_flag(MouseButtonMask::MIDDLE);5194mb->set_button_index(MouseButton::MIDDLE);5195}5196if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {5197last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);5198mb->set_button_index(MouseButton::MB_XBUTTON1);5199}5200if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {5201last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);5202mb->set_button_index(MouseButton::MB_XBUTTON2);5203}5204mb->set_button_mask(last_button_state);52055206const BitField<WinKeyModifierMask> &mods = _get_mods();5207mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5208mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5209mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5210mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));52115212POINT coords; // Client coords.5213coords.x = GET_X_LPARAM(lParam);5214coords.y = GET_Y_LPARAM(lParam);52155216// Note: Handle popup closing here, since mouse event is not emulated and hook will not be called.5217uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;5218if (delta > 250) {5219Point2i pos = Point2i(coords.x, coords.y) - _get_screens_origin();5220List<WindowID>::Element *C = nullptr;5221List<WindowID>::Element *E = popup_list.back();5222// Find top popup to close.5223while (E) {5224// Popup window area.5225Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));5226// Area of the parent window, which responsible for opening sub-menu.5227Rect2i safe_rect = window_get_popup_safe_rect(E->get());5228if (win_rect.has_point(pos)) {5229break;5230} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {5231break;5232} else {5233C = E;5234E = E->prev();5235}5236}5237if (C) {5238_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);5239}5240}52415242int64_t pen_id = GET_POINTERID_WPARAM(wParam);5243if (uMsg == WM_POINTERDOWN) {5244mb->set_pressed(true);5245if (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()) {5246mb->set_double_click(true);5247pointer_down_time[pen_id] = 0;5248} else {5249pointer_down_time[pen_id] = GetMessageTime();5250pointer_prev_button[pen_id] = mb->get_button_index();5251pointer_last_pos[pen_id] = Vector2(coords.x, coords.y);5252}5253pointer_button[pen_id] = mb->get_button_index();5254} else {5255if (!pointer_button.has(pen_id)) {5256return 0;5257}5258mb->set_pressed(false);5259mb->set_button_index(pointer_button[pen_id]);5260pointer_button[pen_id] = MouseButton::NONE;5261}52625263ScreenToClient(windows[window_id].hWnd, &coords);52645265mb->set_position(Vector2(coords.x, coords.y));5266mb->set_global_position(Vector2(coords.x, coords.y));52675268Input::get_singleton()->parse_input_event(mb);52695270return 0;5271} break;5272case WM_POINTERUPDATE: {5273if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5274break;5275}52765277if (tablet_get_current_driver() != "winink") {5278break;5279}52805281uint32_t pointer_id = LOWORD(wParam);5282POINTER_INPUT_TYPE pointer_type = PT_POINTER;5283if (!GetPointerType(pointer_id, &pointer_type)) {5284break;5285}52865287if (pointer_type != PT_PEN) {5288break;5289}52905291POINTER_PEN_INFO pen_info;5292if (!GetPointerPenInfo(pointer_id, &pen_info)) {5293break;5294}52955296if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5297// Universal translation enabled; ignore OS translation.5298LPARAM extra = GetMessageExtraInfo();5299if (IsTouchEvent(extra)) {5300break;5301}5302}53035304if (window_mouseover_id != window_id) {5305// Mouse enter.53065307if (mouse_mode != MOUSE_MODE_CAPTURED) {5308if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {5309// Leave previous window.5310_send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);5311}5312_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);5313}53145315CursorShape c = cursor_shape;5316cursor_shape = CURSOR_MAX;5317cursor_set_shape(c);5318window_mouseover_id = window_id;53195320// Once-off notification, must call again.5321track_mouse_leave_event(hWnd);5322}53235324// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5325if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5326break;5327}53285329Ref<InputEventMouseMotion> mm;5330mm.instantiate();53315332mm->set_window_id(window_id);5333if (pen_info.penMask & PEN_MASK_PRESSURE) {5334mm->set_pressure((float)pen_info.pressure / 1024);5335} else {5336mm->set_pressure((HIWORD(wParam) & POINTER_MESSAGE_FLAG_FIRSTBUTTON) ? 1.0f : 0.0f);5337}5338if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) {5339mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90));5340}5341mm->set_pen_inverted(pen_info.penFlags & (PEN_FLAG_INVERTED | PEN_FLAG_ERASER));53425343const BitField<WinKeyModifierMask> &mods = _get_mods();5344mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5345mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5346mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5347mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));53485349BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;5350if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {5351last_button_state.set_flag(MouseButtonMask::LEFT);5352}5353if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {5354last_button_state.set_flag(MouseButtonMask::RIGHT);5355}5356if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {5357last_button_state.set_flag(MouseButtonMask::MIDDLE);5358}5359if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {5360last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);5361}5362if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {5363last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);5364}5365mm->set_button_mask(last_button_state);53665367POINT coords; // Client coords.5368coords.x = GET_X_LPARAM(lParam);5369coords.y = GET_Y_LPARAM(lParam);53705371ScreenToClient(windows[window_id].hWnd, &coords);53725373mm->set_position(Vector2(coords.x, coords.y));5374mm->set_global_position(Vector2(coords.x, coords.y));53755376if (mouse_mode == MOUSE_MODE_CAPTURED) {5377Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5378old_x = c.x;5379old_y = c.y;53805381if (mm->get_position() == c) {5382center = c;5383return 0;5384}53855386Point2i ncenter = mm->get_position();5387center = ncenter;5388POINT pos = { (int)c.x, (int)c.y };5389ClientToScreen(hWnd, &pos);5390SetCursorPos(pos.x, pos.y);5391}53925393mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5394mm->set_screen_velocity(mm->get_velocity());53955396if (old_invalid) {5397old_x = mm->get_position().x;5398old_y = mm->get_position().y;5399old_invalid = false;5400}54015402mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5403mm->set_relative_screen_position(mm->get_relative());5404old_x = mm->get_position().x;5405old_y = mm->get_position().y;5406if (windows[window_id].window_focused || window_get_active_popup() == window_id) {5407Input::get_singleton()->parse_input_event(mm);5408}54095410return 0; // Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event.5411} break;5412case WM_MOUSEMOVE: {5413if (windows[window_id].block_mm) {5414break;5415}54165417if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5418break;5419}54205421if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5422// Universal translation enabled; ignore OS translation.5423LPARAM extra = GetMessageExtraInfo();5424if (IsTouchEvent(extra)) {5425break;5426}5427}54285429DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position());5430if (windows.has(over_id) && !Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) {5431// Don't consider the windowborder as part of the window.5432over_id = INVALID_WINDOW_ID;5433}5434if (window_mouseover_id != over_id) {5435// Mouse enter.54365437if (mouse_mode != MOUSE_MODE_CAPTURED) {5438if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {5439// Leave previous window.5440_send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);5441}54425443if (over_id != INVALID_WINDOW_ID && windows.has(over_id)) {5444_send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER);5445}5446}54475448CursorShape c = cursor_shape;5449cursor_shape = CURSOR_MAX;5450cursor_set_shape(c);5451window_mouseover_id = over_id;54525453// Once-off notification, must call again.5454track_mouse_leave_event(hWnd);5455}54565457// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5458if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5459break;5460}54615462DisplayServer::WindowID receiving_window_id = window_id;5463if (!windows[window_id].no_focus) {5464receiving_window_id = _get_focused_window_or_popup();5465if (receiving_window_id == INVALID_WINDOW_ID) {5466receiving_window_id = window_id;5467}5468}54695470const BitField<WinKeyModifierMask> &mods = _get_mods();5471Ref<InputEventMouseMotion> mm;5472mm.instantiate();5473mm->set_window_id(receiving_window_id);5474mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5475mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5476mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5477mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));54785479if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5480// 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.5481if (windows[window_id].last_pressure_update < 10) {5482windows[window_id].last_pressure_update++;5483} else {5484windows[window_id].last_tilt = Vector2();5485windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;5486windows[window_id].last_pen_inverted = false;5487}5488} else {5489windows[window_id].last_tilt = Vector2();5490windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;5491windows[window_id].last_pen_inverted = false;5492}54935494mm->set_pressure(windows[window_id].last_pressure);5495mm->set_tilt(windows[window_id].last_tilt);5496mm->set_pen_inverted(windows[window_id].last_pen_inverted);54975498mm->set_button_mask(mouse_get_button_state());54995500mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));5501mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));55025503if (mouse_mode == MOUSE_MODE_CAPTURED) {5504Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5505old_x = c.x;5506old_y = c.y;55075508if (mm->get_position() == c) {5509center = c;5510return 0;5511}55125513Point2i ncenter = mm->get_position();5514center = ncenter;5515POINT pos = { (int)c.x, (int)c.y };5516ClientToScreen(windows[window_id].hWnd, &pos);5517SetCursorPos(pos.x, pos.y);5518}55195520mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5521mm->set_screen_velocity(mm->get_velocity());55225523if (old_invalid) {5524old_x = mm->get_position().x;5525old_y = mm->get_position().y;5526old_invalid = false;5527}55285529mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5530mm->set_relative_screen_position(mm->get_relative());5531old_x = mm->get_position().x;5532old_y = mm->get_position().y;55335534if (receiving_window_id != window_id) {5535// Adjust event position relative to window distance when event is sent to a different window.5536mm->set_position(mm->get_position() - window_get_position(receiving_window_id) + window_get_position(window_id));5537mm->set_global_position(mm->get_position());5538}55395540Input::get_singleton()->parse_input_event(mm);55415542} break;5543case WM_LBUTTONDOWN:5544case WM_LBUTTONUP:5545if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5546// Universal translation enabled; ignore OS translations for left button.5547LPARAM extra = GetMessageExtraInfo();5548if (IsTouchEvent(extra)) {5549break;5550}5551}5552[[fallthrough]];5553case WM_MBUTTONDOWN:5554case WM_MBUTTONUP:5555case WM_RBUTTONDOWN:5556case WM_RBUTTONUP:5557case WM_MOUSEWHEEL:5558case WM_MOUSEHWHEEL:5559case WM_LBUTTONDBLCLK:5560case WM_MBUTTONDBLCLK:5561case WM_RBUTTONDBLCLK:5562case WM_XBUTTONDBLCLK:5563case WM_XBUTTONDOWN:5564case WM_XBUTTONUP: {5565Ref<InputEventMouseButton> mb;5566mb.instantiate();5567mb->set_window_id(window_id);55685569switch (uMsg) {5570case WM_LBUTTONDOWN: {5571mb->set_pressed(true);5572mb->set_button_index(MouseButton::LEFT);5573} break;5574case WM_LBUTTONUP: {5575mb->set_pressed(false);5576mb->set_button_index(MouseButton::LEFT);5577} break;5578case WM_MBUTTONDOWN: {5579mb->set_pressed(true);5580mb->set_button_index(MouseButton::MIDDLE);5581} break;5582case WM_MBUTTONUP: {5583mb->set_pressed(false);5584mb->set_button_index(MouseButton::MIDDLE);5585} break;5586case WM_RBUTTONDOWN: {5587mb->set_pressed(true);5588mb->set_button_index(MouseButton::RIGHT);5589} break;5590case WM_RBUTTONUP: {5591mb->set_pressed(false);5592mb->set_button_index(MouseButton::RIGHT);5593} break;5594case WM_LBUTTONDBLCLK: {5595mb->set_pressed(true);5596mb->set_button_index(MouseButton::LEFT);5597mb->set_double_click(true);5598} break;5599case WM_RBUTTONDBLCLK: {5600mb->set_pressed(true);5601mb->set_button_index(MouseButton::RIGHT);5602mb->set_double_click(true);5603} break;5604case WM_MBUTTONDBLCLK: {5605mb->set_pressed(true);5606mb->set_button_index(MouseButton::MIDDLE);5607mb->set_double_click(true);5608} break;5609case WM_MOUSEWHEEL: {5610mb->set_pressed(true);5611int motion = (short)HIWORD(wParam);5612if (!motion) {5613return 0;5614}56155616if (motion > 0) {5617mb->set_button_index(MouseButton::WHEEL_UP);5618} else {5619mb->set_button_index(MouseButton::WHEEL_DOWN);5620}5621mb->set_factor(std::fabs((double)motion / (double)WHEEL_DELTA));5622} break;5623case WM_MOUSEHWHEEL: {5624mb->set_pressed(true);5625int motion = (short)HIWORD(wParam);5626if (!motion) {5627return 0;5628}56295630if (motion < 0) {5631mb->set_button_index(MouseButton::WHEEL_LEFT);5632} else {5633mb->set_button_index(MouseButton::WHEEL_RIGHT);5634}5635mb->set_factor(std::fabs((double)motion / (double)WHEEL_DELTA));5636} break;5637case WM_XBUTTONDOWN: {5638mb->set_pressed(true);5639if (HIWORD(wParam) == XBUTTON1) {5640mb->set_button_index(MouseButton::MB_XBUTTON1);5641} else {5642mb->set_button_index(MouseButton::MB_XBUTTON2);5643}5644} break;5645case WM_XBUTTONUP: {5646mb->set_pressed(false);5647if (HIWORD(wParam) == XBUTTON1) {5648mb->set_button_index(MouseButton::MB_XBUTTON1);5649} else {5650mb->set_button_index(MouseButton::MB_XBUTTON2);5651}5652} break;5653case WM_XBUTTONDBLCLK: {5654mb->set_pressed(true);5655if (HIWORD(wParam) == XBUTTON1) {5656mb->set_button_index(MouseButton::MB_XBUTTON1);5657} else {5658mb->set_button_index(MouseButton::MB_XBUTTON2);5659}5660mb->set_double_click(true);5661} break;5662default: {5663return 0;5664}5665}56665667const BitField<WinKeyModifierMask> &mods = _get_mods();5668mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5669mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5670mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5671mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));56725673if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) {5674MouseButtonMask mask = mouse_button_to_mask(mb->get_button_index());5675BitField<MouseButtonMask> scroll_mask = mouse_get_button_state();5676scroll_mask.set_flag(mask);5677mb->set_button_mask(scroll_mask);5678} else {5679mb->set_button_mask(mouse_get_button_state());5680}56815682mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));56835684if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {5685mb->set_position(Vector2(old_x, old_y));5686}56875688if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {5689if (mb->is_pressed()) {5690if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED) {5691SetCapture(hWnd);5692}5693} else {5694if (--pressrc <= 0 || mouse_get_button_state().is_empty()) {5695if (mouse_mode != MOUSE_MODE_CAPTURED) {5696ReleaseCapture();5697}5698pressrc = 0;5699}5700}5701} else {5702// For reasons unknown to humanity, wheel comes in screen coordinates.5703POINT coords;5704coords.x = mb->get_position().x;5705coords.y = mb->get_position().y;57065707ScreenToClient(hWnd, &coords);57085709mb->set_position(Vector2(coords.x, coords.y));5710}57115712mb->set_global_position(mb->get_position());57135714Input::get_singleton()->parse_input_event(mb);5715if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) {5716// Send release for mouse wheel.5717Ref<InputEventMouseButton> mbd = mb->duplicate();5718mbd->set_window_id(window_id);5719mbd->set_button_mask(mouse_get_button_state());5720mbd->set_pressed(false);5721Input::get_singleton()->parse_input_event(mbd);5722}57235724// Propagate the button up event to the window on which the button down5725// event was triggered. This is needed for drag & drop to work between windows,5726// because the engine expects events to keep being processed5727// on the same window dragging started.5728if (mb->is_pressed()) {5729last_mouse_button_down_window = window_id;5730} else if (last_mouse_button_down_window != INVALID_WINDOW_ID) {5731mb->set_window_id(last_mouse_button_down_window);5732last_mouse_button_down_window = INVALID_WINDOW_ID;5733}5734} break;57355736case WM_WINDOWPOSCHANGED: {5737WindowData &window = windows[window_id];57385739int off_x = (window.multiwindow_fs || (!window.fullscreen && window.borderless && IsZoomed(hWnd))) ? FS_TRANSP_BORDER : 0;5740Rect2i window_client_rect;5741Rect2i window_rect;5742{5743RECT rect;5744GetClientRect(hWnd, &rect);5745ClientToScreen(hWnd, (POINT *)&rect.left);5746ClientToScreen(hWnd, (POINT *)&rect.right);5747window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left - off_x, rect.bottom - rect.top);5748window_client_rect.position -= _get_screens_origin();57495750RECT wrect;5751GetWindowRect(hWnd, &wrect);5752window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left - off_x, wrect.bottom - wrect.top);5753window_rect.position -= _get_screens_origin();5754}57555756WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam;57575758bool rect_changed = false;5759if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) {5760int screen_id = window_get_current_screen(window_id);5761Size2i screen_size = screen_get_size(screen_id);5762Point2i screen_position = screen_get_position(screen_id);5763Rect2i usable = screen_get_usable_rect(screen_id);57645765window.maximized = false;5766window.minimized = false;5767window.fullscreen = false;57685769if (IsIconic(hWnd)) {5770window.minimized = true;5771} else if (IsZoomed(hWnd)) {5772window.maximized = true;57735774// If maximized_window_size == screen_size add 1px border to prevent switching to exclusive_fs.5775if (!window.maximized_fs && window.borderless && window_rect.position == screen_position && window_rect.size == screen_size) {5776// Window (borderless) was just maximized and the covers the entire screen.5777window.maximized_fs = true;5778_update_window_style(window_id, false);5779}5780if (window.borderless && (screen_size != usable.size || screen_position != usable.position)) {5781Point2 pos = usable.position + _get_screens_origin();5782Size2 size = usable.size;5783MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);5784}5785} else if (window_rect.position == screen_position && window_rect.size == screen_size) {5786window.fullscreen = true;5787} else if (window.borderless && usable.position == window_rect.position && usable.size == window_rect.size) {5788window.maximized = true;5789}57905791if (window.maximized_fs && !window.maximized) {5792// Window (maximized and covering fullscreen) was just non-maximized.5793window.maximized_fs = false;5794_update_window_style(window_id, false);5795}57965797if (!window.minimized) {5798window.width = window_client_rect.size.width;5799window.height = window_client_rect.size.height;58005801rect_changed = true;5802}5803#if defined(RD_ENABLED)5804if (window.create_completed && rendering_context && window.context_created) {5805// Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.5806rendering_context->window_set_size(window_id, window.width, window.height);5807}5808#endif5809#if defined(GLES3_ENABLED)5810if (window.create_completed && gl_manager_native) {5811gl_manager_native->window_resize(window_id, window.width, window.height);5812}5813if (window.create_completed && gl_manager_angle) {5814gl_manager_angle->window_resize(window_id, window.width, window.height);5815}5816#endif5817}58185819if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) {5820window.last_pos = window_client_rect.position;5821rect_changed = true;5822}58235824if (rect_changed) {5825if (window.rect_changed_callback.is_valid()) {5826window.rect_changed_callback.call(Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height));5827}58285829// Update cursor clip region after window rect has changed.5830if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {5831RECT crect;5832GetClientRect(window.hWnd, &crect);5833crect.right -= off_x;5834ClientToScreen(window.hWnd, (POINT *)&crect.left);5835ClientToScreen(window.hWnd, (POINT *)&crect.right);5836ClipCursor(&crect);5837}58385839if (!window.minimized && window.was_fullscreen_pre_min) {5840// Restore fullscreen mode if window was in fullscreen before it was minimized.5841int cs = window_get_current_screen(window_id);5842Point2 pos = screen_get_position(cs) + _get_screens_origin();5843Size2 size = screen_get_size(cs);58445845window.was_fullscreen_pre_min = false;5846window.fullscreen = true;5847window.maximized = false;5848window.minimized = false;58495850_update_window_style(window_id, false);58515852MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);5853}5854} else {5855if (window.parent_hwnd) {5856// WM_WINDOWPOSCHANGED is sent when the parent changes.5857// If we are supposed to have a parent and now we don't, it's likely5858// because the parent was closed. We will close our window as well.5859// This prevents an embedded game from staying alive when the editor is closed or crashes.5860if (!GetParent(window.hWnd)) {5861SendMessage(window.hWnd, WM_CLOSE, 0, 0);5862}5863}5864}58655866// Return here to prevent WM_MOVE and WM_SIZE from being sent5867// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks5868return 0;5869} break;58705871case WM_ENTERSIZEMOVE: {5872Input::get_singleton()->release_pressed_events();5873windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_MOVE_REDRAW, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);5874} break;5875case WM_EXITSIZEMOVE: {5876KillTimer(windows[window_id].hWnd, windows[window_id].move_timer_id);5877windows[window_id].move_timer_id = 0;5878// Reset the correct mouse mode because we couldn't call ReleaseCapture in5879// _set_mouse_mode_impl while in _process_activate_event (because the user was moving a window).5880_set_mouse_mode_impl(mouse_mode);5881} break;5882case WM_TIMER: {5883if (wParam == windows[window_id].move_timer_id) {5884_THREAD_SAFE_UNLOCK_5885_process_key_events();5886if (!Main::is_iterating()) {5887Main::iteration();5888}5889_THREAD_SAFE_LOCK_5890} else if (wParam == windows[window_id].activate_timer_id) {5891_process_activate_event(window_id);5892KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);5893windows[window_id].activate_timer_id = 0;5894windows[window_id].first_activation_done = true;5895}5896} break;5897case WM_SYSKEYUP:5898case WM_KEYUP:5899case WM_SYSKEYDOWN:5900case WM_KEYDOWN: {5901if (windows[window_id].ime_suppress_next_keyup && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)) {5902windows[window_id].ime_suppress_next_keyup = false;5903break;5904}5905if (windows[window_id].ime_in_progress) {5906break;5907}59085909if (mouse_mode == MOUSE_MODE_CAPTURED) {5910// When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves5911if (wParam == VK_F4 && _get_mods().has_flag(WinKeyModifierMask::ALT) && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) {5912_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);5913}5914}5915[[fallthrough]];5916}5917case WM_CHAR: {5918ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);5919const BitField<WinKeyModifierMask> &mods = _get_mods();59205921KeyEvent ke;5922ke.shift = mods.has_flag(WinKeyModifierMask::SHIFT);5923ke.alt = mods.has_flag(WinKeyModifierMask::ALT);5924ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);5925ke.control = mods.has_flag(WinKeyModifierMask::CTRL);5926ke.meta = mods.has_flag(WinKeyModifierMask::META);5927ke.uMsg = uMsg;5928ke.window_id = window_id;59295930if (ke.uMsg == WM_SYSKEYDOWN) {5931ke.uMsg = WM_KEYDOWN;5932}5933if (ke.uMsg == WM_SYSKEYUP) {5934ke.uMsg = WM_KEYUP;5935}59365937ke.wParam = wParam;5938ke.lParam = lParam;5939key_event_buffer[key_event_pos++] = ke;59405941} break;5942case WM_IME_COMPOSITION: {5943CANDIDATEFORM cf;5944cf.dwIndex = 0;59455946cf.dwStyle = CFS_CANDIDATEPOS;5947cf.ptCurrentPos.x = windows[window_id].im_position.x;5948cf.ptCurrentPos.y = windows[window_id].im_position.y;5949ImmSetCandidateWindow(windows[window_id].im_himc, &cf);59505951cf.dwStyle = CFS_EXCLUDE;5952cf.rcArea.left = windows[window_id].im_position.x;5953cf.rcArea.right = windows[window_id].im_position.x;5954cf.rcArea.top = windows[window_id].im_position.y;5955cf.rcArea.bottom = windows[window_id].im_position.y;5956ImmSetCandidateWindow(windows[window_id].im_himc, &cf);59575958if (windows[window_id].ime_active) {5959SetCaretPos(windows[window_id].im_position.x, windows[window_id].im_position.y);5960OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);5961}5962} break;5963case WM_INPUTLANGCHANGEREQUEST: {5964// FIXME: Do something?5965} break;5966case WM_IME_STARTCOMPOSITION: {5967if (windows[window_id].ime_active) {5968windows[window_id].ime_in_progress = true;5969if (key_event_pos > 0) {5970key_event_pos--;5971}5972}5973return 0;5974} break;5975case WM_IME_ENDCOMPOSITION: {5976if (windows[window_id].ime_active) {5977windows[window_id].ime_in_progress = false;5978windows[window_id].ime_suppress_next_keyup = true;5979}5980return 0;5981} break;5982case WM_IME_NOTIFY: {5983return 0;5984} break;5985case WM_TOUCH: {5986BOOL bHandled = FALSE;5987UINT cInputs = LOWORD(wParam);5988PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);5989if (pInputs) {5990if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {5991for (UINT i = 0; i < cInputs; i++) {5992TOUCHINPUT ti = pInputs[i];5993POINT touch_pos = {5994TOUCH_COORD_TO_PIXEL(ti.x),5995TOUCH_COORD_TO_PIXEL(ti.y),5996};5997ScreenToClient(hWnd, &touch_pos);5998// Do something with each touch input entry.5999if (ti.dwFlags & TOUCHEVENTF_MOVE) {6000_drag_event(window_id, touch_pos.x, touch_pos.y, ti.dwID);6001} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {6002_touch_event(window_id, ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);6003}6004}6005bHandled = TRUE;6006} else {6007// TODO: Handle the error here.6008}6009memdelete_arr(pInputs);6010} else {6011// TODO: Handle the error here, probably out of memory.6012}6013if (bHandled) {6014CloseTouchInputHandle((HTOUCHINPUT)lParam);6015return 0;6016}60176018} break;6019case WM_DESTROY: {6020#ifdef ACCESSKIT_ENABLED6021if (accessibility_driver) {6022accessibility_driver->window_destroy(window_id);6023}6024#endif6025Input::get_singleton()->flush_buffered_events();6026if (window_mouseover_id == window_id) {6027window_mouseover_id = INVALID_WINDOW_ID;6028_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);6029}6030} break;6031case WM_SETCURSOR: {6032if (LOWORD(lParam) == HTCLIENT) {6033if (windows[window_id].window_focused && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) {6034// Hide the cursor.6035if (hCursor == nullptr) {6036hCursor = SetCursor(nullptr);6037} else {6038SetCursor(nullptr);6039}6040} else {6041if (hCursor != nullptr) {6042CursorShape c = cursor_shape;6043cursor_shape = CURSOR_MAX;6044cursor_set_shape(c);6045hCursor = nullptr;6046}6047}6048}6049} break;6050default: {6051if (user_proc) {6052return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);6053}6054}6055}60566057return DefWindowProcW(hWnd, uMsg, wParam, lParam);6058}60596060LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {6061DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());6062if (ds_win) {6063return ds_win->WndProc(hWnd, uMsg, wParam, lParam);6064} else {6065return DefWindowProcW(hWnd, uMsg, wParam, lParam);6066}6067}60686069void DisplayServerWindows::_process_activate_event(WindowID p_window_id) {6070WindowData &wd = windows[p_window_id];6071if (wd.activate_state == WA_ACTIVE || wd.activate_state == WA_CLICKACTIVE) {6072last_focused_window = p_window_id;6073_set_mouse_mode_impl(mouse_mode);6074if (!IsIconic(wd.hWnd)) {6075SetFocus(wd.hWnd);6076}6077wd.window_focused = true;6078#ifdef ACCESSKIT_ENABLED6079if (accessibility_driver) {6080accessibility_driver->accessibility_set_window_focused(p_window_id, true);6081}6082#endif6083_send_window_event(wd, WINDOW_EVENT_FOCUS_IN);6084} else { // WM_INACTIVE.6085Input::get_singleton()->release_pressed_events();6086track_mouse_leave_event(wd.hWnd);6087// Release capture unconditionally because it can be set due to dragging, in addition to captured mode.6088// When the user is moving a window, it's important to not ReleaseCapture because it will cause6089// the window movement to stop and if the user tries to move the Windows when it's not activated,6090// it will prevent the window movement. If we are here and a window is moving, it's because we had multiple6091// opened windows in the editor and we are definitively not in a middle of dragging.6092if (!_has_moving_window()) {6093ReleaseCapture();6094}6095wd.window_focused = false;6096#ifdef ACCESSKIT_ENABLED6097if (accessibility_driver) {6098accessibility_driver->accessibility_set_window_focused(p_window_id, false);6099}6100#endif6101_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);6102}61036104if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {6105wintab_WTEnable(wd.wtctx, wd.activate_state);6106}6107}61086109void DisplayServerWindows::_process_key_events() {6110for (int i = 0; i < key_event_pos; i++) {6111KeyEvent &ke = key_event_buffer[i];6112switch (ke.uMsg) {6113case WM_CHAR: {6114// Extended keys should only be processed as WM_KEYDOWN message.6115if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) {6116static char32_t prev_wc = 0;6117char32_t unicode = ke.wParam;6118if ((unicode & 0xfffffc00) == 0xd800) {6119if (prev_wc != 0) {6120ERR_PRINT("invalid utf16 surrogate input");6121}6122prev_wc = unicode;6123break; // Skip surrogate.6124} else if ((unicode & 0xfffffc00) == 0xdc00) {6125if (prev_wc == 0) {6126ERR_PRINT("invalid utf16 surrogate input");6127break; // Skip invalid surrogate.6128}6129unicode = (prev_wc << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);6130prev_wc = 0;6131} else {6132prev_wc = 0;6133}6134Ref<InputEventKey> k;6135k.instantiate();61366137UINT vk = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK);6138bool is_oem = (vk >= 0xB8) && (vk <= 0xE6);6139Key keycode = KeyMappingWindows::get_keysym(vk);6140Key key_label = keycode;6141Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));61426143static BYTE keyboard_state[256];6144memset(keyboard_state, 0, 256);6145wchar_t chars[256] = {};6146UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX);6147if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) {6148String keysym = String::utf16((char16_t *)chars, 255);6149if (!keysym.is_empty()) {6150char32_t unicode_value = keysym[0];6151// For printable ASCII characters (0x20-0x7E), override the original keycode with the character value.6152if (is_oem && Key::SPACE <= (Key)unicode_value && (Key)unicode_value <= Key::ASCIITILDE) {6153keycode = fix_keycode(unicode_value, (Key)unicode_value);6154}6155key_label = fix_key_label(unicode_value, keycode);6156}6157}61586159k->set_window_id(ke.window_id);6160if (keycode != Key::SHIFT) {6161k->set_shift_pressed(ke.shift);6162}6163if (keycode != Key::ALT) {6164k->set_alt_pressed(ke.alt);6165}6166if (keycode != Key::CTRL) {6167k->set_ctrl_pressed(ke.control);6168}6169if (keycode != Key::META) {6170k->set_meta_pressed(ke.meta);6171}6172k->set_pressed(true);6173k->set_keycode(keycode);6174k->set_physical_keycode(physical_keycode);6175k->set_key_label(key_label);6176k->set_unicode(fix_unicode(unicode));6177if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {6178k->set_alt_pressed(false);6179k->set_ctrl_pressed(false);6180}61816182Input::get_singleton()->parse_input_event(k);6183} else {6184// Do nothing.6185}6186} break;6187case WM_KEYUP:6188case WM_KEYDOWN: {6189Ref<InputEventKey> k;6190k.instantiate();61916192k->set_window_id(ke.window_id);6193k->set_pressed(ke.uMsg == WM_KEYDOWN);61946195bool is_oem = (ke.wParam >= 0xB8) && (ke.wParam <= 0xE6);6196Key keycode = KeyMappingWindows::get_keysym(ke.wParam);6197if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) {6198// Special case for Numpad Enter key.6199keycode = Key::KP_ENTER;6200}6201Key key_label = keycode;6202Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));6203KeyLocation location = KeyMappingWindows::get_location((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));62046205static BYTE keyboard_state[256];6206memset(keyboard_state, 0, 256);6207wchar_t chars[256] = {};6208UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX);6209if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) {6210String keysym = String::utf16((char16_t *)chars, 255);6211if (!keysym.is_empty()) {6212char32_t unicode_value = keysym[0];6213// For printable ASCII characters (0x20-0x7E), override the original keycode with the character value.6214if (is_oem && Key::SPACE <= (Key)unicode_value && (Key)unicode_value <= Key::ASCIITILDE) {6215keycode = fix_keycode(unicode_value, (Key)unicode_value);6216}6217key_label = fix_key_label(unicode_value, keycode);6218}6219}62206221if (keycode != Key::SHIFT) {6222k->set_shift_pressed(ke.shift);6223}6224if (keycode != Key::ALT) {6225k->set_alt_pressed(ke.alt);6226}6227if (keycode != Key::CTRL) {6228k->set_ctrl_pressed(ke.control);6229}6230if (keycode != Key::META) {6231k->set_meta_pressed(ke.meta);6232}6233k->set_keycode(keycode);6234k->set_physical_keycode(physical_keycode);6235k->set_location(location);6236k->set_key_label(key_label);62376238if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {6239char32_t unicode = key_event_buffer[i + 1].wParam;6240static char32_t prev_wck = 0;6241if ((unicode & 0xfffffc00) == 0xd800) {6242if (prev_wck != 0) {6243ERR_PRINT("invalid utf16 surrogate input");6244}6245prev_wck = unicode;6246break; // Skip surrogate.6247} else if ((unicode & 0xfffffc00) == 0xdc00) {6248if (prev_wck == 0) {6249ERR_PRINT("invalid utf16 surrogate input");6250break; // Skip invalid surrogate.6251}6252unicode = (prev_wck << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);6253prev_wck = 0;6254} else {6255prev_wck = 0;6256}6257k->set_unicode(fix_unicode(unicode));6258}6259if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {6260k->set_alt_pressed(false);6261k->set_ctrl_pressed(false);6262}62636264k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));62656266Input::get_singleton()->parse_input_event(k);62676268} break;6269}6270}62716272key_event_pos = 0;6273}62746275void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const String &p_new_driver) {6276for (KeyValue<WindowID, WindowData> &E : windows) {6277WindowData &wd = E.value;6278wd.block_mm = false;6279if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) {6280wintab_WTEnable(wd.wtctx, false);6281wintab_WTClose(wd.wtctx);6282wd.wtctx = nullptr;6283}6284if ((p_new_driver == "wintab") && wintab_available) {6285wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);6286wd.wtlc.lcOptions |= CXO_MESSAGES;6287wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;6288wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;6289wd.wtlc.lcPktMode = 0;6290wd.wtlc.lcOutOrgX = 0;6291wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;6292wd.wtlc.lcOutOrgY = 0;6293wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;6294wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);6295if (wd.wtctx) {6296wintab_WTEnable(wd.wtctx, true);6297AXIS pressure;6298if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {6299wd.min_pressure = int(pressure.axMin);6300wd.max_pressure = int(pressure.axMax);6301}6302AXIS orientation[3];6303if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {6304wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;6305}6306wintab_WTEnable(wd.wtctx, true);6307} else {6308print_verbose("WinTab context creation failed.");6309}6310}6311}6312}63136314DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent, HWND p_parent_hwnd) {6315DWORD dwExStyle;6316DWORD dwStyle;63176318_get_window_style(window_id_counter == 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, dwStyle, dwExStyle);63196320int rq_screen = get_screen_from_rect(p_rect);6321if (rq_screen < 0) {6322rq_screen = get_primary_screen(); // Requested window rect is outside any screen bounds.6323}6324Rect2i usable_rect = screen_get_usable_rect(rq_screen);63256326Point2i offset = _get_screens_origin();63276328RECT WindowRect;63296330int off_x = (p_mode == WINDOW_MODE_FULLSCREEN || ((p_flags & WINDOW_FLAG_BORDERLESS_BIT) && p_mode == WINDOW_MODE_MAXIMIZED)) ? FS_TRANSP_BORDER : 0;63316332WindowRect.left = p_rect.position.x;6333WindowRect.right = p_rect.position.x + p_rect.size.x + off_x;6334WindowRect.top = p_rect.position.y;6335WindowRect.bottom = p_rect.position.y + p_rect.size.y;63366337if (!p_parent_hwnd) {6338if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6339Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));63406341WindowRect.left = screen_rect.position.x;6342WindowRect.right = screen_rect.position.x + screen_rect.size.x + off_x;6343WindowRect.top = screen_rect.position.y;6344WindowRect.bottom = screen_rect.position.y + screen_rect.size.y;6345} else {6346Rect2i srect = screen_get_usable_rect(rq_screen);6347Point2i wpos = p_rect.position;6348if (srect != Rect2i()) {6349wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);6350}63516352WindowRect.left = wpos.x;6353WindowRect.right = wpos.x + p_rect.size.x + off_x;6354WindowRect.top = wpos.y;6355WindowRect.bottom = wpos.y + p_rect.size.y;6356}6357}63586359WindowRect.left += offset.x;6360WindowRect.right += offset.x;6361WindowRect.top += offset.y;6362WindowRect.bottom += offset.y;63636364if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6365AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);6366}63676368WindowID id = window_id_counter;6369{6370WindowData *wd_transient_parent = nullptr;6371HWND owner_hwnd = nullptr;6372if (p_parent_hwnd) {6373owner_hwnd = p_parent_hwnd;6374} else if (p_transient_parent != INVALID_WINDOW_ID) {6375if (!windows.has(p_transient_parent)) {6376ERR_PRINT("Condition \"!windows.has(p_transient_parent)\" is true.");6377p_transient_parent = INVALID_WINDOW_ID;6378} else {6379wd_transient_parent = &windows[p_transient_parent];6380if (p_exclusive) {6381owner_hwnd = wd_transient_parent->hWnd;6382}6383}6384}63856386WindowData &wd = windows[id];63876388wd.id = id;6389wd.hWnd = CreateWindowExW(6390dwExStyle,6391L"Engine", L"",6392dwStyle,6393WindowRect.left,6394WindowRect.top,6395WindowRect.right - WindowRect.left,6396WindowRect.bottom - WindowRect.top,6397owner_hwnd,6398nullptr,6399hInstance,6400// tunnel the WindowData we need to handle creation message6401// lifetime is ensured because we are still on the stack when this is6402// processed in the window proc6403reinterpret_cast<void *>(&wd));6404if (!wd.hWnd) {6405MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);6406windows.erase(id);6407ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window.");6408}64096410wd.parent_hwnd = p_parent_hwnd;64116412// Detach the input queue from the parent window.6413// This prevents the embedded window from waiting on the main window's input queue,6414// causing lags input lags when resizing or moving the main window.6415if (p_parent_hwnd) {6416DWORD mainThreadId = GetWindowThreadProcessId(owner_hwnd, nullptr);6417DWORD embeddedThreadId = GetCurrentThreadId();6418AttachThreadInput(embeddedThreadId, mainThreadId, FALSE);6419}64206421if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6422wd.fullscreen = true;6423if (p_mode == WINDOW_MODE_FULLSCREEN) {6424wd.multiwindow_fs = true;6425}6426}64276428if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6429// Save initial non-fullscreen rect.6430Rect2i srect = screen_get_usable_rect(rq_screen);6431Point2i wpos = p_rect.position;6432if (srect != Rect2i()) {6433wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);6434}64356436wd.pre_fs_rect.left = wpos.x + offset.x;6437wd.pre_fs_rect.right = wpos.x + p_rect.size.x + offset.x;6438wd.pre_fs_rect.top = wpos.y + offset.y;6439wd.pre_fs_rect.bottom = wpos.y + p_rect.size.y + offset.y;6440wd.pre_fs_valid = true;6441}64426443wd.exclusive = p_exclusive;6444if (wd_transient_parent) {6445wd.transient_parent = p_transient_parent;6446wd_transient_parent->transient_children.insert(id);6447}64486449wd.sharp_corners = p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT;6450{6451DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;6452::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));6453}64546455if (is_dark_mode_supported() && dark_title_available) {6456BOOL value = is_dark_mode();6457::DwmSetWindowAttribute(wd.hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));6458}64596460RECT real_client_rect;6461GetClientRect(wd.hWnd, &real_client_rect);64626463#ifdef RD_ENABLED6464if (rendering_context) {6465union {6466#ifdef VULKAN_ENABLED6467RenderingContextDriverVulkanWindows::WindowPlatformData vulkan;6468#endif6469#ifdef D3D12_ENABLED6470RenderingContextDriverD3D12::WindowPlatformData d3d12;6471#endif6472} wpd;6473#ifdef VULKAN_ENABLED6474if (rendering_driver == "vulkan") {6475wpd.vulkan.window = wd.hWnd;6476wpd.vulkan.instance = hInstance;6477}6478#endif6479#ifdef D3D12_ENABLED6480if (rendering_driver == "d3d12") {6481wpd.d3d12.window = wd.hWnd;6482}6483#endif6484if (rendering_context->window_create(id, &wpd) != OK) {6485ERR_PRINT(vformat("Failed to create %s window.", rendering_driver));6486memdelete(rendering_context);6487rendering_context = nullptr;6488windows.erase(id);6489return INVALID_WINDOW_ID;6490}64916492rendering_context->window_set_size(id, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top);6493rendering_context->window_set_vsync_mode(id, p_vsync_mode);6494wd.context_created = true;6495}6496#endif64976498#ifdef GLES3_ENABLED6499if (gl_manager_native) {6500if (gl_manager_native->window_create(id, wd.hWnd, hInstance, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top) != OK) {6501memdelete(gl_manager_native);6502gl_manager_native = nullptr;6503windows.erase(id);6504ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");6505}6506window_set_vsync_mode(p_vsync_mode, id);6507}65086509if (gl_manager_angle) {6510if (gl_manager_angle->window_create(id, nullptr, wd.hWnd, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top) != OK) {6511memdelete(gl_manager_angle);6512gl_manager_angle = nullptr;6513windows.erase(id);6514ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");6515}6516window_set_vsync_mode(p_vsync_mode, id);6517}6518#endif65196520RegisterTouchWindow(wd.hWnd, 0);6521DragAcceptFiles(wd.hWnd, true);65226523if ((tablet_get_current_driver() == "wintab") && wintab_available) {6524wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);6525wd.wtlc.lcOptions |= CXO_MESSAGES;6526wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;6527wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;6528wd.wtlc.lcPktMode = 0;6529wd.wtlc.lcOutOrgX = 0;6530wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;6531wd.wtlc.lcOutOrgY = 0;6532wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;6533wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);6534if (wd.wtctx) {6535wintab_WTEnable(wd.wtctx, true);6536AXIS pressure;6537if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {6538wd.min_pressure = int(pressure.axMin);6539wd.max_pressure = int(pressure.axMax);6540}6541AXIS orientation[3];6542if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {6543wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;6544}6545} else {6546print_verbose("WinTab context creation failed.");6547}6548} else {6549wd.wtctx = nullptr;6550}65516552if (p_mode == WINDOW_MODE_MAXIMIZED) {6553wd.maximized = true;6554wd.minimized = false;6555}65566557if (p_mode == WINDOW_MODE_MINIMIZED) {6558wd.maximized = false;6559wd.minimized = true;6560}65616562wd.last_pressure = 0;6563wd.last_pressure_update = 0;6564wd.last_tilt = Vector2();65656566IPropertyStore *prop_store;6567HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);6568if (hr == S_OK) {6569PROPVARIANT val;6570String appname;6571if (Engine::get_singleton()->is_editor_hint()) {6572appname = "Godot.GodotEditor." + String(GODOT_VERSION_FULL_CONFIG);6573} else {6574String name = GLOBAL_GET("application/config/name");6575String version = GLOBAL_GET("application/config/version");6576if (version.is_empty()) {6577version = "0";6578}6579String clean_app_name = name.to_pascal_case();6580for (int i = 0; i < clean_app_name.length(); i++) {6581if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {6582clean_app_name[i] = '_';6583}6584}6585clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");6586appname = "Godot." + clean_app_name + "." + version;6587}6588InitPropVariantFromString((PCWSTR)appname.utf16().get_data(), &val);6589prop_store->SetValue(PKEY_AppUserModel_ID, val);6590prop_store->Release();6591}65926593// IME.6594wd.im_himc = ImmGetContext(wd.hWnd);6595ImmAssociateContext(wd.hWnd, (HIMC) nullptr);65966597wd.im_position = Vector2();65986599if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN || p_mode == WINDOW_MODE_MAXIMIZED) {6600RECT r;6601GetClientRect(wd.hWnd, &r);6602ClientToScreen(wd.hWnd, (POINT *)&r.left);6603ClientToScreen(wd.hWnd, (POINT *)&r.right);6604wd.last_pos = Point2i(r.left, r.top) - _get_screens_origin();6605wd.width = r.right - r.left - off_x;6606wd.height = r.bottom - r.top;6607} else {6608wd.last_pos = p_rect.position;6609wd.width = p_rect.size.width;6610wd.height = p_rect.size.height;6611}66126613wd.create_completed = true;6614// Set size of maximized borderless window (by default it covers the entire screen).6615if (!p_parent_hwnd && p_mode == WINDOW_MODE_MAXIMIZED && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {6616SetWindowPos(wd.hWnd, HWND_TOP, usable_rect.position.x - off_x, usable_rect.position.y, usable_rect.size.width + off_x, usable_rect.size.height, SWP_NOZORDER | SWP_NOACTIVATE);6617}6618_update_window_mouse_passthrough(id);6619window_id_counter++;6620}66216622return id;6623}66246625BitField<DisplayServerWindows::DriverID> DisplayServerWindows::tested_drivers = 0;66266627// WinTab API.6628bool DisplayServerWindows::wintab_available = false;6629WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr;6630WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr;6631WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr;6632WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;6633WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;66346635// UXTheme API.6636bool DisplayServerWindows::dark_title_available = false;6637bool DisplayServerWindows::use_legacy_dark_mode_before_20H1 = false;6638bool DisplayServerWindows::ux_theme_available = false;6639ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;6640GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;6641GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr;6642GetImmersiveUserColorSetPreferencePtr DisplayServerWindows::GetImmersiveUserColorSetPreference = nullptr;66436644Vector2i _get_device_ids(const String &p_device_name) {6645if (p_device_name.is_empty()) {6646return Vector2i();6647}66486649REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID6650REFIID uuid = IID_IWbemLocator; // Interface UUID6651IWbemLocator *wbemLocator = nullptr; // to get the services6652IWbemServices *wbemServices = nullptr; // to get the class6653IEnumWbemClassObject *iter = nullptr;6654IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.66556656HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);6657if (hr != S_OK) {6658return Vector2i();6659}6660BSTR resource_name = SysAllocString(L"root\\CIMV2");6661hr = wbemLocator->ConnectServer(resource_name, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemServices);6662SysFreeString(resource_name);66636664SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices`6665if (hr != S_OK) {6666SAFE_RELEASE(wbemServices)6667return Vector2i();6668}66696670Vector2i ids;66716672const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", p_device_name);6673BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data());6674BSTR query_lang = SysAllocString(L"WQL");6675hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, nullptr, &iter);6676SysFreeString(query_lang);6677SysFreeString(query);6678if (hr == S_OK) {6679ULONG resultCount;6680hr = iter->Next(5000, 1, pnpSDriverObject, &resultCount); // Get exactly 1. Wait max 5 seconds.66816682if (hr == S_OK && resultCount > 0) {6683VARIANT did;6684VariantInit(&did);6685BSTR object_name = SysAllocString(L"DeviceID");6686hr = pnpSDriverObject[0]->Get(object_name, 0, &did, nullptr, nullptr);6687SysFreeString(object_name);6688if (hr == S_OK) {6689String device_id = String(V_BSTR(&did));6690ids.x = device_id.get_slicec('&', 0).lstrip("PCI\\VEN_").hex_to_int();6691ids.y = device_id.get_slicec('&', 1).lstrip("DEV_").hex_to_int();6692}66936694for (ULONG i = 0; i < resultCount; i++) {6695SAFE_RELEASE(pnpSDriverObject[i])6696}6697}6698}66996700SAFE_RELEASE(wbemServices)6701SAFE_RELEASE(iter)67026703return ids;6704}67056706bool DisplayServerWindows::is_dark_mode_supported() const {6707return ux_theme_available;6708}67096710bool DisplayServerWindows::is_dark_mode() const {6711return ux_theme_available && ShouldAppsUseDarkMode();6712}67136714Color DisplayServerWindows::get_accent_color() const {6715if (!ux_theme_available) {6716return Color(0, 0, 0, 0);6717}67186719int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"), false, 0);6720return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);6721}67226723Color DisplayServerWindows::get_base_color() const {6724if (!ux_theme_available) {6725return Color(0, 0, 0, 0);6726}67276728int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(ShouldAppsUseDarkMode() ? L"ImmersiveDarkChromeMediumLow" : L"ImmersiveLightChromeMediumLow"), false, 0);6729return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);6730}67316732void DisplayServerWindows::set_system_theme_change_callback(const Callable &p_callable) {6733system_theme_changed = p_callable;6734}67356736int DisplayServerWindows::tablet_get_driver_count() const {6737return tablet_drivers.size();6738}67396740String DisplayServerWindows::tablet_get_driver_name(int p_driver) const {6741if (p_driver < 0 || p_driver >= tablet_drivers.size()) {6742return "";6743} else {6744return tablet_drivers[p_driver];6745}6746}67476748String DisplayServerWindows::tablet_get_current_driver() const {6749return tablet_driver;6750}67516752void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {6753if (tablet_get_driver_count() == 0) {6754return;6755}67566757String driver = p_driver;6758if (driver == "auto") {6759if (!winink_disabled) {6760driver = "winink";6761} else if (wintab_available) {6762driver = "wintab";6763} else {6764driver = "dummy";6765}6766}67676768bool found = false;6769for (int i = 0; i < tablet_get_driver_count(); i++) {6770if (driver == tablet_get_driver_name(i)) {6771found = true;6772}6773}6774if (found) {6775_update_tablet_ctx(tablet_driver, driver);6776tablet_driver = driver;6777} else {6778ERR_PRINT("Unknown tablet driver " + p_driver + ".");6779}6780}67816782DisplayServerWindows::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) {6783KeyMappingWindows::initialize();67846785tested_drivers.clear();67866787drop_events = false;6788key_event_pos = 0;67896790hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance();67916792pressrc = 0;6793old_invalid = true;6794mouse_mode = MOUSE_MODE_VISIBLE;67956796rendering_driver = p_rendering_driver;67976798// Init TTS6799bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");6800if (tts_enabled) {6801initialize_tts();6802}6803native_menu = memnew(NativeMenuWindows);68046805#ifdef ACCESSKIT_ENABLED6806if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {6807accessibility_driver = memnew(AccessibilityDriverAccessKit);6808if (accessibility_driver->init() != OK) {6809if (OS::get_singleton()->is_stdout_verbose()) {6810ERR_PRINT("Can't create an accessibility driver, accessibility support disabled!");6811}6812memdelete(accessibility_driver);6813accessibility_driver = nullptr;6814}6815}6816#endif68176818// Enforce default keep screen on value.6819screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));68206821// Load Windows version info.6822ZeroMemory(&os_ver, sizeof(OSVERSIONINFOW));6823os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);68246825HMODULE nt_lib = LoadLibraryW(L"ntdll.dll");6826if (nt_lib) {6827WineGetVersionPtr 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.6828if (!wine_get_version) {6829RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)(void *)GetProcAddress(nt_lib, "RtlGetVersion");6830if (RtlGetVersion) {6831RtlGetVersion(&os_ver);6832}6833}6834FreeLibrary(nt_lib);6835}68366837// Load UXTheme.6838if (os_ver.dwBuildNumber >= 10240) { // Not available on Wine, use only if real Windows 10/11 detected.6839HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");6840if (ux_theme_lib) {6841ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132));6842GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));6843GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));6844GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));6845if (os_ver.dwBuildNumber >= 17763) { // Windows 10 Redstone 5 (1809)+ only.6846AllowDarkModeForAppPtr AllowDarkModeForApp = nullptr;6847SetPreferredAppModePtr SetPreferredAppMode = nullptr;6848FlushMenuThemesPtr FlushMenuThemes = nullptr;6849if (os_ver.dwBuildNumber < 18362) { // Windows 10 Redstone 5 (1809) and 19H1 (1903) only.6850AllowDarkModeForApp = (AllowDarkModeForAppPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));6851} else { // Windows 10 19H2 (1909)+ only.6852SetPreferredAppMode = (SetPreferredAppModePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));6853FlushMenuThemes = (FlushMenuThemesPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));6854}6855RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyState = (RefreshImmersiveColorPolicyStatePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(104));6856if (ShouldAppsUseDarkMode) {6857bool dark_mode = ShouldAppsUseDarkMode();6858if (SetPreferredAppMode) {6859SetPreferredAppMode(dark_mode ? APPMODE_ALLOWDARK : APPMODE_DEFAULT);6860} else if (AllowDarkModeForApp) {6861AllowDarkModeForApp(dark_mode);6862}6863if (RefreshImmersiveColorPolicyState) {6864RefreshImmersiveColorPolicyState();6865}6866if (FlushMenuThemes) {6867FlushMenuThemes();6868}6869}6870}68716872ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;6873if (os_ver.dwBuildNumber >= 18363) {6874dark_title_available = true;6875if (os_ver.dwBuildNumber < 19041) {6876use_legacy_dark_mode_before_20H1 = true;6877}6878}6879}6880}68816882tablet_drivers.push_back("auto");6883tablet_drivers.push_back("winink");68846885// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.6886HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");6887if (wintab_lib) {6888wintab_WTOpen = (WTOpenPtr)(void *)GetProcAddress(wintab_lib, "WTOpenW");6889wintab_WTClose = (WTClosePtr)(void *)GetProcAddress(wintab_lib, "WTClose");6890wintab_WTInfo = (WTInfoPtr)(void *)GetProcAddress(wintab_lib, "WTInfoW");6891wintab_WTPacket = (WTPacketPtr)(void *)GetProcAddress(wintab_lib, "WTPacket");6892wintab_WTEnable = (WTEnablePtr)(void *)GetProcAddress(wintab_lib, "WTEnable");68936894wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable;6895}68966897if (wintab_available) {6898tablet_drivers.push_back("wintab");6899}69006901tablet_drivers.push_back("dummy");69026903String wacom_cfg = OS::get_singleton()->get_config_path().path_join("WTablet").path_join("Wacom_Tablet.dat");6904if (FileAccess::exists(wacom_cfg)) {6905Ref<XMLParser> parser;6906parser.instantiate();6907if (parser->open(wacom_cfg) == OK) {6908while (parser->read() == OK) {6909if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {6910continue;6911}6912if (parser->get_node_name() == "WinUseInk") {6913parser->read();6914if (parser->get_node_type() == XMLParser::NODE_TEXT) {6915winink_disabled = (parser->get_node_data().to_lower().strip_edges() != "true");6916print_verbose(vformat("Wacom tablet config found at \"%s\", Windows Ink support is %s.", wacom_cfg, winink_disabled ? "disabled" : "enabled"));6917break;6918}6919}6920}6921}6922}69236924if (OS::get_singleton()->is_hidpi_allowed()) {6925SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE);6926}69276928HMODULE comctl32 = LoadLibraryW(L"comctl32.dll");6929if (comctl32) {6930typedef BOOL(WINAPI * InitCommonControlsExPtr)(_In_ const INITCOMMONCONTROLSEX *picce);6931InitCommonControlsExPtr init_common_controls_ex = (InitCommonControlsExPtr)(void *)GetProcAddress(comctl32, "InitCommonControlsEx");69326933// Fails if the incorrect version was loaded. Probably not a big enough deal to print an error about.6934if (init_common_controls_ex) {6935INITCOMMONCONTROLSEX icc = {};6936icc.dwICC = ICC_STANDARD_CLASSES;6937icc.dwSize = sizeof(INITCOMMONCONTROLSEX);6938if (!init_common_controls_ex(&icc)) {6939WARN_PRINT("Unable to initialize Windows common controls. Native dialogs may not work properly.");6940}6941}6942FreeLibrary(comctl32);6943}69446945OleInitialize(nullptr);69466947memset(&wc, 0, sizeof(WNDCLASSEXW));6948wc.cbSize = sizeof(WNDCLASSEXW);6949wc.style = CS_OWNDC | CS_DBLCLKS;6950wc.lpfnWndProc = (WNDPROC)::WndProc;6951wc.cbClsExtra = 0;6952wc.cbWndExtra = 0;6953wc.hInstance = hInstance ? hInstance : GetModuleHandle(nullptr);6954wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO);6955wc.hCursor = nullptr;6956wc.hbrBackground = nullptr;6957wc.lpszMenuName = nullptr;6958wc.lpszClassName = L"Engine";69596960if (!RegisterClassExW(&wc)) {6961r_error = ERR_UNAVAILABLE;6962return;6963}69646965_register_raw_input_devices(INVALID_WINDOW_ID);69666967// Init context and rendering device.6968if (rendering_driver == "dummy") {6969RasterizerDummy::make_current();6970}69716972#if defined(RD_ENABLED)6973[[maybe_unused]] bool fallback_to_vulkan = GLOBAL_GET("rendering/rendering_device/fallback_to_vulkan");6974[[maybe_unused]] bool fallback_to_d3d12 = GLOBAL_GET("rendering/rendering_device/fallback_to_d3d12");69756976#if defined(VULKAN_ENABLED)6977if (rendering_driver == "vulkan") {6978rendering_context = memnew(RenderingContextDriverVulkanWindows);6979tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);6980}6981#else6982fallback_to_d3d12 = true; // Always enable fallback if engine was built w/o other driver support.6983#endif6984#if defined(D3D12_ENABLED)6985if (rendering_driver == "d3d12") {6986rendering_context = memnew(RenderingContextDriverD3D12);6987tested_drivers.set_flag(DRIVER_ID_RD_D3D12);6988}6989#else6990fallback_to_vulkan = true; // Always enable fallback if engine was built w/o other driver support.6991#endif69926993if (rendering_context) {6994if (rendering_context->initialize() != OK) {6995bool failed = true;6996#if defined(VULKAN_ENABLED)6997if (failed && fallback_to_vulkan && rendering_driver != "vulkan") {6998memdelete(rendering_context);6999rendering_context = memnew(RenderingContextDriverVulkanWindows);7000tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);7001if (rendering_context->initialize() == OK) {7002WARN_PRINT("Your video card drivers seem not to support Direct3D 12, switching to Vulkan.");7003rendering_driver = "vulkan";7004OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);7005failed = false;7006}7007}7008#endif7009#if defined(D3D12_ENABLED)7010if (failed && fallback_to_d3d12 && rendering_driver != "d3d12") {7011memdelete(rendering_context);7012rendering_context = memnew(RenderingContextDriverD3D12);7013tested_drivers.set_flag(DRIVER_ID_RD_D3D12);7014if (rendering_context->initialize() == OK) {7015WARN_PRINT("Your video card drivers seem not to support Vulkan, switching to Direct3D 12.");7016rendering_driver = "d3d12";7017OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);7018failed = false;7019}7020}7021#endif7022#if defined(GLES3_ENABLED)7023bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");7024if (failed && fallback_to_opengl3 && rendering_driver != "opengl3") {7025memdelete(rendering_context);7026rendering_context = nullptr;7027tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);7028WARN_PRINT("Your video card drivers seem not to support Direct3D 12 or Vulkan, switching to OpenGL 3.");7029rendering_driver = "opengl3";7030OS::get_singleton()->set_current_rendering_method("gl_compatibility");7031OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);7032failed = false;7033}7034#endif7035if (failed) {7036memdelete(rendering_context);7037rendering_context = nullptr;7038r_error = ERR_UNAVAILABLE;7039return;7040}7041}7042}7043#endif70447045#if defined(GLES3_ENABLED)70467047bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_angle");7048bool show_warning = true;70497050if (rendering_driver == "opengl3") {7051// There's no native OpenGL drivers on Windows for ARM, always enable fallback.7052#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)7053fallback = true;7054show_warning = false;7055#else7056typedef BOOL(WINAPI * IsWow64Process2Ptr)(HANDLE, USHORT *, USHORT *);70577058IsWow64Process2Ptr IsWow64Process2 = (IsWow64Process2Ptr)(void *)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process2");7059if (IsWow64Process2) {7060USHORT process_arch = 0;7061USHORT machine_arch = 0;7062if (!IsWow64Process2(GetCurrentProcess(), &process_arch, &machine_arch)) {7063machine_arch = 0;7064}7065if (machine_arch == 0xAA64) {7066fallback = true;7067show_warning = false;7068}7069}7070#endif7071}70727073bool gl_supported = true;7074if (fallback && (rendering_driver == "opengl3")) {7075Dictionary gl_info = detect_wgl();70767077bool force_angle = false;7078gl_supported = gl_info["version"].operator int() >= 30003;70797080Vector2i device_id = _get_device_ids(gl_info["name"]);7081Array device_list = GLOBAL_GET("rendering/gl_compatibility/force_angle_on_devices");7082for (int i = 0; i < device_list.size(); i++) {7083const Dictionary &device = device_list[i];7084if (device.has("vendor") && device.has("name")) {7085const String &vendor = device["vendor"];7086const String &name = device["name"];7087if (device_id != Vector2i() && vendor.begins_with("0x") && name.begins_with("0x") && device_id.x == vendor.lstrip("0x").hex_to_int() && device_id.y == name.lstrip("0x").hex_to_int()) {7088// Check vendor/device IDs.7089force_angle = true;7090break;7091} else if (gl_info["vendor"].operator String().to_upper().contains(vendor.to_upper()) && (name == "*" || gl_info["name"].operator String().to_upper().contains(name.to_upper()))) {7092// Check vendor/device names.7093force_angle = true;7094break;7095}7096}7097}70987099if (force_angle || (gl_info["version"].operator int() < 30003)) {7100tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);7101if (show_warning) {7102if (gl_info["version"].operator int() < 30003) {7103WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE.");7104} else {7105WARN_PRINT("Your video card drivers are known to have low quality OpenGL 3.3 support, switching to ANGLE.");7106}7107}7108rendering_driver = "opengl3_angle";7109OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);7110}7111}71127113if (rendering_driver == "opengl3_angle") {7114gl_manager_angle = memnew(GLManagerANGLE_Windows);7115tested_drivers.set_flag(DRIVER_ID_COMPAT_ANGLE_D3D11);71167117if (gl_manager_angle->initialize() != OK) {7118memdelete(gl_manager_angle);7119gl_manager_angle = nullptr;7120bool fallback_to_native = GLOBAL_GET("rendering/gl_compatibility/fallback_to_native");7121if (fallback_to_native && gl_supported) {7122#ifdef EGL_STATIC7123WARN_PRINT("Your video card drivers seem not to support GLES3 / ANGLE, switching to native OpenGL.");7124#else7125WARN_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.");7126#endif7127rendering_driver = "opengl3";7128} else {7129r_error = ERR_UNAVAILABLE;7130ERR_FAIL_MSG("Could not initialize ANGLE OpenGL.");7131}7132}7133}7134if (rendering_driver == "opengl3") {7135gl_manager_native = memnew(GLManagerNative_Windows);7136tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);71377138if (gl_manager_native->initialize() != OK) {7139memdelete(gl_manager_native);7140gl_manager_native = nullptr;7141r_error = ERR_UNAVAILABLE;7142ERR_FAIL_MSG("Could not initialize native OpenGL.");7143}7144}71457146if (rendering_driver == "opengl3") {7147RasterizerGLES3::make_current(true);7148}7149if (rendering_driver == "opengl3_angle") {7150RasterizerGLES3::make_current(false);7151}7152#endif7153String appname;7154if (Engine::get_singleton()->is_editor_hint()) {7155appname = "Godot.GodotEditor." + String(GODOT_VERSION_FULL_CONFIG);7156} else {7157String name = GLOBAL_GET("application/config/name");7158String version = GLOBAL_GET("application/config/version");7159if (version.is_empty()) {7160version = "0";7161}7162String clean_app_name = name.to_pascal_case();7163for (int i = 0; i < clean_app_name.length(); i++) {7164if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {7165clean_app_name[i] = '_';7166}7167}7168clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");7169appname = "Godot." + clean_app_name + "." + version;71707171#ifndef TOOLS_ENABLED7172// Set for exported projects only.7173HKEY key;7174if (RegOpenKeyW(HKEY_CURRENT_USER_LOCAL_SETTINGS, L"Software\\Microsoft\\Windows\\Shell\\MuiCache", &key) == ERROR_SUCCESS) {7175Char16String cs_name = name.utf16();7176String value_name = OS::get_singleton()->get_executable_path().replace_char('/', '\\') + ".FriendlyAppName";7177RegSetValueExW(key, (LPCWSTR)value_name.utf16().get_data(), 0, REG_SZ, (const BYTE *)cs_name.get_data(), cs_name.size() * sizeof(WCHAR));7178RegCloseKey(key);7179}7180#endif7181}7182SetCurrentProcessExplicitAppUserModelID((PCWSTR)appname.utf16().get_data());71837184mouse_monitor = SetWindowsHookEx(WH_MOUSE, ::MouseProc, nullptr, GetCurrentThreadId());71857186Point2i window_position;7187if (p_position != nullptr) {7188window_position = *p_position;7189} else {7190if (p_screen == SCREEN_OF_MAIN_WINDOW) {7191p_screen = SCREEN_PRIMARY;7192}7193Rect2i scr_rect = screen_get_usable_rect(p_screen);7194window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2;7195}71967197HWND parent_hwnd = NULL;7198if (p_parent_window) {7199// Parented window.7200parent_hwnd = (HWND)p_parent_window;7201}72027203WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID, parent_hwnd);7204if (main_window == INVALID_WINDOW_ID) {7205r_error = ERR_UNAVAILABLE;7206ERR_FAIL_MSG("Failed to create main window.");7207}72087209#ifdef SDL_ENABLED7210joypad_sdl = memnew(JoypadSDL(windows[MAIN_WINDOW_ID].hWnd));7211if (joypad_sdl->initialize() != OK) {7212ERR_PRINT("Couldn't initialize SDL joypad input driver.");7213memdelete(joypad_sdl);7214joypad_sdl = nullptr;7215}7216#endif72177218for (int i = 0; i < WINDOW_FLAG_MAX; i++) {7219if (p_flags & (1 << i)) {7220window_set_flag(WindowFlags(i), true, main_window);7221}7222}72237224windows[MAIN_WINDOW_ID].initialized = true;72257226#ifdef ACCESSKIT_ENABLED7227if (accessibility_screen_reader_active()) {7228_THREAD_SAFE_LOCK_7229uint64_t time_wait = OS::get_singleton()->get_ticks_msec();7230while (true) {7231MSG msg = {};7232while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {7233TranslateMessage(&msg);7234DispatchMessageW(&msg);7235}72367237uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_wait;7238if (delta > 500 || get_object_received) {7239break;7240}7241}7242_THREAD_SAFE_UNLOCK_7243}7244#endif72457246#if defined(RD_ENABLED)7247if (rendering_context) {7248rendering_device = memnew(RenderingDevice);7249if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {7250memdelete(rendering_device);7251rendering_device = nullptr;7252memdelete(rendering_context);7253rendering_context = nullptr;7254r_error = ERR_UNAVAILABLE;7255return;7256}7257rendering_device->screen_create(MAIN_WINDOW_ID);72587259RendererCompositorRD::make_current();7260}7261#endif72627263if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) {7264// Increase priority for projects that are not in low-processor mode (typically games)7265// to reduce the risk of frame stuttering.7266// This is not done for the editor to prevent importers or resource bakers7267// from making the system unresponsive.7268SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);7269DWORD index = 0;7270HANDLE handle = AvSetMmThreadCharacteristicsW(L"Games", &index);7271if (handle) {7272AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);7273}72747275// This is needed to make sure that background work does not starve the main thread.7276// This is only setting the priority of this thread, not the whole process.7277SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);7278}72797280cursor_shape = CURSOR_ARROW;72817282_update_real_mouse_position(MAIN_WINDOW_ID);72837284r_error = OK;72857286static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);7287Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);7288}72897290Vector<String> DisplayServerWindows::get_rendering_drivers_func() {7291Vector<String> drivers;72927293#ifdef VULKAN_ENABLED7294drivers.push_back("vulkan");7295#endif7296#ifdef D3D12_ENABLED7297drivers.push_back("d3d12");7298#endif7299#ifdef GLES3_ENABLED7300drivers.push_back("opengl3");7301drivers.push_back("opengl3_angle");7302#endif7303drivers.push_back("dummy");73047305return drivers;7306}73077308DisplayServer *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) {7309DisplayServer *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));7310if (r_error != OK) {7311if (tested_drivers == 0) {7312OS::get_singleton()->alert("Failed to register the window class.", "Unable to initialize DisplayServer");7313} else if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN) || tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {7314Vector<String> drivers;7315if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN)) {7316drivers.push_back("Vulkan");7317}7318if (tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {7319drivers.push_back("Direct3D 12");7320}7321String executable_name = OS::get_singleton()->get_executable_path().get_file();7322OS::get_singleton()->alert(7323vformat("Your video card drivers seem not to support the required %s version.\n\n"7324"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"7325"You can enable the OpenGL 3 driver by starting the engine from the\n"7326"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"7327"If you have recently updated your video card drivers, try rebooting.",7328String(" or ").join(drivers),7329executable_name),7330"Unable to initialize video driver");7331} else {7332Vector<String> drivers;7333if (tested_drivers.has_flag(DRIVER_ID_COMPAT_OPENGL3)) {7334drivers.push_back("OpenGL 3.3");7335}7336if (tested_drivers.has_flag(DRIVER_ID_COMPAT_ANGLE_D3D11)) {7337drivers.push_back("Direct3D 11");7338}7339OS::get_singleton()->alert(7340vformat(7341"Your video card drivers seem not to support the required %s version.\n\n"7342"If possible, consider updating your video card drivers.\n\n"7343"If you have recently updated your video card drivers, try rebooting.",7344String(" or ").join(drivers)),7345"Unable to initialize video driver");7346}7347}7348return ds;7349}73507351void DisplayServerWindows::register_windows_driver() {7352register_create_function("windows", create_func, get_rendering_drivers_func);7353}73547355DisplayServerWindows::~DisplayServerWindows() {7356LocalVector<List<FileDialogData *>::Element *> to_remove;7357for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {7358FileDialogData *fd = E->get();7359if (fd->listener_thread.is_started()) {7360fd->close_requested.set();7361fd->listener_thread.wait_to_finish();7362}7363to_remove.push_back(E);7364}7365for (List<FileDialogData *>::Element *E : to_remove) {7366memdelete(E->get());7367E->erase();7368}73697370#ifdef SDL_ENABLED7371if (joypad_sdl) {7372memdelete(joypad_sdl);7373}7374#endif7375touch_state.clear();73767377cursors_cache.clear();73787379// Destroy all status indicators.7380for (HashMap<IndicatorID, IndicatorData>::Iterator E = indicators.begin(); E; ++E) {7381NOTIFYICONDATAW ndat;7382ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));7383ndat.cbSize = sizeof(NOTIFYICONDATAW);7384ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;7385ndat.uID = E->key;7386ndat.uVersion = NOTIFYICON_VERSION;73877388Shell_NotifyIconW(NIM_DELETE, &ndat);7389}73907391if (mouse_monitor) {7392UnhookWindowsHookEx(mouse_monitor);7393}73947395if (user_proc) {7396SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);7397}73987399// Close power request handle.7400screen_set_keep_on(false);74017402if (native_menu) {7403memdelete(native_menu);7404native_menu = nullptr;7405}74067407#ifdef GLES3_ENABLED7408// destroy windows .. NYI?7409// FIXME wglDeleteContext is never called7410#endif74117412if (windows.has(MAIN_WINDOW_ID)) {7413#ifdef RD_ENABLED7414if (rendering_device) {7415rendering_device->screen_free(MAIN_WINDOW_ID);7416}74177418if (rendering_context) {7419rendering_context->window_destroy(MAIN_WINDOW_ID);7420}7421#endif7422if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) {7423wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);7424windows[MAIN_WINDOW_ID].wtctx = nullptr;7425}74267427if (windows[MAIN_WINDOW_ID].drop_target != nullptr) {7428RevokeDragDrop(windows[MAIN_WINDOW_ID].hWnd);7429windows[MAIN_WINDOW_ID].drop_target->Release();7430}74317432DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);7433}74347435#ifdef RD_ENABLED7436if (rendering_device) {7437memdelete(rendering_device);7438rendering_device = nullptr;7439}74407441if (rendering_context) {7442memdelete(rendering_context);7443rendering_context = nullptr;7444}7445#endif74467447if (restore_mouse_trails > 1) {7448SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);7449}7450#ifdef GLES3_ENABLED7451if (gl_manager_angle) {7452memdelete(gl_manager_angle);7453gl_manager_angle = nullptr;7454}7455if (gl_manager_native) {7456memdelete(gl_manager_native);7457gl_manager_native = nullptr;7458}7459#endif7460#ifdef ACCESSKIT_ENABLED7461if (accessibility_driver) {7462memdelete(accessibility_driver);7463}7464#endif7465if (tts) {7466memdelete(tts);7467}74687469OleUninitialize();7470}747174727473