Path: blob/master/platform/windows/display_server_windows.cpp
11352 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/file_access.h"39#include "core/io/marshalls.h"40#include "core/io/xml_parser.h"41#include "core/os/main_loop.h"42#include "core/version.h"43#include "drivers/png/png_driver_common.h"44#include "main/main.h"45#include "scene/resources/texture.h"4647#ifdef SDL_ENABLED48#include "drivers/sdl/joypad_sdl.h"49#endif5051#include "servers/rendering/dummy/rasterizer_dummy.h"5253#if defined(VULKAN_ENABLED)54#include "rendering_context_driver_vulkan_windows.h"55#endif56#if defined(D3D12_ENABLED)57#include "drivers/d3d12/rendering_context_driver_d3d12.h"58#endif59#if defined(GLES3_ENABLED)60#include "drivers/gles3/rasterizer_gles3.h"61#endif6263#if defined(ACCESSKIT_ENABLED)64#include "drivers/accesskit/accessibility_driver_accesskit.h"65#endif6667#include <avrt.h>68#include <dwmapi.h>69#include <propkey.h>70#include <propvarutil.h>71#include <shellapi.h>72#include <shellscalingapi.h>73#include <shlwapi.h>74#include <shobjidl.h>75#include <wbemcli.h>7677#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE78#define DWMWA_USE_IMMERSIVE_DARK_MODE 2079#endif8081#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H182#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 1983#endif8485#ifndef DWMWA_WINDOW_CORNER_PREFERENCE86#define DWMWA_WINDOW_CORNER_PREFERENCE 3387#endif8889#ifndef DWMWCP_DEFAULT90#define DWMWCP_DEFAULT 091#endif9293#ifndef DWMWCP_DONOTROUND94#define DWMWCP_DONOTROUND 195#endif9697#define WM_INDICATOR_CALLBACK_MESSAGE (WM_USER + 1)9899int constexpr FS_TRANSP_BORDER = 2;100101static String format_error_message(DWORD id) {102LPWSTR messageBuffer = nullptr;103size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,104nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);105106String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size);107108LocalFree(messageBuffer);109110return msg;111}112113static void track_mouse_leave_event(HWND hWnd) {114TRACKMOUSEEVENT tme;115tme.cbSize = sizeof(TRACKMOUSEEVENT);116tme.dwFlags = TME_LEAVE;117tme.hwndTrack = hWnd;118tme.dwHoverTime = HOVER_DEFAULT;119TrackMouseEvent(&tme);120}121122bool DisplayServerWindows::has_feature(Feature p_feature) const {123switch (p_feature) {124#ifndef DISABLE_DEPRECATED125case FEATURE_GLOBAL_MENU: {126return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));127} break;128#endif129case FEATURE_SUBWINDOWS:130case FEATURE_TOUCHSCREEN:131case FEATURE_MOUSE:132case FEATURE_MOUSE_WARP:133case FEATURE_CLIPBOARD:134case FEATURE_CURSOR_SHAPE:135case FEATURE_CUSTOM_CURSOR_SHAPE:136case FEATURE_IME:137case FEATURE_WINDOW_TRANSPARENCY:138case FEATURE_HIDPI:139case FEATURE_ICON:140case FEATURE_NATIVE_ICON:141case FEATURE_NATIVE_DIALOG:142case FEATURE_NATIVE_DIALOG_INPUT:143case FEATURE_NATIVE_DIALOG_FILE:144case FEATURE_NATIVE_DIALOG_FILE_EXTRA:145//case FEATURE_NATIVE_DIALOG_FILE_MIME:146case FEATURE_SWAP_BUFFERS:147case FEATURE_KEEP_SCREEN_ON:148case FEATURE_TEXT_TO_SPEECH:149case FEATURE_SCREEN_CAPTURE:150case FEATURE_STATUS_INDICATOR:151case FEATURE_WINDOW_EMBEDDING:152case FEATURE_WINDOW_DRAG:153return true;154case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:155return (os_ver.dwBuildNumber >= 19041); // Fully supported on Windows 10 Vibranium R1 (2004)+ only, captured as black rect on older versions.156case FEATURE_EMOJI_AND_SYMBOL_PICKER:157return (os_ver.dwBuildNumber >= 17134); // Windows 10 Redstone 4 (1803)+ only.158#ifdef ACCESSKIT_ENABLED159case FEATURE_ACCESSIBILITY_SCREEN_READER: {160return (accessibility_driver != nullptr);161} break;162#endif163default:164return false;165}166}167168String DisplayServerWindows::get_name() const {169return "Windows";170}171172void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {173if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {174// Hide cursor before moving.175if (hCursor == nullptr) {176hCursor = SetCursor(nullptr);177} else {178SetCursor(nullptr);179}180}181182if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {183// Mouse is grabbed (captured or confined).184WindowID window_id = _get_focused_window_or_popup();185if (!windows.has(window_id)) {186window_id = MAIN_WINDOW_ID;187}188189WindowData &wd = windows[window_id];190191int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;192193RECT clipRect;194GetClientRect(wd.hWnd, &clipRect);195clipRect.right -= off_x;196ClientToScreen(wd.hWnd, (POINT *)&clipRect.left);197ClientToScreen(wd.hWnd, (POINT *)&clipRect.right);198ClipCursor(&clipRect);199if (p_mode == MOUSE_MODE_CAPTURED) {200center = window_get_size() / 2;201POINT pos = { (int)center.x, (int)center.y };202ClientToScreen(wd.hWnd, &pos);203SetCursorPos(pos.x, pos.y);204SetCapture(wd.hWnd);205206_register_raw_input_devices(window_id);207}208} else {209// Mouse is free to move around (not captured or confined).210// When the user is moving a window, it's important to not ReleaseCapture because it will cause211// the window movement to stop and if the user tries to move the Windows when it's not activated,212// it will prevent the window movement. It's probably impossible to move the Window while it's captured anyway.213if (!_has_moving_window()) {214ReleaseCapture();215}216ClipCursor(nullptr);217218_register_raw_input_devices(INVALID_WINDOW_ID);219}220221if (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED) {222// Show cursor.223CursorShape c = cursor_shape;224cursor_shape = CURSOR_MAX;225cursor_set_shape(c);226}227}228229DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() const {230const List<WindowID>::Element *E = popup_list.back();231if (E) {232return E->get();233}234235return last_focused_window;236}237238bool DisplayServerWindows::_has_moving_window() const {239for (const KeyValue<WindowID, WindowData> &E : windows) {240if (E.value.move_timer_id) {241return true;242}243}244return false;245}246247void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) {248use_raw_input = true;249250RAWINPUTDEVICE rid[2] = {};251rid[0].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC252rid[0].usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE253rid[0].dwFlags = 0;254255rid[1].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC256rid[1].usUsage = 0x06; // HID_USAGE_GENERIC_KEYBOARD257rid[1].dwFlags = 0;258259if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) {260// Follow the defined window261rid[0].hwndTarget = windows[p_target_window].hWnd;262rid[1].hwndTarget = windows[p_target_window].hWnd;263} else {264// Follow the keyboard focus265rid[0].hwndTarget = nullptr;266rid[1].hwndTarget = nullptr;267}268269if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) {270// Registration failed.271use_raw_input = false;272}273}274275void DisplayServerWindows::initialize_tts() const {276const_cast<DisplayServerWindows *>(this)->tts = memnew(TTS_Windows);277}278279bool DisplayServerWindows::tts_is_speaking() const {280if (unlikely(!tts)) {281initialize_tts();282}283ERR_FAIL_NULL_V(tts, false);284return tts->is_speaking();285}286287bool DisplayServerWindows::tts_is_paused() const {288if (unlikely(!tts)) {289initialize_tts();290}291ERR_FAIL_NULL_V(tts, false);292return tts->is_paused();293}294295TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const {296if (unlikely(!tts)) {297initialize_tts();298}299ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());300return tts->get_voices();301}302303void 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) {304if (unlikely(!tts)) {305initialize_tts();306}307ERR_FAIL_NULL(tts);308tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);309}310311void DisplayServerWindows::tts_pause() {312if (unlikely(!tts)) {313initialize_tts();314}315ERR_FAIL_NULL(tts);316tts->pause();317}318319void DisplayServerWindows::tts_resume() {320if (unlikely(!tts)) {321initialize_tts();322}323ERR_FAIL_NULL(tts);324tts->resume();325}326327void DisplayServerWindows::tts_stop() {328if (unlikely(!tts)) {329initialize_tts();330}331ERR_FAIL_NULL(tts);332tts->stop();333}334335Error 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) {336return _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);337}338339Error 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) {340return _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);341}342343GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Wnon-virtual-dtor") // Silence warning due to a COM API weirdness.344345class FileDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents {346LONG ref_count = 1;347int ctl_id = 1;348349HashMap<int, String> ctls;350Dictionary selected;351String root;352353public:354// IUnknown methods355HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) {356static const QITAB qit[] = {357#ifdef __MINGW32__358{ &__uuidof(IFileDialogEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogEvents, FileDialogEventHandler)) },359{ &__uuidof(IFileDialogControlEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogControlEvents, FileDialogEventHandler)) },360#else361QITABENT(FileDialogEventHandler, IFileDialogEvents),362QITABENT(FileDialogEventHandler, IFileDialogControlEvents),363#endif364{ nullptr, 0 },365};366return QISearch(this, qit, riid, ppv);367}368369ULONG STDMETHODCALLTYPE AddRef() {370return InterlockedIncrement(&ref_count);371}372373ULONG STDMETHODCALLTYPE Release() {374long ref = InterlockedDecrement(&ref_count);375if (!ref) {376delete this;377}378return ref;379}380381// IFileDialogEvents methods382HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *) { return S_OK; }383HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialog *) { return S_OK; }384385HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog *p_pfd, IShellItem *p_item) {386if (root.is_empty()) {387return S_OK;388}389390LPWSTR lpw_path = nullptr;391p_item->GetDisplayName(SIGDN_FILESYSPATH, &lpw_path);392if (!lpw_path) {393return S_FALSE;394}395String path = String::utf16((const char16_t *)lpw_path).replace_char('\\', '/').trim_prefix(R"(\\?\)").simplify_path();396if (!path.begins_with(root.simplify_path())) {397return S_FALSE;398}399return S_OK;400}401402HRESULT STDMETHODCALLTYPE OnHelp(IFileDialog *) { return S_OK; }403HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialog *) { return S_OK; }404HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; }405HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog *pfd) { return S_OK; }406HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; }407408// IFileDialogControlEvents methods409HRESULT STDMETHODCALLTYPE OnItemSelected(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, DWORD p_item_idx) {410if (ctls.has(p_ctl_id)) {411selected[ctls[p_ctl_id]] = (int)p_item_idx;412}413return S_OK;414}415416HRESULT STDMETHODCALLTYPE OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; }417HRESULT STDMETHODCALLTYPE OnCheckButtonToggled(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, BOOL p_checked) {418if (ctls.has(p_ctl_id)) {419selected[ctls[p_ctl_id]] = (bool)p_checked;420}421return S_OK;422}423HRESULT STDMETHODCALLTYPE OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; }424425Dictionary get_selected() {426return selected;427}428429void set_root(const String &p_root) {430root = p_root;431}432433void add_option(IFileDialogCustomize *p_pfdc, const String &p_name, const Vector<String> &p_options, int p_default) {434int gid = ctl_id++;435int cid = ctl_id++;436437if (p_options.is_empty()) {438// Add check box.439p_pfdc->StartVisualGroup(gid, L"");440p_pfdc->AddCheckButton(cid, (LPCWSTR)p_name.utf16().get_data(), p_default);441p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);442p_pfdc->EndVisualGroup();443selected[p_name] = (bool)p_default;444} else {445// Add combo box.446p_pfdc->StartVisualGroup(gid, (LPCWSTR)p_name.utf16().get_data());447p_pfdc->AddComboBox(cid);448p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);449for (int i = 0; i < p_options.size(); i++) {450p_pfdc->AddControlItem(cid, i, (LPCWSTR)p_options[i].utf16().get_data());451}452p_pfdc->SetSelectedControlItem(cid, p_default);453p_pfdc->EndVisualGroup();454selected[p_name] = p_default;455}456ctls[cid] = p_name;457}458459virtual ~FileDialogEventHandler() {}460};461462GODOT_GCC_WARNING_POP463464LRESULT CALLBACK WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {465DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());466if (ds_win) {467return ds_win->WndProcFileDialog(hWnd, uMsg, wParam, lParam);468} else {469return DefWindowProcW(hWnd, uMsg, wParam, lParam);470}471}472473LRESULT DisplayServerWindows::WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {474MutexLock lock(file_dialog_mutex);475if (file_dialog_wnd.has(hWnd)) {476if (file_dialog_wnd[hWnd]->close_requested.is_set()) {477IPropertyStore *prop_store;478HRESULT hr = SHGetPropertyStoreForWindow(hWnd, IID_IPropertyStore, (void **)&prop_store);479if (hr == S_OK) {480PROPVARIANT val;481PropVariantInit(&val);482prop_store->SetValue(PKEY_AppUserModel_ID, val);483prop_store->Release();484}485DestroyWindow(hWnd);486file_dialog_wnd.erase(hWnd);487}488}489return DefWindowProcW(hWnd, uMsg, wParam, lParam);490}491492void DisplayServerWindows::_thread_fd_monitor(void *p_ud) {493DisplayServerWindows *ds = static_cast<DisplayServerWindows *>(get_singleton());494FileDialogData *fd = (FileDialogData *)p_ud;495496if (fd->mode < 0 && fd->mode >= DisplayServer::FILE_DIALOG_MODE_SAVE_MAX) {497fd->finished.set();498return;499}500CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);501502int64_t x = fd->wrect.position.x;503int64_t y = fd->wrect.position.y;504int64_t w = fd->wrect.size.x;505int64_t h = fd->wrect.size.y;506507WNDCLASSW wc = {};508wc.lpfnWndProc = (WNDPROC)::WndProcFileDialog;509wc.hInstance = GetModuleHandle(nullptr);510wc.lpszClassName = L"Engine File Dialog";511RegisterClassW(&wc);512513HWND hwnd_dialog = CreateWindowExW(WS_EX_APPWINDOW, L"Engine File Dialog", L"", WS_OVERLAPPEDWINDOW, x, y, w, h, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);514if (hwnd_dialog) {515{516MutexLock lock(ds->file_dialog_mutex);517ds->file_dialog_wnd[hwnd_dialog] = fd;518}519520HICON mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_SMALL, 0);521if (mainwindow_icon) {522SendMessage(hwnd_dialog, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);523}524mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_BIG, 0);525if (mainwindow_icon) {526SendMessage(hwnd_dialog, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);527}528IPropertyStore *prop_store;529HRESULT hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);530if (hr == S_OK) {531PROPVARIANT val;532InitPropVariantFromString((PCWSTR)fd->appid.utf16().get_data(), &val);533prop_store->SetValue(PKEY_AppUserModel_ID, val);534prop_store->Release();535}536}537538SetCurrentProcessExplicitAppUserModelID((PCWSTR)fd->appid.utf16().get_data());539540Vector<Char16String> filter_names;541Vector<Char16String> filter_exts;542for (const String &E : fd->filters) {543Vector<String> tokens = E.split(";");544if (tokens.size() >= 1) {545String flt = tokens[0].strip_edges();546int filter_slice_count = flt.get_slice_count(",");547Vector<String> exts;548for (int j = 0; j < filter_slice_count; j++) {549String str = (flt.get_slicec(',', j).strip_edges());550if (!str.is_empty()) {551exts.push_back(str);552}553}554if (!exts.is_empty()) {555String str = String(";").join(exts);556filter_exts.push_back(str.utf16());557if (tokens.size() == 2) {558filter_names.push_back(tokens[1].strip_edges().utf16());559} else {560filter_names.push_back(str.utf16());561}562}563}564}565if (filter_names.is_empty()) {566filter_exts.push_back(String("*.*").utf16());567filter_names.push_back((RTR("All Files") + " (*.*)").utf16());568}569570Vector<COMDLG_FILTERSPEC> filters;571for (int i = 0; i < filter_names.size(); i++) {572filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });573}574575HRESULT hr = S_OK;576IFileDialog *pfd = nullptr;577if (fd->mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {578hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd);579} else {580hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd);581}582if (SUCCEEDED(hr)) {583IFileDialogEvents *pfde = nullptr;584FileDialogEventHandler *event_handler = new FileDialogEventHandler();585hr = event_handler->QueryInterface(IID_PPV_ARGS(&pfde));586587DWORD cookie = 0;588hr = pfd->Advise(pfde, &cookie);589590IFileDialogCustomize *pfdc = nullptr;591hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));592593for (int i = 0; i < fd->options.size(); i++) {594const Dictionary &item = fd->options[i];595if (!item.has("name") || !item.has("values") || !item.has("default")) {596continue;597}598event_handler->add_option(pfdc, item["name"], item["values"], item["default"]);599}600event_handler->set_root(fd->root);601602pfdc->Release();603604DWORD flags;605pfd->GetOptions(&flags);606if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {607flags |= FOS_ALLOWMULTISELECT;608}609if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR) {610flags |= FOS_PICKFOLDERS;611}612if (fd->show_hidden) {613flags |= FOS_FORCESHOWHIDDEN;614}615pfd->SetOptions(flags | FOS_FORCEFILESYSTEM);616pfd->SetTitle((LPCWSTR)fd->title.utf16().get_data());617618String dir = ProjectSettings::get_singleton()->globalize_path(fd->current_directory);619if (dir == ".") {620dir = OS::get_singleton()->get_executable_path().get_base_dir();621}622if (dir.is_relative_path() || dir == ".") {623Char16String current_dir_name;624size_t str_len = GetCurrentDirectoryW(0, nullptr);625current_dir_name.resize_uninitialized(str_len + 1);626GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw());627if (dir == ".") {628dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/');629} else {630dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/').path_join(dir);631}632}633dir = dir.simplify_path();634dir = dir.trim_prefix(R"(\\?\)").replace_char('/', '\\');635636IShellItem *shellitem = nullptr;637hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem);638if (SUCCEEDED(hr)) {639pfd->SetDefaultFolder(shellitem);640pfd->SetFolder(shellitem);641}642643pfd->SetFileName((LPCWSTR)fd->filename.utf16().get_data());644pfd->SetFileTypes(filters.size(), filters.ptr());645pfd->SetFileTypeIndex(0);646647hr = pfd->Show(hwnd_dialog);648pfd->Unadvise(cookie);649650Dictionary options = event_handler->get_selected();651652pfde->Release();653event_handler->Release();654655UINT index = 0;656pfd->GetFileTypeIndex(&index);657if (index > 0) {658index = index - 1;659}660661if (SUCCEEDED(hr)) {662Vector<String> file_names;663664if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {665IShellItemArray *results;666hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results);667if (SUCCEEDED(hr)) {668DWORD count = 0;669results->GetCount(&count);670for (DWORD i = 0; i < count; i++) {671IShellItem *result;672results->GetItemAt(i, &result);673674PWSTR file_path = nullptr;675hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);676if (SUCCEEDED(hr)) {677file_names.push_back(String::utf16((const char16_t *)file_path).replace_char('\\', '/').trim_prefix(R"(\\?\)"));678CoTaskMemFree(file_path);679}680result->Release();681}682results->Release();683}684} else {685IShellItem *result;686hr = pfd->GetResult(&result);687if (SUCCEEDED(hr)) {688PWSTR file_path = nullptr;689hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);690if (SUCCEEDED(hr)) {691file_names.push_back(String::utf16((const char16_t *)file_path).replace_char('\\', '/').trim_prefix(R"(\\?\)"));692CoTaskMemFree(file_path);693}694result->Release();695}696}697if (fd->callback.is_valid()) {698MutexLock lock(ds->file_dialog_mutex);699FileDialogCallback cb;700cb.callback = fd->callback;701cb.status = true;702cb.files = file_names;703cb.index = index;704cb.options = options;705cb.opt_in_cb = fd->options_in_cb;706ds->pending_cbs.push_back(cb);707}708} else {709if (fd->callback.is_valid()) {710MutexLock lock(ds->file_dialog_mutex);711FileDialogCallback cb;712cb.callback = fd->callback;713cb.status = false;714cb.files = Vector<String>();715cb.index = index;716cb.options = options;717cb.opt_in_cb = fd->options_in_cb;718ds->pending_cbs.push_back(cb);719}720}721pfd->Release();722} else {723if (fd->callback.is_valid()) {724MutexLock lock(ds->file_dialog_mutex);725FileDialogCallback cb;726cb.callback = fd->callback;727cb.status = false;728cb.files = Vector<String>();729cb.index = 0;730cb.options = Dictionary();731cb.opt_in_cb = fd->options_in_cb;732ds->pending_cbs.push_back(cb);733}734}735{736MutexLock lock(ds->file_dialog_mutex);737if (hwnd_dialog && ds->file_dialog_wnd.has(hwnd_dialog)) {738IPropertyStore *prop_store;739hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);740if (hr == S_OK) {741PROPVARIANT val;742PropVariantInit(&val);743prop_store->SetValue(PKEY_AppUserModel_ID, val);744prop_store->Release();745}746DestroyWindow(hwnd_dialog);747ds->file_dialog_wnd.erase(hwnd_dialog);748}749}750UnregisterClassW(L"Engine File Dialog", GetModuleHandle(nullptr));751CoUninitialize();752753fd->finished.set();754755if (fd->window_id != INVALID_WINDOW_ID) {756callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd->window_id);757}758}759760Error 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) {761_THREAD_SAFE_METHOD_762763ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);764765String appname;766if (Engine::get_singleton()->is_editor_hint()) {767appname = "Godot.GodotEditor." + String(GODOT_VERSION_BRANCH);768} else {769String name = GLOBAL_GET("application/config/name");770String version = GLOBAL_GET("application/config/version");771if (version.is_empty()) {772version = "0";773}774String clean_app_name = name.to_pascal_case();775for (int i = 0; i < clean_app_name.length(); i++) {776if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {777clean_app_name[i] = '_';778}779}780clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");781appname = "Godot." + clean_app_name + "." + version;782}783784FileDialogData *fd = memnew(FileDialogData);785if (windows.has(p_window_id) && !windows[p_window_id].is_popup) {786fd->hwnd_owner = windows[p_window_id].hWnd;787RECT crect;788GetWindowRect(fd->hwnd_owner, &crect);789fd->wrect = Rect2i(crect.left, crect.top, crect.right - crect.left, crect.bottom - crect.top);790} else {791fd->hwnd_owner = nullptr;792fd->wrect = Rect2i(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT);793}794fd->appid = appname;795fd->title = p_title;796fd->current_directory = p_current_directory;797fd->root = p_root;798fd->filename = p_filename;799fd->show_hidden = p_show_hidden;800fd->mode = p_mode;801fd->window_id = p_window_id;802fd->filters = p_filters;803fd->options = p_options;804fd->callback = p_callback;805fd->options_in_cb = p_options_in_cb;806fd->finished.clear();807fd->close_requested.clear();808809fd->listener_thread.start(DisplayServerWindows::_thread_fd_monitor, fd);810811file_dialogs.push_back(fd);812813return OK;814}815816void DisplayServerWindows::process_file_dialog_callbacks() {817MutexLock lock(file_dialog_mutex);818while (!pending_cbs.is_empty()) {819FileDialogCallback cb = pending_cbs.front()->get();820pending_cbs.pop_front();821822if (cb.opt_in_cb) {823Variant ret;824Callable::CallError ce;825const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options };826827cb.callback.callp(args, 4, ret, ce);828if (ce.error != Callable::CallError::CALL_OK) {829ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce)));830}831} else {832Variant ret;833Callable::CallError ce;834const Variant *args[3] = { &cb.status, &cb.files, &cb.index };835836cb.callback.callp(args, 3, ret, ce);837if (ce.error != Callable::CallError::CALL_OK) {838ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce)));839}840}841}842}843844void DisplayServerWindows::beep() const {845MessageBeep(MB_OK);846}847848void DisplayServerWindows::_mouse_update_mode() {849_THREAD_SAFE_METHOD_850851MouseMode wanted_mouse_mode = mouse_mode_override_enabled852? mouse_mode_override853: mouse_mode_base;854855if (mouse_mode == wanted_mouse_mode) {856// Already in the same mode; do nothing.857return;858}859860mouse_mode = wanted_mouse_mode;861862_set_mouse_mode_impl(wanted_mouse_mode);863}864865void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {866ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);867if (p_mode == mouse_mode_base) {868return;869}870mouse_mode_base = p_mode;871_mouse_update_mode();872}873874DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {875return mouse_mode;876}877878void DisplayServerWindows::mouse_set_mode_override(MouseMode p_mode) {879ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);880if (p_mode == mouse_mode_override) {881return;882}883mouse_mode_override = p_mode;884_mouse_update_mode();885}886887DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode_override() const {888return mouse_mode_override;889}890891void DisplayServerWindows::mouse_set_mode_override_enabled(bool p_override_enabled) {892if (p_override_enabled == mouse_mode_override_enabled) {893return;894}895mouse_mode_override_enabled = p_override_enabled;896_mouse_update_mode();897}898899bool DisplayServerWindows::mouse_is_mode_override_enabled() const {900return mouse_mode_override_enabled;901}902903void DisplayServerWindows::warp_mouse(const Point2i &p_position) {904_THREAD_SAFE_METHOD_905906WindowID window_id = _get_focused_window_or_popup();907908if (!windows.has(window_id)) {909return; // No focused window?910}911912if (mouse_mode == MOUSE_MODE_CAPTURED) {913old_x = p_position.x;914old_y = p_position.y;915} else {916POINT p;917p.x = p_position.x;918p.y = p_position.y;919ClientToScreen(windows[window_id].hWnd, &p);920921SetCursorPos(p.x, p.y);922}923}924925Point2i DisplayServerWindows::mouse_get_position() const {926POINT p;927GetCursorPos(&p);928return Point2i(p.x, p.y) - _get_screens_origin();929}930931BitField<MouseButtonMask> DisplayServerWindows::mouse_get_button_state() const {932BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;933934if (GetKeyState(VK_LBUTTON) & (1 << 15)) {935last_button_state.set_flag(MouseButtonMask::LEFT);936}937if (GetKeyState(VK_RBUTTON) & (1 << 15)) {938last_button_state.set_flag(MouseButtonMask::RIGHT);939}940if (GetKeyState(VK_MBUTTON) & (1 << 15)) {941last_button_state.set_flag(MouseButtonMask::MIDDLE);942}943if (GetKeyState(VK_XBUTTON1) & (1 << 15)) {944last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);945}946if (GetKeyState(VK_XBUTTON2) & (1 << 15)) {947last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);948}949950return last_button_state;951}952953void DisplayServerWindows::clipboard_set(const String &p_text) {954_THREAD_SAFE_METHOD_955956if (!windows.has(MAIN_WINDOW_ID)) {957return;958}959960// Convert LF line endings to CRLF in clipboard content.961// Otherwise, line endings won't be visible when pasted in other software.962String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // Avoid \r\r\n.963964if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {965ERR_FAIL_MSG("Unable to open clipboard.");966}967EmptyClipboard();968969Char16String utf16 = text.utf16();970HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR));971ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");972973LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);974memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR));975GlobalUnlock(mem);976977SetClipboardData(CF_UNICODETEXT, mem);978979// Set the CF_TEXT version (not needed?).980CharString utf8 = text.utf8();981mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);982ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");983984LPTSTR ptr = (LPTSTR)GlobalLock(mem);985memcpy(ptr, utf8.get_data(), utf8.length());986ptr[utf8.length()] = 0;987GlobalUnlock(mem);988989SetClipboardData(CF_TEXT, mem);990991CloseClipboard();992}993994String DisplayServerWindows::clipboard_get() const {995_THREAD_SAFE_METHOD_996997if (!windows.has(MAIN_WINDOW_ID)) {998return String();999}10001001String ret;1002if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {1003ERR_FAIL_V_MSG("", "Unable to open clipboard.");1004}10051006if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {1007HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);1008if (mem != nullptr) {1009LPWSTR ptr = (LPWSTR)GlobalLock(mem);1010if (ptr != nullptr) {1011ret = String::utf16((const char16_t *)ptr);1012GlobalUnlock(mem);1013}1014}10151016} else if (IsClipboardFormatAvailable(CF_TEXT)) {1017HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);1018if (mem != nullptr) {1019LPTSTR ptr = (LPTSTR)GlobalLock(mem);1020if (ptr != nullptr) {1021ret.append_utf8((const char *)ptr);1022GlobalUnlock(mem);1023}1024}1025}10261027CloseClipboard();10281029return ret;1030}10311032Ref<Image> DisplayServerWindows::clipboard_get_image() const {1033Ref<Image> image;1034if (!windows.has(last_focused_window)) {1035return image; // No focused window?1036}1037if (!OpenClipboard(windows[last_focused_window].hWnd)) {1038ERR_FAIL_V_MSG(image, "Unable to open clipboard.");1039}1040UINT png_format = RegisterClipboardFormatA("PNG");1041if (png_format && IsClipboardFormatAvailable(png_format)) {1042HANDLE png_handle = GetClipboardData(png_format);1043if (png_handle) {1044size_t png_size = GlobalSize(png_handle);1045uint8_t *png_data = (uint8_t *)GlobalLock(png_handle);1046image.instantiate();10471048PNGDriverCommon::png_to_image(png_data, png_size, false, image);10491050GlobalUnlock(png_handle);1051}1052} else if (IsClipboardFormatAvailable(CF_DIB)) {1053HGLOBAL mem = GetClipboardData(CF_DIB);1054if (mem != nullptr) {1055BITMAPINFO *ptr = static_cast<BITMAPINFO *>(GlobalLock(mem));10561057if (ptr != nullptr) {1058BITMAPINFOHEADER *info = &ptr->bmiHeader;1059void *dib_bits = (void *)(ptr->bmiColors);10601061// Draw DIB image to temporary DC surface and read it back as BGRA8.1062HDC dc = GetDC(nullptr);1063if (dc) {1064HDC hdc = CreateCompatibleDC(dc);1065if (hdc) {1066HBITMAP hbm = CreateCompatibleBitmap(dc, info->biWidth, std::abs(info->biHeight));1067if (hbm) {1068SelectObject(hdc, hbm);1069SetDIBitsToDevice(hdc, 0, 0, info->biWidth, std::abs(info->biHeight), 0, 0, 0, std::abs(info->biHeight), dib_bits, ptr, DIB_RGB_COLORS);10701071BITMAPINFO bmp_info = {};1072bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1073bmp_info.bmiHeader.biWidth = info->biWidth;1074bmp_info.bmiHeader.biHeight = -std::abs(info->biHeight);1075bmp_info.bmiHeader.biPlanes = 1;1076bmp_info.bmiHeader.biBitCount = 32;1077bmp_info.bmiHeader.biCompression = BI_RGB;10781079Vector<uint8_t> img_data;1080img_data.resize(info->biWidth * std::abs(info->biHeight) * 4);1081GetDIBits(hdc, hbm, 0, std::abs(info->biHeight), img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);10821083uint8_t *wr = (uint8_t *)img_data.ptrw();1084for (int i = 0; i < info->biWidth * std::abs(info->biHeight); i++) {1085SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1086if (info->biBitCount != 32) {1087wr[i * 4 + 3] = 255; // Set A to solid if it's not in the source image.1088}1089}1090image = Image::create_from_data(info->biWidth, std::abs(info->biHeight), false, Image::Format::FORMAT_RGBA8, img_data);10911092DeleteObject(hbm);1093}1094DeleteDC(hdc);1095}1096ReleaseDC(nullptr, dc);1097}1098GlobalUnlock(mem);1099}1100}1101}1102CloseClipboard();11031104return image;1105}11061107bool DisplayServerWindows::clipboard_has() const {1108return (IsClipboardFormatAvailable(CF_TEXT) ||1109IsClipboardFormatAvailable(CF_UNICODETEXT) ||1110IsClipboardFormatAvailable(CF_OEMTEXT));1111}11121113bool DisplayServerWindows::clipboard_has_image() const {1114UINT png_format = RegisterClipboardFormatA("PNG");1115return ((png_format && IsClipboardFormatAvailable(png_format)) || IsClipboardFormatAvailable(CF_DIB));1116}11171118typedef struct {1119int count;1120int screen;1121HMONITOR monitor;1122} EnumScreenData;11231124static BOOL CALLBACK _MonitorEnumProcPrim(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1125EnumScreenData *data = (EnumScreenData *)dwData;1126if ((lprcMonitor->left == 0) && (lprcMonitor->top == 0)) {1127data->screen = data->count;1128return FALSE;1129}11301131data->count++;1132return TRUE;1133}11341135static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1136EnumScreenData *data = (EnumScreenData *)dwData;1137if (data->monitor == hMonitor) {1138data->screen = data->count;1139}11401141data->count++;1142return TRUE;1143}11441145static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1146int *data = (int *)dwData;1147(*data)++;1148return TRUE;1149}11501151int DisplayServerWindows::get_screen_count() const {1152_THREAD_SAFE_METHOD_11531154int data = 0;1155EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcCount, (LPARAM)&data);1156return data;1157}11581159int DisplayServerWindows::get_primary_screen() const {1160EnumScreenData data = { 0, 0, nullptr };1161EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPrim, (LPARAM)&data);1162return data.screen;1163}11641165int DisplayServerWindows::get_keyboard_focus_screen() const {1166HWND hwnd = GetForegroundWindow();1167if (hwnd) {1168EnumScreenData data = { 0, 0, MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) };1169EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);1170return data.screen;1171} else {1172return get_primary_screen();1173}1174}11751176typedef struct {1177int count;1178int screen;1179Point2 pos;1180} EnumPosData;11811182static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1183EnumPosData *data = (EnumPosData *)dwData;1184if (data->count == data->screen) {1185data->pos.x = lprcMonitor->left;1186data->pos.y = lprcMonitor->top;1187}11881189data->count++;1190return TRUE;1191}11921193static BOOL CALLBACK _MonitorEnumProcOrigin(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1194EnumPosData *data = (EnumPosData *)dwData;1195data->pos = data->pos.min(Point2(lprcMonitor->left, lprcMonitor->top));11961197return TRUE;1198}11991200Point2i DisplayServerWindows::_get_screens_origin() const {1201_THREAD_SAFE_METHOD_12021203EnumPosData data = { 0, 0, Point2() };1204EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcOrigin, (LPARAM)&data);1205return data.pos;1206}12071208Point2i DisplayServerWindows::screen_get_position(int p_screen) const {1209_THREAD_SAFE_METHOD_12101211p_screen = _get_screen_index(p_screen);1212int screen_count = get_screen_count();1213ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());12141215EnumPosData data = { 0, p_screen, Point2() };1216EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPos, (LPARAM)&data);1217return data.pos - _get_screens_origin();1218}12191220typedef struct {1221int count;1222int screen;1223Size2 size;1224} EnumSizeData;12251226typedef struct {1227int count;1228int screen;1229Rect2i rect;1230} EnumRectData;12311232typedef struct {1233Vector<DISPLAYCONFIG_PATH_INFO> paths;1234Vector<DISPLAYCONFIG_MODE_INFO> modes;1235int count;1236int screen;1237float rate;1238} EnumRefreshRateData;12391240static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1241EnumSizeData *data = (EnumSizeData *)dwData;1242if (data->count == data->screen) {1243data->size.x = lprcMonitor->right - lprcMonitor->left;1244data->size.y = lprcMonitor->bottom - lprcMonitor->top;1245}12461247data->count++;1248return TRUE;1249}12501251Size2i DisplayServerWindows::screen_get_size(int p_screen) const {1252_THREAD_SAFE_METHOD_12531254p_screen = _get_screen_index(p_screen);1255int screen_count = get_screen_count();1256ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());12571258EnumSizeData data = { 0, p_screen, Size2() };1259EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcSize, (LPARAM)&data);1260return data.size;1261}12621263static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1264EnumRectData *data = (EnumRectData *)dwData;1265if (data->count == data->screen) {1266MONITORINFO minfo;1267memset(&minfo, 0, sizeof(MONITORINFO));1268minfo.cbSize = sizeof(MONITORINFO);1269GetMonitorInfoA(hMonitor, &minfo);12701271data->rect.position.x = minfo.rcWork.left;1272data->rect.position.y = minfo.rcWork.top;1273data->rect.size.x = minfo.rcWork.right - minfo.rcWork.left;1274data->rect.size.y = minfo.rcWork.bottom - minfo.rcWork.top;1275}12761277data->count++;1278return TRUE;1279}12801281static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1282EnumRefreshRateData *data = (EnumRefreshRateData *)dwData;1283if (data->count == data->screen) {1284MONITORINFOEXW minfo;1285memset(&minfo, 0, sizeof(minfo));1286minfo.cbSize = sizeof(minfo);1287GetMonitorInfoW(hMonitor, &minfo);12881289bool found = false;1290for (const DISPLAYCONFIG_PATH_INFO &path : data->paths) {1291DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name;1292memset(&source_name, 0, sizeof(source_name));1293source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;1294source_name.header.size = sizeof(source_name);1295source_name.header.adapterId = path.sourceInfo.adapterId;1296source_name.header.id = path.sourceInfo.id;1297if (DisplayConfigGetDeviceInfo(&source_name.header) == ERROR_SUCCESS) {1298if (wcscmp(minfo.szDevice, source_name.viewGdiDeviceName) == 0 && path.targetInfo.refreshRate.Numerator != 0 && path.targetInfo.refreshRate.Denominator != 0) {1299data->rate = (double)path.targetInfo.refreshRate.Numerator / (double)path.targetInfo.refreshRate.Denominator;1300found = true;1301break;1302}1303}1304}1305if (!found) {1306DEVMODEW dm;1307memset(&dm, 0, sizeof(dm));1308dm.dmSize = sizeof(dm);1309EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);13101311data->rate = dm.dmDisplayFrequency;1312}1313}13141315data->count++;1316return TRUE;1317}13181319Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const {1320_THREAD_SAFE_METHOD_13211322p_screen = _get_screen_index(p_screen);1323int screen_count = get_screen_count();1324ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());13251326EnumRectData data = { 0, p_screen, Rect2i() };1327EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcUsableSize, (LPARAM)&data);1328data.rect.position -= _get_screens_origin();1329return data.rect;1330}13311332typedef struct {1333int count;1334int screen;1335int dpi;1336} EnumDpiData;13371338static int QueryDpiForMonitor(HMONITOR hmon, MONITOR_DPI_TYPE dpiType = MDT_DEFAULT) {1339int dpiX = 96, dpiY = 96;13401341UINT x = 0, y = 0;1342if (hmon) {1343HRESULT hr = GetDpiForMonitor(hmon, dpiType, &x, &y);1344if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {1345dpiX = (int)x;1346dpiY = (int)y;1347}1348} else {1349static int overallX = 0, overallY = 0;1350if (overallX <= 0 || overallY <= 0) {1351HDC hdc = GetDC(nullptr);1352if (hdc) {1353overallX = GetDeviceCaps(hdc, LOGPIXELSX);1354overallY = GetDeviceCaps(hdc, LOGPIXELSY);1355ReleaseDC(nullptr, hdc);1356}1357}1358if (overallX > 0 && overallY > 0) {1359dpiX = overallX;1360dpiY = overallY;1361}1362}13631364return (dpiX + dpiY) / 2;1365}13661367static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {1368EnumDpiData *data = (EnumDpiData *)dwData;1369if (data->count == data->screen) {1370data->dpi = QueryDpiForMonitor(hMonitor);1371}13721373data->count++;1374return TRUE;1375}13761377int DisplayServerWindows::screen_get_dpi(int p_screen) const {1378_THREAD_SAFE_METHOD_13791380p_screen = _get_screen_index(p_screen);1381int screen_count = get_screen_count();1382ERR_FAIL_INDEX_V(p_screen, screen_count, 72);13831384EnumDpiData data = { 0, p_screen, 72 };1385EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);1386return data.dpi;1387}13881389Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const {1390Point2i pos = p_position + _get_screens_origin();13911392POINT p;1393p.x = pos.x;1394p.y = pos.y;1395LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p);13961397HDC dc = GetDC(nullptr);1398if (dc) {1399COLORREF col = GetPixel(dc, p.x, p.y);1400if (col != CLR_INVALID) {1401ReleaseDC(nullptr, dc);1402return Color(float(col & 0x000000FF) / 255.0f, float((col & 0x0000FF00) >> 8) / 255.0f, float((col & 0x00FF0000) >> 16) / 255.0f, 1.0f);1403}1404ReleaseDC(nullptr, dc);1405}14061407return Color();1408}14091410Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {1411p_screen = _get_screen_index(p_screen);1412int screen_count = get_screen_count();1413ERR_FAIL_INDEX_V(p_screen, screen_count, Ref<Image>());14141415Point2i pos = screen_get_position(p_screen) + _get_screens_origin();1416Size2i size = screen_get_size(p_screen);14171418POINT p1;1419p1.x = pos.x;1420p1.y = pos.y;14211422POINT p2;1423p2.x = pos.x + size.x;1424p2.y = pos.y + size.y;1425LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p1);1426LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p2);14271428Ref<Image> img;1429HDC dc = GetDC(nullptr);1430if (dc) {1431HDC hdc = CreateCompatibleDC(dc);1432int width = p2.x - p1.x;1433int height = p2.y - p1.y;1434if (hdc) {1435HBITMAP hbm = CreateCompatibleBitmap(dc, width, height);1436if (hbm) {1437SelectObject(hdc, hbm);1438BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY);14391440BITMAPINFO bmp_info = {};1441bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1442bmp_info.bmiHeader.biWidth = width;1443bmp_info.bmiHeader.biHeight = -height;1444bmp_info.bmiHeader.biPlanes = 1;1445bmp_info.bmiHeader.biBitCount = 32;1446bmp_info.bmiHeader.biCompression = BI_RGB;14471448Vector<uint8_t> img_data;1449img_data.resize(width * height * 4);1450GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);14511452uint8_t *wr = (uint8_t *)img_data.ptrw();1453for (int i = 0; i < width * height; i++) {1454SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1455}1456img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);14571458DeleteObject(hbm);1459}1460DeleteDC(hdc);1461}1462ReleaseDC(nullptr, dc);1463}14641465return img;1466}14671468Ref<Image> DisplayServerWindows::screen_get_image_rect(const Rect2i &p_rect) const {1469Point2i pos = p_rect.position + _get_screens_origin();1470Size2i size = p_rect.size;14711472POINT p1;1473p1.x = pos.x;1474p1.y = pos.y;14751476POINT p2;1477p2.x = pos.x + size.x;1478p2.y = pos.y + size.y;1479LogicalToPhysicalPointForPerMonitorDPI(0, &p1);1480LogicalToPhysicalPointForPerMonitorDPI(0, &p2);14811482Ref<Image> img;1483HDC dc = GetDC(0);1484if (dc) {1485HDC hdc = CreateCompatibleDC(dc);1486int width = p2.x - p1.x;1487int height = p2.y - p1.y;1488if (hdc) {1489HBITMAP hbm = CreateCompatibleBitmap(dc, width, height);1490if (hbm) {1491SelectObject(hdc, hbm);1492BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY);14931494BITMAPINFO bmp_info = {};1495bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);1496bmp_info.bmiHeader.biWidth = width;1497bmp_info.bmiHeader.biHeight = -height;1498bmp_info.bmiHeader.biPlanes = 1;1499bmp_info.bmiHeader.biBitCount = 32;1500bmp_info.bmiHeader.biCompression = BI_RGB;15011502Vector<uint8_t> img_data;1503img_data.resize(width * height * 4);1504GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);15051506uint8_t *wr = (uint8_t *)img_data.ptrw();1507for (int i = 0; i < width * height; i++) {1508SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.1509}1510img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);15111512DeleteObject(hbm);1513}1514DeleteDC(hdc);1515}1516ReleaseDC(NULL, dc);1517}15181519return img;1520}15211522float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {1523_THREAD_SAFE_METHOD_15241525p_screen = _get_screen_index(p_screen);1526int screen_count = get_screen_count();1527ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);15281529EnumRefreshRateData data = { Vector<DISPLAYCONFIG_PATH_INFO>(), Vector<DISPLAYCONFIG_MODE_INFO>(), 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK };15301531uint32_t path_count = 0;1532uint32_t mode_count = 0;1533if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) {1534data.paths.resize(path_count);1535data.modes.resize(mode_count);1536if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, data.paths.ptrw(), &mode_count, data.modes.ptrw(), nullptr) != ERROR_SUCCESS) {1537data.paths.clear();1538data.modes.clear();1539}1540}15411542EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data);1543return data.rate;1544}15451546void DisplayServerWindows::screen_set_keep_on(bool p_enable) {1547if (keep_screen_on == p_enable) {1548return;1549}15501551if (p_enable) {1552const String reason = "Godot Engine running with display/window/energy_saving/keep_screen_on = true";1553Char16String reason_utf16 = reason.utf16();1554REASON_CONTEXT context;1555context.Version = POWER_REQUEST_CONTEXT_VERSION;1556context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;1557context.Reason.SimpleReasonString = (LPWSTR)(reason_utf16.ptrw());1558power_request = PowerCreateRequest(&context);1559if (power_request == INVALID_HANDLE_VALUE) {1560print_error("Failed to enable screen_keep_on.");1561return;1562}1563if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired) == 0) {1564print_error("Failed to request system sleep override.");1565return;1566}1567if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired) == 0) {1568print_error("Failed to request display timeout override.");1569return;1570}1571} else {1572PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired);1573PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired);1574CloseHandle(power_request);1575power_request = nullptr;1576}15771578keep_screen_on = p_enable;1579}15801581bool DisplayServerWindows::screen_is_kept_on() const {1582return keep_screen_on;1583}15841585Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {1586_THREAD_SAFE_METHOD_15871588Vector<DisplayServer::WindowID> ret;1589for (const KeyValue<WindowID, WindowData> &E : windows) {1590ret.push_back(E.key);1591}1592return ret;1593}15941595DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(const Point2i &p_position) const {1596Point2i offset = _get_screens_origin();1597POINT p;1598p.x = p_position.x + offset.x;1599p.y = p_position.y + offset.y;1600HWND hwnd = WindowFromPoint(p);1601for (const KeyValue<WindowID, WindowData> &E : windows) {1602if (E.value.hWnd == hwnd) {1603return E.key;1604}1605}16061607return INVALID_WINDOW_ID;1608}16091610DisplayServer::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) {1611_THREAD_SAFE_METHOD_16121613WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_exclusive, p_transient_parent, NULL);1614ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window.");16151616WindowData &wd = windows[window_id];16171618if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {1619wd.resizable = false;1620}1621if (p_flags & WINDOW_FLAG_MINIMIZE_DISABLED_BIT) {1622wd.no_min_btn = true;1623}1624if (p_flags & WINDOW_FLAG_MAXIMIZE_DISABLED_BIT) {1625wd.no_max_btn = true;1626}1627if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {1628wd.borderless = true;1629}1630if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {1631wd.always_on_top = true;1632}1633if (p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT) {1634wd.sharp_corners = true;1635}1636if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {1637wd.no_focus = true;1638}1639if (p_flags & WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT) {1640wd.mpass = true;1641}1642if (p_flags & WINDOW_FLAG_EXCLUDE_FROM_CAPTURE_BIT) {1643wd.hide_from_capture = true;1644if (os_ver.dwBuildNumber >= 19041) {1645SetWindowDisplayAffinity(wd.hWnd, WDA_EXCLUDEFROMCAPTURE);1646} else {1647SetWindowDisplayAffinity(wd.hWnd, WDA_MONITOR);1648}1649}1650if (p_flags & WINDOW_FLAG_POPUP_BIT) {1651wd.is_popup = true;1652}1653if (p_flags & WINDOW_FLAG_TRANSPARENT_BIT) {1654if (OS::get_singleton()->is_layered_allowed()) {1655DWM_BLURBEHIND bb;1656ZeroMemory(&bb, sizeof(bb));1657HRGN hRgn = CreateRectRgn(0, 0, -1, -1);1658bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;1659bb.hRgnBlur = hRgn;1660bb.fEnable = TRUE;1661DwmEnableBlurBehindWindow(wd.hWnd, &bb);1662}16631664wd.layered_window = true;1665}16661667// Inherit icons from MAIN_WINDOW for all sub windows.1668HICON mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_SMALL, 0);1669if (mainwindow_icon) {1670SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);1671}1672mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_BIG, 0);1673if (mainwindow_icon) {1674SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);1675}1676#ifdef RD_ENABLED1677if (rendering_device) {1678rendering_device->screen_create(window_id);1679}1680#endif1681return window_id;1682}16831684bool DisplayServerWindows::_is_always_on_top_recursive(WindowID p_window) const {1685ERR_FAIL_COND_V(!windows.has(p_window), false);16861687const WindowData &wd = windows[p_window];1688if (wd.always_on_top) {1689return true;1690}16911692if (wd.transient_parent != INVALID_WINDOW_ID) {1693return _is_always_on_top_recursive(wd.transient_parent);1694}16951696return false;1697}16981699void DisplayServerWindows::show_window(WindowID p_id) {1700ERR_FAIL_COND(!windows.has(p_id));17011702WindowData &wd = windows[p_id];1703popup_open(p_id);17041705if (p_id != MAIN_WINDOW_ID) {1706_update_window_style(p_id);1707}1708wd.initialized = true;17091710if (wd.maximized) {1711ShowWindow(wd.hWnd, SW_SHOWMAXIMIZED);1712SetForegroundWindow(wd.hWnd); // Slightly higher priority.1713SetFocus(wd.hWnd); // Set keyboard focus.1714} else if (wd.minimized) {1715ShowWindow(wd.hWnd, SW_SHOWMINIMIZED);1716} else if (wd.no_focus) {1717// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow1718ShowWindow(wd.hWnd, SW_SHOWNA);1719} else if (wd.is_popup) {1720ShowWindow(wd.hWnd, SW_SHOWNA);1721SetFocus(wd.hWnd); // Set keyboard focus.1722} else {1723ShowWindow(wd.hWnd, SW_SHOW);1724SetForegroundWindow(wd.hWnd); // Slightly higher priority.1725SetFocus(wd.hWnd); // Set keyboard focus.1726}1727if (_is_always_on_top_recursive(p_id)) {1728SetWindowPos(wd.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));1729}1730}17311732void DisplayServerWindows::delete_sub_window(WindowID p_window) {1733_THREAD_SAFE_METHOD_17341735ERR_FAIL_COND(!windows.has(p_window));1736ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted.");17371738popup_close(p_window);17391740WindowData &wd = windows[p_window];17411742IPropertyStore *prop_store;1743HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);1744if (hr == S_OK) {1745PROPVARIANT val;1746PropVariantInit(&val);1747prop_store->SetValue(PKEY_AppUserModel_ID, val);1748prop_store->Release();1749}17501751while (wd.transient_children.size()) {1752window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);1753}17541755if (wd.transient_parent != INVALID_WINDOW_ID) {1756window_set_transient(p_window, INVALID_WINDOW_ID);1757}17581759#ifdef RD_ENABLED1760if (rendering_device) {1761rendering_device->screen_free(p_window);1762}17631764if (rendering_context) {1765rendering_context->window_destroy(p_window);1766}1767#endif1768#ifdef GLES3_ENABLED1769if (gl_manager_angle) {1770gl_manager_angle->window_destroy(p_window);1771}1772if (gl_manager_native) {1773gl_manager_native->window_destroy(p_window);1774}1775#endif17761777if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {1778wintab_WTClose(wd.wtctx);1779wd.wtctx = nullptr;1780}17811782if (wd.drop_target != nullptr) {1783RevokeDragDrop(wd.hWnd);1784wd.drop_target->Release();1785}17861787DestroyWindow(wd.hWnd);1788windows.erase(p_window);17891790if (last_focused_window == p_window) {1791last_focused_window = INVALID_WINDOW_ID;1792}1793}17941795void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) {1796#if defined(GLES3_ENABLED)1797if (gl_manager_angle) {1798gl_manager_angle->window_make_current(p_window_id);1799}1800if (gl_manager_native) {1801gl_manager_native->window_make_current(p_window_id);1802}1803#endif1804}18051806int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {1807ERR_FAIL_COND_V(!windows.has(p_window), 0);1808switch (p_handle_type) {1809case DISPLAY_HANDLE: {1810return 0; // Not supported.1811}1812case WINDOW_HANDLE: {1813return (int64_t)windows[p_window].hWnd;1814}1815#if defined(GLES3_ENABLED)1816case WINDOW_VIEW: {1817if (gl_manager_native) {1818return (int64_t)gl_manager_native->get_hdc(p_window);1819} else {1820return (int64_t)GetDC(windows[p_window].hWnd);1821}1822}1823case OPENGL_CONTEXT: {1824if (gl_manager_native) {1825return (int64_t)gl_manager_native->get_hglrc(p_window);1826}1827if (gl_manager_angle) {1828return (int64_t)gl_manager_angle->get_context(p_window);1829}1830return 0;1831}1832case EGL_DISPLAY: {1833if (gl_manager_angle) {1834return (int64_t)gl_manager_angle->get_display(p_window);1835}1836return 0;1837}1838case EGL_CONFIG: {1839if (gl_manager_angle) {1840return (int64_t)gl_manager_angle->get_config(p_window);1841}1842return 0;1843}1844#endif1845default: {1846return 0;1847}1848}1849}18501851void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {1852_THREAD_SAFE_METHOD_18531854ERR_FAIL_COND(!windows.has(p_window));1855windows[p_window].instance_id = p_instance;1856}18571858ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window) const {1859_THREAD_SAFE_METHOD_18601861ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());1862return windows[p_window].instance_id;1863}18641865void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {1866_THREAD_SAFE_METHOD_18671868ERR_FAIL_COND(!windows.has(p_window));1869windows[p_window].rect_changed_callback = p_callable;1870}18711872void DisplayServerWindows::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {1873_THREAD_SAFE_METHOD_18741875ERR_FAIL_COND(!windows.has(p_window));1876windows[p_window].event_callback = p_callable;1877}18781879void DisplayServerWindows::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {1880_THREAD_SAFE_METHOD_18811882ERR_FAIL_COND(!windows.has(p_window));1883windows[p_window].input_event_callback = p_callable;1884}18851886void DisplayServerWindows::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {1887_THREAD_SAFE_METHOD_18881889ERR_FAIL_COND(!windows.has(p_window));1890windows[p_window].input_text_callback = p_callable;1891}18921893void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {1894_THREAD_SAFE_METHOD_18951896ERR_FAIL_COND(!windows.has(p_window));1897WindowData &window_data = windows[p_window];18981899window_data.drop_files_callback = p_callable;19001901if (window_data.drop_target == nullptr) {1902window_data.drop_target = memnew(DropTargetWindows(&window_data));1903ERR_FAIL_COND(RegisterDragDrop(window_data.hWnd, window_data.drop_target) != S_OK);1904}1905}19061907void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) {1908_THREAD_SAFE_METHOD_19091910ERR_FAIL_COND(!windows.has(p_window));1911SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data()));1912}19131914Size2i DisplayServerWindows::window_get_title_size(const String &p_title, WindowID p_window) const {1915_THREAD_SAFE_METHOD_19161917Size2i size;1918ERR_FAIL_COND_V(!windows.has(p_window), size);19191920const WindowData &wd = windows[p_window];1921if (wd.fullscreen || wd.minimized || wd.borderless) {1922return size;1923}19241925HDC hdc = GetDCEx(wd.hWnd, nullptr, DCX_WINDOW);1926if (hdc) {1927Char16String s = p_title.utf16();1928SIZE text_size;1929if (GetTextExtentPoint32W(hdc, (LPCWSTR)(s.get_data()), s.length(), &text_size)) {1930size.x = text_size.cx;1931size.y = text_size.cy;1932}19331934ReleaseDC(wd.hWnd, hdc);1935}1936RECT rect;1937if (DwmGetWindowAttribute(wd.hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rect, sizeof(RECT)) == S_OK) {1938if (rect.right - rect.left > 0) {1939ClientToScreen(wd.hWnd, (POINT *)&rect.left);1940ClientToScreen(wd.hWnd, (POINT *)&rect.right);19411942PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.left);1943PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.right);19441945size.x += (rect.right - rect.left);1946size.y = MAX(size.y, rect.bottom - rect.top);1947}1948}1949if (icon.is_valid()) {1950size.x += 32;1951} else {1952size.x += 16;1953}1954return size;1955}19561957void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {1958_THREAD_SAFE_METHOD_19591960ERR_FAIL_COND(!windows.has(p_window));1961windows[p_window].mpath = p_region;1962_update_window_mouse_passthrough(p_window);1963}19641965void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {1966ERR_FAIL_COND(!windows.has(p_window));19671968const WindowData &wd = windows[p_window];1969bool clip_pixel = (wd.multiwindow_fs || (wd.borderless && wd.maximized));1970bool pass_set = (wd.mpath.size() > 0);1971if (!clip_pixel && !pass_set) {1972SetWindowRgn(wd.hWnd, nullptr, TRUE);1973} else {1974HRGN region = nullptr;1975if (pass_set) {1976Vector<POINT> points;1977points.resize(wd.mpath.size());1978POINT *points_ptr = points.ptrw();1979for (int i = 0; i < wd.mpath.size(); i++) {1980if (wd.borderless) {1981points_ptr[i].x = wd.mpath[i].x;1982points_ptr[i].y = wd.mpath[i].y;1983} else {1984points_ptr[i].x = wd.mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME);1985points_ptr[i].y = wd.mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);1986}1987}1988region = CreatePolygonRgn(points.ptr(), points.size(), ALTERNATE);1989} else {1990region = CreateRectRgn(0, 0, wd.width, wd.height);1991}1992if (clip_pixel) {1993HRGN region_clip = CreateRectRgn(0, 0, wd.width, wd.height);1994CombineRgn(region, region, region_clip, RGN_AND);1995DeleteObject(region_clip);1996}1997SetWindowRgn(wd.hWnd, region, FALSE);1998}1999}20002001int DisplayServerWindows::window_get_current_screen(WindowID p_window) const {2002_THREAD_SAFE_METHOD_20032004ERR_FAIL_COND_V(!windows.has(p_window), INVALID_SCREEN);20052006EnumScreenData data = { 0, 0, MonitorFromWindow(windows[p_window].hWnd, MONITOR_DEFAULTTONEAREST) };2007EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);2008return data.screen;2009}20102011void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) {2012_THREAD_SAFE_METHOD_20132014ERR_FAIL_COND(!windows.has(p_window));20152016p_screen = _get_screen_index(p_screen);2017int screen_count = get_screen_count();2018ERR_FAIL_INDEX(p_screen, screen_count);20192020if (window_get_current_screen(p_window) == p_screen) {2021return;2022}2023const WindowData &wd = windows[p_window];20242025if (wd.parent_hwnd) {2026print_line("Embedded window can't be moved to another screen.");2027return;2028}2029if (wd.fullscreen) {2030Point2 pos = screen_get_position(p_screen) + _get_screens_origin();2031Size2 size = screen_get_size(p_screen);2032int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;20332034MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);2035} else if (wd.maximized) {2036Point2 pos = screen_get_position(p_screen) + _get_screens_origin();2037Size2 size = screen_get_size(p_screen);2038int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;20392040ShowWindow(wd.hWnd, SW_RESTORE);2041MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);2042ShowWindow(wd.hWnd, SW_MAXIMIZE);2043} else {2044Rect2i srect = screen_get_usable_rect(p_screen);2045Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));2046Size2i wsize = window_get_size(p_window);2047wpos += srect.position;20482049wpos = wpos.clamp(srect.position, srect.position + srect.size - wsize / 3);2050window_set_position(wpos, p_window);2051}2052}20532054Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {2055_THREAD_SAFE_METHOD_20562057ERR_FAIL_COND_V(!windows.has(p_window), Point2i());2058const WindowData &wd = windows[p_window];20592060if (wd.minimized) {2061return wd.last_pos;2062}20632064POINT point;2065point.x = 0;2066point.y = 0;20672068ClientToScreen(wd.hWnd, &point);20692070return Point2i(point.x, point.y) - _get_screens_origin();2071}20722073Point2i DisplayServerWindows::window_get_position_with_decorations(WindowID p_window) const {2074_THREAD_SAFE_METHOD_20752076ERR_FAIL_COND_V(!windows.has(p_window), Point2i());2077const WindowData &wd = windows[p_window];20782079if (wd.minimized) {2080return wd.last_pos;2081}20822083RECT r;2084if (GetWindowRect(wd.hWnd, &r)) {2085return Point2i(r.left, r.top) - _get_screens_origin();2086}20872088return Point2i();2089}20902091void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) {2092ERR_FAIL_COND(!windows.has(p_window));20932094POINT mouse_pos;2095if (GetCursorPos(&mouse_pos) && ScreenToClient(windows[p_window].hWnd, &mouse_pos)) {2096if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= windows[p_window].width && mouse_pos.y <= windows[p_window].height) {2097old_x = mouse_pos.x;2098old_y = mouse_pos.y;2099old_invalid = false;2100Input::get_singleton()->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y));2101}2102}2103}21042105void DisplayServerWindows::window_set_position(const Point2i &p_position, WindowID p_window) {2106_THREAD_SAFE_METHOD_21072108ERR_FAIL_COND(!windows.has(p_window));2109WindowData &wd = windows[p_window];21102111if (wd.parent_hwnd) {2112print_line("Embedded window can't be moved.");2113return;2114}21152116if (wd.fullscreen || wd.maximized) {2117return;2118}21192120Point2i offset = _get_screens_origin();21212122RECT rc;2123rc.left = p_position.x + offset.x;2124rc.right = p_position.x + wd.width + offset.x;2125rc.bottom = p_position.y + wd.height + offset.y;2126rc.top = p_position.y + offset.y;21272128const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);2129const DWORD exStyle = GetWindowLongPtr(wd.hWnd, GWL_EXSTYLE);21302131AdjustWindowRectEx(&rc, style, false, exStyle);2132MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);21332134wd.last_pos = p_position;2135_update_real_mouse_position(p_window);2136}21372138void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclusive) {2139_THREAD_SAFE_METHOD_2140ERR_FAIL_COND(!windows.has(p_window));2141WindowData &wd = windows[p_window];2142if (wd.exclusive != p_exclusive) {2143wd.exclusive = p_exclusive;2144if (wd.transient_parent != INVALID_WINDOW_ID) {2145if (wd.exclusive) {2146WindowData &wd_parent = windows[wd.transient_parent];2147SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);2148} else {2149SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);2150}2151}2152}2153}21542155void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) {2156_THREAD_SAFE_METHOD_21572158ERR_FAIL_COND(p_window == p_parent);2159ERR_FAIL_COND(!windows.has(p_window));21602161WindowData &wd_window = windows[p_window];21622163ERR_FAIL_COND(wd_window.transient_parent == p_parent);2164ERR_FAIL_COND_MSG(wd_window.always_on_top, "Windows with the 'on top' can't become transient.");21652166if (p_parent == INVALID_WINDOW_ID) {2167// Remove transient.21682169ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);2170ERR_FAIL_COND(!windows.has(wd_window.transient_parent));21712172WindowData &wd_parent = windows[wd_window.transient_parent];21732174wd_window.transient_parent = INVALID_WINDOW_ID;2175wd_parent.transient_children.erase(p_window);21762177if (wd_window.exclusive) {2178SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);2179}2180} else {2181ERR_FAIL_COND(!windows.has(p_parent));2182ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");2183WindowData &wd_parent = windows[p_parent];21842185wd_window.transient_parent = p_parent;2186wd_parent.transient_children.insert(p_window);21872188if (wd_window.exclusive) {2189SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);2190}2191}2192}21932194void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_window) {2195_THREAD_SAFE_METHOD_21962197ERR_FAIL_COND(!windows.has(p_window));2198WindowData &wd = windows[p_window];21992200if (wd.parent_hwnd) {2201print_line("Embedded windows can't have a maximum size.");2202return;2203}22042205if ((p_size != Size2()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {2206ERR_PRINT("Maximum window size can't be smaller than minimum window size!");2207return;2208}2209wd.max_size = p_size;2210}22112212Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const {2213_THREAD_SAFE_METHOD_22142215ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2216const WindowData &wd = windows[p_window];2217return wd.max_size;2218}22192220void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_window) {2221_THREAD_SAFE_METHOD_22222223ERR_FAIL_COND(!windows.has(p_window));2224WindowData &wd = windows[p_window];22252226if (wd.parent_hwnd) {2227print_line("Embedded windows can't have a minimum size.");2228return;2229}22302231if ((p_size != Size2()) && (wd.max_size != Size2()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {2232ERR_PRINT("Minimum window size can't be larger than maximum window size!");2233return;2234}2235wd.min_size = p_size;2236}22372238Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const {2239_THREAD_SAFE_METHOD_22402241ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2242const WindowData &wd = windows[p_window];2243return wd.min_size;2244}22452246void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_window) {2247_THREAD_SAFE_METHOD_22482249ERR_FAIL_COND(!windows.has(p_window));2250WindowData &wd = windows[p_window];22512252if (wd.parent_hwnd) {2253print_line("Embedded window can't be resized.");2254return;2255}22562257if (wd.fullscreen || wd.maximized) {2258return;2259}22602261int w = p_size.width;2262int h = p_size.height;2263RECT rect;2264GetWindowRect(wd.hWnd, &rect);22652266if (!wd.borderless) {2267RECT crect;2268GetClientRect(wd.hWnd, &crect);22692270w += (rect.right - rect.left) - (crect.right - crect.left);2271h += (rect.bottom - rect.top) - (crect.bottom - crect.top);2272}22732274MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE);2275}22762277Size2i DisplayServerWindows::window_get_size(WindowID p_window) const {2278_THREAD_SAFE_METHOD_22792280ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2281const WindowData &wd = windows[p_window];22822283// GetClientRect() returns a zero rect for a minimized window, so we need to get the size in another way.2284if (wd.minimized) {2285return Size2(wd.width, wd.height);2286}22872288RECT r;2289if (GetClientRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.2290int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;2291return Size2(r.right - r.left - off_x, r.bottom - r.top);2292}2293return Size2();2294}22952296Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window) const {2297_THREAD_SAFE_METHOD_22982299ERR_FAIL_COND_V(!windows.has(p_window), Size2i());2300const WindowData &wd = windows[p_window];23012302RECT r;2303if (GetWindowRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.2304int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;2305return Size2(r.right - r.left - off_x, r.bottom - r.top);2306}2307return Size2();2308}23092310void 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) {2311// Windows docs for window styles:2312// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles2313// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles23142315r_style = 0;2316r_style_ex = WS_EX_WINDOWEDGE;2317if (p_main_window) {2318// When embedded, we don't want the window to have WS_EX_APPWINDOW because it will2319// show the embedded process in the taskbar and Alt-Tab.2320if (!p_embed_child) {2321r_style_ex |= WS_EX_APPWINDOW;2322}2323if (p_initialized) {2324r_style |= WS_VISIBLE;2325}2326}23272328if (p_embed_child) {2329r_style |= WS_POPUP;2330} else if (p_fullscreen || p_borderless) {2331r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past.2332if (p_minimized) {2333r_style |= WS_MINIMIZE;2334} else if (p_maximized) {2335r_style |= WS_MAXIMIZE;2336}2337if (!p_fullscreen) {2338r_style |= WS_SYSMENU;2339if (!p_no_min_btn) {2340r_style |= WS_MINIMIZEBOX;2341}2342if (!p_no_max_btn) {2343r_style |= WS_MAXIMIZEBOX;2344}2345}2346} else {2347if (p_resizable) {2348if (p_minimized) {2349r_style = WS_OVERLAPPEDWINDOW | WS_MINIMIZE;2350} else if (p_maximized) {2351r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE;2352} else {2353r_style = WS_OVERLAPPEDWINDOW;2354}2355if (p_no_min_btn) {2356r_style &= ~WS_MINIMIZEBOX;2357}2358if (p_no_max_btn) {2359r_style &= ~WS_MAXIMIZEBOX;2360}2361} else {2362if (p_minimized) {2363r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZE;2364} else {2365r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;2366}2367if (!p_no_min_btn) {2368r_style |= WS_MINIMIZEBOX;2369}2370if (!p_no_max_btn) {2371r_style |= WS_MAXIMIZEBOX;2372}2373}2374}23752376if (p_no_activate_focus && !p_embed_child) {2377r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;2378}23792380if (!p_borderless && !p_no_activate_focus && p_initialized) {2381r_style |= WS_VISIBLE;2382}23832384r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;2385r_style_ex |= WS_EX_ACCEPTFILES;23862387if (OS::get_singleton()->get_current_rendering_driver_name() == "d3d12") {2388r_style_ex |= WS_EX_NOREDIRECTIONBITMAP;2389}2390}23912392void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint) {2393_THREAD_SAFE_METHOD_23942395ERR_FAIL_COND(!windows.has(p_window));2396WindowData &wd = windows[p_window];23972398DWORD style = 0;2399DWORD style_ex = 0;24002401_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);24022403SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);2404SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);24052406if (icon.is_valid()) {2407set_icon(icon);2408}24092410SetWindowPos(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));24112412if (p_repaint) {2413RECT rect;2414GetWindowRect(wd.hWnd, &rect);2415int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;2416MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left + off_x, rect.bottom - rect.top, TRUE);2417}2418}24192420void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) {2421_THREAD_SAFE_METHOD_24222423ERR_FAIL_COND(!windows.has(p_window));2424WindowData &wd = windows[p_window];24252426if (p_mode != WINDOW_MODE_WINDOWED && wd.parent_hwnd) {2427print_line("Embedded window only supports Windowed mode.");2428return;2429}24302431bool was_fullscreen = wd.fullscreen;2432wd.was_fullscreen_pre_min = false;24332434if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {2435int cs = window_get_current_screen(p_window);2436Rect2i full = Rect2i(screen_get_position(cs), screen_get_size(cs));2437Rect2i usable = screen_get_usable_rect(cs);2438if (full == usable) {2439p_mode = WINDOW_MODE_FULLSCREEN;2440}2441}24422443if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {2444RECT rect;24452446wd.fullscreen = false;2447wd.multiwindow_fs = false;24482449// Restore previous maximized state.2450wd.maximized = wd.was_maximized_pre_fs;24512452_update_window_style(p_window, false);24532454// Restore window rect after exiting fullscreen.2455if (wd.pre_fs_valid) {2456rect = wd.pre_fs_rect;2457} else {2458rect.left = 0;2459rect.right = wd.width;2460rect.top = 0;2461rect.bottom = wd.height;2462}24632464ShowWindow(wd.hWnd, SW_RESTORE);2465MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);24662467if (restore_mouse_trails > 1) {2468SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);2469restore_mouse_trails = 0;2470}2471}24722473if ((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) {2474RECT rect;2475if (wd.pre_fs_valid) {2476rect = wd.pre_fs_rect;2477} else {2478rect.left = 0;2479rect.right = wd.width;2480rect.top = 0;2481rect.bottom = wd.height;2482}24832484ShowWindow(wd.hWnd, SW_RESTORE);2485MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);2486}24872488if (p_mode == WINDOW_MODE_WINDOWED) {2489ShowWindow(wd.hWnd, SW_NORMAL);2490wd.maximized = false;2491wd.minimized = false;2492}24932494if (p_mode == WINDOW_MODE_MAXIMIZED && !wd.borderless) {2495ShowWindow(wd.hWnd, SW_MAXIMIZE);2496wd.maximized = true;2497wd.minimized = false;2498}24992500if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {2501if (!was_fullscreen && !(wd.maximized && wd.borderless)) {2502// Save non-fullscreen rect before entering fullscreen.2503GetWindowRect(wd.hWnd, &wd.pre_fs_rect);2504wd.pre_fs_valid = true;2505}2506ShowWindow(wd.hWnd, SW_NORMAL);2507wd.maximized = true;2508wd.minimized = false;25092510int cs = window_get_current_screen(p_window);2511Rect2i usable = screen_get_usable_rect(cs);2512Point2 pos = usable.position + _get_screens_origin();2513Size2 size = usable.size;2514MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);2515}25162517if (p_mode == WINDOW_MODE_MINIMIZED) {2518ShowWindow(wd.hWnd, SW_MINIMIZE);2519wd.maximized = false;2520wd.minimized = true;2521wd.was_fullscreen_pre_min = was_fullscreen;2522}25232524if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {2525wd.multiwindow_fs = false;2526} else if (p_mode == WINDOW_MODE_FULLSCREEN) {2527wd.multiwindow_fs = true;2528}2529_update_window_style(p_window, false);25302531if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) {2532if (wd.minimized || wd.maximized) {2533ShowWindow(wd.hWnd, SW_RESTORE);2534}25352536// Save previous maximized stare.2537wd.was_maximized_pre_fs = wd.maximized;25382539if (!was_fullscreen && !(wd.maximized && wd.borderless)) {2540// Save non-fullscreen rect before entering fullscreen.2541GetWindowRect(wd.hWnd, &wd.pre_fs_rect);2542wd.pre_fs_valid = true;2543}25442545int cs = window_get_current_screen(p_window);2546Point2 pos = screen_get_position(cs) + _get_screens_origin();2547Size2 size = screen_get_size(cs);25482549wd.fullscreen = true;2550wd.maximized = false;2551wd.minimized = false;25522553_update_window_style(p_window, false);25542555int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;2556MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);25572558// If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode.2559// Save number of trails so we can restore when exiting, then turn off mouse trails2560SystemParametersInfoA(SPI_GETMOUSETRAILS, 0, &restore_mouse_trails, 0);2561if (restore_mouse_trails > 1) {2562SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, nullptr, 0);2563}2564}2565_update_window_mouse_passthrough(p_window);2566}25672568DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {2569_THREAD_SAFE_METHOD_25702571ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);2572const WindowData &wd = windows[p_window];25732574if (wd.fullscreen) {2575if (wd.multiwindow_fs) {2576return WINDOW_MODE_FULLSCREEN;2577} else {2578return WINDOW_MODE_EXCLUSIVE_FULLSCREEN;2579}2580} else if (wd.minimized) {2581return WINDOW_MODE_MINIMIZED;2582} else if (wd.maximized) {2583return WINDOW_MODE_MAXIMIZED;2584} else {2585return WINDOW_MODE_WINDOWED;2586}2587}25882589bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const {2590_THREAD_SAFE_METHOD_25912592ERR_FAIL_COND_V(!windows.has(p_window), false);2593const WindowData &wd = windows[p_window];25942595const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);2596return (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX;2597}25982599void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {2600_THREAD_SAFE_METHOD_26012602ERR_FAIL_COND(!windows.has(p_window));2603WindowData &wd = windows[p_window];2604switch (p_flag) {2605case WINDOW_FLAG_MINIMIZE_DISABLED: {2606wd.no_min_btn = p_enabled;2607_update_window_style(p_window);2608} break;2609case WINDOW_FLAG_MAXIMIZE_DISABLED: {2610wd.no_max_btn = p_enabled;2611_update_window_style(p_window);2612} break;2613case WINDOW_FLAG_RESIZE_DISABLED: {2614if (p_enabled && wd.parent_hwnd) {2615print_line("Embedded window resize can't be disabled.");2616return;2617}2618wd.resizable = !p_enabled;2619_update_window_style(p_window);2620} break;2621case WINDOW_FLAG_BORDERLESS: {2622wd.borderless = p_enabled;2623_update_window_mouse_passthrough(p_window);2624_update_window_style(p_window);2625ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.2626} break;2627case WINDOW_FLAG_ALWAYS_ON_TOP: {2628ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top.");2629if (p_enabled && wd.parent_hwnd) {2630print_line("Embedded window can't become on top.");2631return;2632}2633wd.always_on_top = p_enabled;2634_update_window_style(p_window);2635} break;2636case WINDOW_FLAG_SHARP_CORNERS: {2637wd.sharp_corners = p_enabled;2638DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;2639::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));2640_update_window_style(p_window);2641} break;2642case WINDOW_FLAG_TRANSPARENT: {2643if (p_enabled) {2644// Enable per-pixel alpha.2645if (OS::get_singleton()->is_layered_allowed()) {2646DWM_BLURBEHIND bb;2647ZeroMemory(&bb, sizeof(bb));2648HRGN hRgn = CreateRectRgn(0, 0, -1, -1);2649bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;2650bb.hRgnBlur = hRgn;2651bb.fEnable = TRUE;2652DwmEnableBlurBehindWindow(wd.hWnd, &bb);2653}2654wd.layered_window = true;2655} else {2656// Disable per-pixel alpha.2657wd.layered_window = false;2658if (OS::get_singleton()->is_layered_allowed()) {2659DWM_BLURBEHIND bb;2660ZeroMemory(&bb, sizeof(bb));2661HRGN hRgn = CreateRectRgn(0, 0, -1, -1);2662bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;2663bb.hRgnBlur = hRgn;2664bb.fEnable = FALSE;2665DwmEnableBlurBehindWindow(wd.hWnd, &bb);2666}2667}2668} break;2669case WINDOW_FLAG_NO_FOCUS: {2670wd.no_focus = p_enabled;2671_update_window_style(p_window);2672} break;2673case WINDOW_FLAG_MOUSE_PASSTHROUGH: {2674wd.mpass = p_enabled;2675} break;2676case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {2677wd.hide_from_capture = p_enabled;2678if (p_enabled) {2679if (os_ver.dwBuildNumber >= 19041) {2680SetWindowDisplayAffinity(wd.hWnd, WDA_EXCLUDEFROMCAPTURE);2681} else {2682SetWindowDisplayAffinity(wd.hWnd, WDA_MONITOR);2683}2684} else {2685SetWindowDisplayAffinity(wd.hWnd, WDA_NONE);2686}2687} break;2688case WINDOW_FLAG_POPUP: {2689ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");2690ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");2691if (p_enabled && wd.parent_hwnd) {2692print_line("Embedded window can't be popup.");2693return;2694}2695wd.is_popup = p_enabled;2696} break;2697default:2698break;2699}2700}27012702bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window) const {2703_THREAD_SAFE_METHOD_27042705ERR_FAIL_COND_V(!windows.has(p_window), false);2706const WindowData &wd = windows[p_window];2707switch (p_flag) {2708case WINDOW_FLAG_MAXIMIZE_DISABLED: {2709return wd.no_max_btn;2710} break;2711case WINDOW_FLAG_MINIMIZE_DISABLED: {2712return wd.no_min_btn;2713} break;2714case WINDOW_FLAG_RESIZE_DISABLED: {2715return !wd.resizable;2716} break;2717case WINDOW_FLAG_BORDERLESS: {2718return wd.borderless;2719} break;2720case WINDOW_FLAG_ALWAYS_ON_TOP: {2721return wd.always_on_top;2722} break;2723case WINDOW_FLAG_SHARP_CORNERS: {2724return wd.sharp_corners;2725} break;2726case WINDOW_FLAG_TRANSPARENT: {2727return wd.layered_window;2728} break;2729case WINDOW_FLAG_NO_FOCUS: {2730return wd.no_focus;2731} break;2732case WINDOW_FLAG_MOUSE_PASSTHROUGH: {2733return wd.mpass;2734} break;2735case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {2736return wd.hide_from_capture;2737} break;2738case WINDOW_FLAG_POPUP: {2739return wd.is_popup;2740} break;2741default:2742break;2743}27442745return false;2746}27472748void DisplayServerWindows::window_request_attention(WindowID p_window) {2749_THREAD_SAFE_METHOD_27502751ERR_FAIL_COND(!windows.has(p_window));2752const WindowData &wd = windows[p_window];27532754FLASHWINFO info;2755info.cbSize = sizeof(FLASHWINFO);2756info.hwnd = wd.hWnd;2757info.dwFlags = FLASHW_ALL;2758info.dwTimeout = 0;2759info.uCount = 2;2760FlashWindowEx(&info);2761}27622763void DisplayServerWindows::window_move_to_foreground(WindowID p_window) {2764_THREAD_SAFE_METHOD_27652766ERR_FAIL_COND(!windows.has(p_window));2767WindowData &wd = windows[p_window];27682769if (!wd.no_focus && !wd.is_popup) {2770SetForegroundWindow(wd.hWnd);2771}2772}27732774bool DisplayServerWindows::window_is_focused(WindowID p_window) const {2775_THREAD_SAFE_METHOD_27762777ERR_FAIL_COND_V(!windows.has(p_window), false);2778const WindowData &wd = windows[p_window];27792780return wd.window_focused;2781}27822783DisplayServerWindows::WindowID DisplayServerWindows::get_focused_window() const {2784return last_focused_window;2785}27862787bool DisplayServerWindows::window_can_draw(WindowID p_window) const {2788_THREAD_SAFE_METHOD_27892790ERR_FAIL_COND_V(!windows.has(p_window), false);2791const WindowData &wd = windows[p_window];2792return !wd.minimized;2793}27942795bool DisplayServerWindows::can_any_window_draw() const {2796_THREAD_SAFE_METHOD_27972798for (const KeyValue<WindowID, WindowData> &E : windows) {2799if (!E.value.minimized) {2800return true;2801}2802}28032804return false;2805}28062807int DisplayServerWindows::accessibility_should_increase_contrast() const {2808HIGHCONTRASTA hc;2809hc.cbSize = sizeof(HIGHCONTRAST);2810if (!SystemParametersInfoA(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &hc, 0)) {2811return -1;2812}2813return (hc.dwFlags & HCF_HIGHCONTRASTON);2814}28152816int DisplayServerWindows::accessibility_should_reduce_animation() const {2817BOOL anim_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2818if (!SystemParametersInfoA(SPI_GETCLIENTAREAANIMATION, 0, &anim_enabled, 0)) {2819return -1;2820}2821return (!anim_enabled);2822}28232824int DisplayServerWindows::accessibility_should_reduce_transparency() const {2825BOOL tr_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2826if (!SystemParametersInfoA(SPI_GETDISABLEOVERLAPPEDCONTENT, 0, &tr_enabled, 0)) {2827return -1;2828}2829return tr_enabled;2830}28312832int DisplayServerWindows::accessibility_screen_reader_active() const {2833BOOL sr_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.2834if (SystemParametersInfoA(SPI_GETSCREENREADER, 0, &sr_enabled, 0) && sr_enabled) {2835return true;2836}28372838static const WCHAR *narrator_mutex_name = L"NarratorRunning";2839HANDLE narrator_mutex = OpenMutexW(MUTEX_ALL_ACCESS, false, narrator_mutex_name);2840if (narrator_mutex) {2841CloseHandle(narrator_mutex);2842return true;2843}2844return false;2845}28462847Vector2i DisplayServerWindows::ime_get_selection() const {2848_THREAD_SAFE_METHOD_28492850DisplayServer::WindowID window_id = _get_focused_window_or_popup();2851const WindowData &wd = windows[window_id];2852if (!wd.ime_active) {2853return Vector2i();2854}28552856int cursor = ImmGetCompositionStringW(wd.im_himc, GCS_CURSORPOS, nullptr, 0);28572858int32_t length = ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, nullptr, 0);2859wchar_t *string = reinterpret_cast<wchar_t *>(memalloc(length));2860ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);28612862int32_t utf32_cursor = 0;2863for (int32_t i = 0; i < length / int32_t(sizeof(wchar_t)); i++) {2864if ((string[i] & 0xfffffc00) == 0xd800) {2865i++;2866}2867if (i < cursor) {2868utf32_cursor++;2869} else {2870break;2871}2872}28732874memdelete(string);28752876return Vector2i(utf32_cursor, 0);2877}28782879String DisplayServerWindows::ime_get_text() const {2880_THREAD_SAFE_METHOD_28812882DisplayServer::WindowID window_id = _get_focused_window_or_popup();2883const WindowData &wd = windows[window_id];2884if (!wd.ime_active) {2885return String();2886}28872888String ret;2889int32_t length = ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, nullptr, 0);2890wchar_t *string = reinterpret_cast<wchar_t *>(memalloc(length));2891ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);2892ret.append_utf16((char16_t *)string, length / sizeof(wchar_t));28932894memdelete(string);28952896return ret;2897}28982899void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p_window) {2900_THREAD_SAFE_METHOD_29012902ERR_FAIL_COND(!windows.has(p_window));2903WindowData &wd = windows[p_window];29042905if (p_active) {2906wd.ime_active = true;2907ImmAssociateContext(wd.hWnd, wd.im_himc);2908CreateCaret(wd.hWnd, nullptr, 1, 1);2909window_set_ime_position(wd.im_position, p_window);2910} else {2911ImmAssociateContext(wd.hWnd, (HIMC) nullptr);2912DestroyCaret();2913wd.ime_active = false;2914}2915}29162917void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {2918_THREAD_SAFE_METHOD_29192920ERR_FAIL_COND(!windows.has(p_window));2921WindowData &wd = windows[p_window];29222923wd.im_position = p_pos;29242925HIMC himc = ImmGetContext(wd.hWnd);2926if (himc == (HIMC) nullptr) {2927return;2928}29292930COMPOSITIONFORM cps;2931cps.dwStyle = CFS_POINT;2932cps.ptCurrentPos.x = wd.im_position.x;2933cps.ptCurrentPos.y = wd.im_position.y;2934ImmSetCompositionWindow(himc, &cps);2935ImmReleaseContext(wd.hWnd, himc);2936}29372938void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {2939_THREAD_SAFE_METHOD_29402941ERR_FAIL_INDEX(p_shape, CURSOR_MAX);29422943if (cursor_shape == p_shape) {2944return;2945}29462947if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {2948cursor_shape = p_shape;2949return;2950}29512952static const LPCTSTR win_cursors[CURSOR_MAX] = {2953IDC_ARROW,2954IDC_IBEAM,2955IDC_HAND, // Finger.2956IDC_CROSS,2957IDC_WAIT,2958IDC_APPSTARTING,2959IDC_SIZEALL,2960IDC_ARROW,2961IDC_NO,2962IDC_SIZENS,2963IDC_SIZEWE,2964IDC_SIZENESW,2965IDC_SIZENWSE,2966IDC_SIZEALL,2967IDC_SIZENS,2968IDC_SIZEWE,2969IDC_HELP2970};29712972if (cursors_cache.has(p_shape)) {2973SetCursor(cursors[p_shape]);2974} else {2975SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));2976}29772978cursor_shape = p_shape;2979}29802981DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {2982return cursor_shape;2983}29842985void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {2986_THREAD_SAFE_METHOD_29872988ERR_FAIL_INDEX(p_shape, CURSOR_MAX);29892990if (p_cursor.is_valid()) {2991RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);29922993if (cursor_c) {2994if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {2995cursor_set_shape(p_shape);2996return;2997}29982999cursors_cache.erase(p_shape);3000}30013002Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);3003ERR_FAIL_COND(image.is_null());3004Vector2i texture_size = image->get_size();30053006UINT image_size = texture_size.width * texture_size.height;30073008// Create the BITMAP with alpha channel.3009COLORREF *buffer = nullptr;30103011BITMAPV5HEADER bi;3012ZeroMemory(&bi, sizeof(bi));3013bi.bV5Size = sizeof(bi);3014bi.bV5Width = texture_size.width;3015bi.bV5Height = -texture_size.height;3016bi.bV5Planes = 1;3017bi.bV5BitCount = 32;3018bi.bV5Compression = BI_BITFIELDS;3019bi.bV5RedMask = 0x00ff0000;3020bi.bV5GreenMask = 0x0000ff00;3021bi.bV5BlueMask = 0x000000ff;3022bi.bV5AlphaMask = 0xff000000;30233024HDC dc = GetDC(nullptr);3025HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);3026HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr);30273028bool fully_transparent = true;3029for (UINT index = 0; index < image_size; index++) {3030int row_index = std::floor(index / texture_size.width);3031int column_index = index % int(texture_size.width);30323033const Color &c = image->get_pixel(column_index, row_index);3034fully_transparent = fully_transparent && (c.a == 0.f);30353036*(buffer + index) = c.to_argb32();3037}30383039// Finally, create the icon.3040if (cursors[p_shape]) {3041DestroyIcon(cursors[p_shape]);3042}30433044if (fully_transparent) {3045cursors[p_shape] = nullptr;3046} else {3047ICONINFO iconinfo;3048iconinfo.fIcon = FALSE;3049iconinfo.xHotspot = p_hotspot.x;3050iconinfo.yHotspot = p_hotspot.y;3051iconinfo.hbmMask = mask;3052iconinfo.hbmColor = bitmap;3053cursors[p_shape] = CreateIconIndirect(&iconinfo);3054}30553056Vector<Variant> params;3057params.push_back(p_cursor);3058params.push_back(p_hotspot);3059cursors_cache.insert(p_shape, params);30603061if (p_shape == cursor_shape) {3062if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {3063SetCursor(cursors[p_shape]);3064}3065}30663067DeleteObject(mask);3068DeleteObject(bitmap);3069ReleaseDC(nullptr, dc);3070} else {3071// Reset to default system cursor.3072if (cursors[p_shape]) {3073DestroyIcon(cursors[p_shape]);3074}3075cursors[p_shape] = nullptr;30763077cursors_cache.erase(p_shape);30783079CursorShape c = cursor_shape;3080cursor_shape = CURSOR_MAX;3081cursor_set_shape(c);3082}3083}30843085bool DisplayServerWindows::get_swap_cancel_ok() {3086return true;3087}30883089void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {3090_THREAD_SAFE_METHOD_30913092AllowSetForegroundWindow(pid);3093}30943095struct WindowEnumData {3096DWORD process_id;3097HWND parent_hWnd;3098HWND hWnd;3099};31003101static BOOL CALLBACK _enum_proc_find_window_from_process_id_callback(HWND hWnd, LPARAM lParam) {3102WindowEnumData &ed = *(WindowEnumData *)lParam;3103DWORD process_id = 0x0;31043105GetWindowThreadProcessId(hWnd, &process_id);3106if (ed.process_id == process_id) {3107if (GetParent(hWnd) != ed.parent_hWnd) {3108return TRUE;3109}31103111// Found it.3112ed.hWnd = hWnd;3113SetLastError(ERROR_SUCCESS);3114return FALSE;3115}3116// Continue enumeration.3117return TRUE;3118}31193120HWND DisplayServerWindows::_find_window_from_process_id(OS::ProcessID p_pid, HWND p_current_hwnd) {3121DWORD pid = p_pid;3122WindowEnumData ed = { pid, p_current_hwnd, NULL };31233124// First, check our own child, maybe it's already embedded.3125if (!EnumChildWindows(p_current_hwnd, _enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {3126if (ed.hWnd) {3127return ed.hWnd;3128}3129}31303131// Then check all the opened windows on the computer.3132if (!EnumWindows(_enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {3133return ed.hWnd;3134}31353136return NULL;3137}31383139Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {3140_THREAD_SAFE_METHOD_31413142ERR_FAIL_COND_V(!windows.has(p_window), FAILED);31433144const WindowData &wd = windows[p_window];31453146EmbeddedProcessData *ep = nullptr;3147if (embedded_processes.has(p_pid)) {3148ep = embedded_processes.get(p_pid);3149} else {3150// New process, trying to find the window.3151HWND handle_to_embed = _find_window_from_process_id(p_pid, wd.hWnd);3152if (!handle_to_embed) {3153return ERR_DOES_NOT_EXIST;3154}31553156const DWORD style = GetWindowLongPtr(handle_to_embed, GWL_STYLE);31573158ep = memnew(EmbeddedProcessData);3159ep->window_handle = handle_to_embed;3160ep->parent_window_handle = wd.hWnd;3161ep->is_visible = (style & WS_VISIBLE) == WS_VISIBLE;31623163embedded_processes.insert(p_pid, ep);3164}31653166if (p_rect.size.x <= 100 || p_rect.size.y <= 100) {3167p_visible = false;3168}31693170// In Godot, the window position is offset by the screen's origin coordinates.3171// We need to adjust for this when a screen is positioned in the negative space3172// (e.g., a screen to the left of the main screen).3173const Rect2i adjusted_rect = Rect2i(p_rect.position + _get_screens_origin(), p_rect.size);31743175// Use HWND_BOTTOM to prevent reordering of the embedded window over another popup.3176SetWindowPos(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);31773178if (ep->is_visible != p_visible) {3179if (p_visible) {3180ShowWindow(ep->window_handle, SW_SHOWNA);3181} else {3182ShowWindow(ep->window_handle, SW_HIDE);3183}3184ep->is_visible = p_visible;3185}31863187if (p_grab_focus) {3188SetForegroundWindow(ep->window_handle);3189SetFocus(ep->window_handle);3190}31913192return OK;3193}31943195Error DisplayServerWindows::request_close_embedded_process(OS::ProcessID p_pid) {3196_THREAD_SAFE_METHOD_31973198if (!embedded_processes.has(p_pid)) {3199return ERR_DOES_NOT_EXIST;3200}32013202EmbeddedProcessData *ep = embedded_processes.get(p_pid);32033204// Send a close message to gracefully close the process.3205PostMessage(ep->window_handle, WM_CLOSE, 0, 0);32063207return OK;3208}32093210Error DisplayServerWindows::remove_embedded_process(OS::ProcessID p_pid) {3211_THREAD_SAFE_METHOD_32123213if (!embedded_processes.has(p_pid)) {3214return ERR_DOES_NOT_EXIST;3215}32163217EmbeddedProcessData *ep = embedded_processes.get(p_pid);32183219request_close_embedded_process(p_pid);32203221// This is a workaround to ensure the parent window correctly regains focus after the3222// embedded window is closed. When the embedded window is closed while it has focus,3223// the parent window (the editor) does not become active. It appears focused but is not truly activated.3224// Opening a new window and closing it forces Windows to set the focus and activation correctly.3225DWORD style = WS_POPUP | WS_VISIBLE;3226DWORD style_ex = WS_EX_TOPMOST;32273228WNDCLASSW wcTemp = {};3229wcTemp.lpfnWndProc = DefWindowProcW;3230wcTemp.hInstance = GetModuleHandle(nullptr);3231wcTemp.lpszClassName = L"Engine temp window";3232RegisterClassW(&wcTemp);32333234HWND hWnd = CreateWindowExW(3235style_ex,3236L"Engine temp window", L"",3237style,32380,32390,32401,32411,3242ep->parent_window_handle,3243nullptr,3244GetModuleHandle(nullptr),3245nullptr);32463247SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);32483249DestroyWindow(hWnd);3250UnregisterClassW(L"Engine temp window", GetModuleHandle(nullptr));32513252SetForegroundWindow(ep->parent_window_handle);32533254embedded_processes.erase(p_pid);3255memdelete(ep);32563257return OK;3258}32593260OS::ProcessID DisplayServerWindows::get_focused_process_id() {3261HWND hwnd = GetForegroundWindow();3262if (!hwnd) {3263return 0;3264}32653266// Get the process ID of the window.3267DWORD processID;3268GetWindowThreadProcessId(hwnd, &processID);32693270return processID;3271}32723273static HRESULT CALLBACK win32_task_dialog_callback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) {3274if (msg == TDN_CREATED) {3275// To match the input text dialog.3276SendMessageW(hwnd, WM_SETICON, ICON_BIG, 0);3277SendMessageW(hwnd, WM_SETICON, ICON_SMALL, 0);3278}32793280return 0;3281}32823283Error DisplayServerWindows::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {3284_THREAD_SAFE_METHOD_32853286TASKDIALOGCONFIG config;3287ZeroMemory(&config, sizeof(TASKDIALOGCONFIG));3288config.cbSize = sizeof(TASKDIALOGCONFIG);32893290Char16String title = p_title.utf16();3291Char16String message = p_description.utf16();3292LocalVector<Char16String> buttons;3293for (String s : p_buttons) {3294buttons.push_back(s.utf16());3295}32963297WindowID window_id = _get_focused_window_or_popup();3298if (!windows.has(window_id)) {3299window_id = MAIN_WINDOW_ID;3300}33013302config.pszWindowTitle = (LPCWSTR)(title.get_data());3303config.pszContent = (LPCWSTR)(message.get_data());3304config.hwndParent = windows[window_id].hWnd;33053306const int button_count = buttons.size();3307config.cButtons = button_count;33083309// No dynamic stack array size :(3310TASKDIALOG_BUTTON *tbuttons = button_count != 0 ? (TASKDIALOG_BUTTON *)alloca(sizeof(TASKDIALOG_BUTTON) * button_count) : nullptr;3311if (tbuttons) {3312for (int i = 0; i < button_count; i++) {3313tbuttons[i].nButtonID = i + 100;3314tbuttons[i].pszButtonText = (LPCWSTR)(buttons[i].get_data());3315}3316}3317config.pButtons = tbuttons;3318config.pfCallback = win32_task_dialog_callback;33193320Error result = FAILED;3321HMODULE comctl = LoadLibraryW(L"comctl32.dll");3322if (comctl) {3323typedef HRESULT(WINAPI * TaskDialogIndirectPtr)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);33243325TaskDialogIndirectPtr task_dialog_indirect = (TaskDialogIndirectPtr)(void *)GetProcAddress(comctl, "TaskDialogIndirect");3326int button_pressed;33273328if (task_dialog_indirect && SUCCEEDED(task_dialog_indirect(&config, &button_pressed, nullptr, nullptr))) {3329if (p_callback.is_valid()) {3330Variant button = button_pressed - 100;3331const Variant *args[1] = { &button };3332Variant ret;3333Callable::CallError ce;3334p_callback.callp(args, 1, ret, ce);3335if (ce.error != Callable::CallError::CALL_OK) {3336ERR_PRINT(vformat("Failed to execute dialog callback: %s.", Variant::get_callable_error_text(p_callback, args, 1, ce)));3337}3338}33393340result = OK;3341}3342FreeLibrary(comctl);3343} else {3344ERR_PRINT("Unable to create native dialog.");3345}33463347return result;3348}33493350struct Win32InputTextDialogInit {3351const char16_t *title;3352const char16_t *description;3353const char16_t *partial;3354const Callable &callback;3355};33563357static int scale_with_dpi(int p_pos, int p_dpi) {3358return IsProcessDPIAware() ? (p_pos * p_dpi / 96) : p_pos;3359}33603361static INT_PTR input_text_dialog_init(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3362Win32InputTextDialogInit init = *(Win32InputTextDialogInit *)lParam;3363SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)&init.callback); // Set dialog callback.33643365SetWindowTextW(hWnd, (LPCWSTR)init.title);33663367const int dpi = DisplayServerWindows::get_singleton()->screen_get_dpi();33683369const int margin = scale_with_dpi(7, dpi);3370const SIZE dlg_size = { scale_with_dpi(300, dpi), scale_with_dpi(50, dpi) };33713372int str_len = lstrlenW((LPCWSTR)init.description);3373SIZE str_size = { dlg_size.cx, 0 };3374if (str_len > 0) {3375HDC hdc = GetDC(nullptr);3376RECT trect = { margin, margin, margin + dlg_size.cx, margin + dlg_size.cy };3377SelectObject(hdc, (HFONT)SendMessageW(hWnd, WM_GETFONT, 0, 0));33783379// `+ margin` adds some space between the static text and the edit field.3380// Don't scale this with DPI because DPI is already handled by DrawText.3381str_size.cy = DrawTextW(hdc, (LPCWSTR)init.description, str_len, &trect, DT_LEFT | DT_WORDBREAK | DT_CALCRECT) + margin;33823383ReleaseDC(nullptr, hdc);3384}33853386RECT crect, wrect;3387GetClientRect(hWnd, &crect);3388GetWindowRect(hWnd, &wrect);3389int sw = GetSystemMetrics(SM_CXSCREEN);3390int sh = GetSystemMetrics(SM_CYSCREEN);3391int new_width = dlg_size.cx + margin * 2 + wrect.right - wrect.left - crect.right;3392int new_height = dlg_size.cy + margin * 2 + wrect.bottom - wrect.top - crect.bottom + str_size.cy;33933394MoveWindow(hWnd, (sw - new_width) / 2, (sh - new_height) / 2, new_width, new_height, true);33953396HWND ok_button = GetDlgItem(hWnd, 1);3397MoveWindow(ok_button,3398dlg_size.cx + margin - scale_with_dpi(65, dpi),3399dlg_size.cy + str_size.cy + margin - scale_with_dpi(20, dpi),3400scale_with_dpi(65, dpi), scale_with_dpi(20, dpi), true);34013402HWND description = GetDlgItem(hWnd, 3);3403MoveWindow(description, margin, margin, dlg_size.cx, str_size.cy, true);3404SetWindowTextW(description, (LPCWSTR)init.description);34053406HWND text_edit = GetDlgItem(hWnd, 2);3407MoveWindow(text_edit, margin, str_size.cy + margin, dlg_size.cx, scale_with_dpi(20, dpi), true);3408SetWindowTextW(text_edit, (LPCWSTR)init.partial);34093410return TRUE;3411}34123413static INT_PTR input_text_dialog_cmd_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3414if (LOWORD(wParam) == 1) {3415HWND text_edit = GetDlgItem(hWnd, 2);3416ERR_FAIL_NULL_V(text_edit, false);34173418Char16String text;3419text.resize_uninitialized(GetWindowTextLengthW(text_edit) + 1);3420GetWindowTextW(text_edit, (LPWSTR)text.get_data(), text.size());34213422const Callable *callback = (const Callable *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);3423if (callback && callback->is_valid()) {3424Variant v_result = String((const wchar_t *)text.get_data());3425Variant ret;3426Callable::CallError ce;3427const Variant *args[1] = { &v_result };34283429callback->callp(args, 1, ret, ce);3430if (ce.error != Callable::CallError::CALL_OK) {3431ERR_PRINT(vformat("Failed to execute input dialog callback: %s.", Variant::get_callable_error_text(*callback, args, 1, ce)));3432}3433}34343435return EndDialog(hWnd, 0);3436}34373438return false;3439}34403441static INT_PTR CALLBACK input_text_dialog_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {3442switch (code) {3443case WM_INITDIALOG:3444return input_text_dialog_init(hWnd, code, wParam, lParam);34453446case WM_COMMAND:3447return input_text_dialog_cmd_proc(hWnd, code, wParam, lParam);34483449default:3450return FALSE;3451}3452}34533454Error DisplayServerWindows::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {3455#pragma pack(push, 1)34563457// NOTE: Use default/placeholder coordinates here. Windows uses its own coordinate system3458// specifically for dialogs which relies on font sizes instead of pixels.3459const struct {3460WORD dlgVer; // must be 13461WORD signature; // must be 0xFFFF3462DWORD helpID;3463DWORD exStyle;3464DWORD style;3465WORD cDlgItems;3466short x;3467short y;3468short cx;3469short cy;3470WCHAR menu[1]; // must be 03471WCHAR windowClass[7]; // must be "#32770" -- the default window class for dialogs3472WCHAR title[1]; // must be 03473WORD pointsize;3474WORD weight;3475BYTE italic;3476BYTE charset;3477WCHAR font[13]; // must be "MS Shell Dlg"3478} template_base = {34791, 0xFFFF, 0, 0,3480DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION,34813, 0, 0, 20, 20, L"", L"#32770", L"", 8, FW_NORMAL, 0, DEFAULT_CHARSET, L"MS Shell Dlg"3482};34833484const struct {3485DWORD helpID;3486DWORD exStyle;3487DWORD style;3488short x;3489short y;3490short cx;3491short cy;3492DWORD id;3493WCHAR windowClass[7]; // must be "Button"3494WCHAR title[3]; // must be "OK"3495WORD extraCount;3496} ok_button = {34970, 0, WS_VISIBLE | BS_DEFPUSHBUTTON, 0, 0, 50, 14, 1, WC_BUTTONW, L"OK", 03498};3499const struct {3500DWORD helpID;3501DWORD exStyle;3502DWORD style;3503short x;3504short y;3505short cx;3506short cy;3507DWORD id;3508WCHAR windowClass[5]; // must be "Edit"3509WCHAR title[1]; // must be 03510WORD extraCount;3511} text_field = {35120, 0, WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 0, 0, 250, 14, 2, WC_EDITW, L"", 03513};3514const struct {3515DWORD helpID;3516DWORD exStyle;3517DWORD style;3518short x;3519short y;3520short cx;3521short cy;3522DWORD id;3523WCHAR windowClass[7]; // must be "Static"3524WCHAR title[1]; // must be 03525WORD extraCount;3526} static_text = {35270, 0, WS_VISIBLE, 0, 0, 250, 14, 3, WC_STATICW, L"", 03528};35293530#pragma pack(pop)35313532// Dialog template3533const size_t data_size = sizeof(template_base) + (sizeof(template_base) % 4) +3534sizeof(ok_button) + (sizeof(ok_button) % 4) +3535sizeof(text_field) + (sizeof(text_field) % 4) +3536sizeof(static_text) + (sizeof(static_text) % 4);35373538void *data_template = memalloc(data_size);3539ERR_FAIL_NULL_V_MSG(data_template, FAILED, "Unable to allocate memory for the dialog template.");3540ZeroMemory(data_template, data_size);35413542char *current_block = (char *)data_template;3543CopyMemory(current_block, &template_base, sizeof(template_base));3544current_block += sizeof(template_base) + (sizeof(template_base) % 4);3545CopyMemory(current_block, &ok_button, sizeof(ok_button));3546current_block += sizeof(ok_button) + (sizeof(ok_button) % 4);3547CopyMemory(current_block, &text_field, sizeof(text_field));3548current_block += sizeof(text_field) + (sizeof(text_field) % 4);3549CopyMemory(current_block, &static_text, sizeof(static_text));35503551Char16String title16 = p_title.utf16();3552Char16String description16 = p_description.utf16();3553Char16String partial16 = p_partial.utf16();35543555Win32InputTextDialogInit init = {3556title16.get_data(), description16.get_data(), partial16.get_data(), p_callback3557};35583559// No modal dialogs for specific windows? Assume main window here.3560INT_PTR ret = DialogBoxIndirectParamW(hInstance, (LPDLGTEMPLATEW)data_template, nullptr, (DLGPROC)input_text_dialog_proc, (LPARAM)(&init));35613562Error result = ret != -1 ? OK : FAILED;3563memfree(data_template);35643565if (result == FAILED) {3566ERR_PRINT("Unable to create native dialog.");3567}3568return result;3569}35703571int DisplayServerWindows::keyboard_get_layout_count() const {3572return GetKeyboardLayoutList(0, nullptr);3573}35743575int DisplayServerWindows::keyboard_get_current_layout() const {3576HKL cur_layout = GetKeyboardLayout(0);35773578int layout_count = GetKeyboardLayoutList(0, nullptr);3579HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3580GetKeyboardLayoutList(layout_count, layouts);35813582for (int i = 0; i < layout_count; i++) {3583if (cur_layout == layouts[i]) {3584memfree(layouts);3585return i;3586}3587}3588memfree(layouts);3589return -1;3590}35913592void DisplayServerWindows::keyboard_set_current_layout(int p_index) {3593int layout_count = GetKeyboardLayoutList(0, nullptr);35943595ERR_FAIL_INDEX(p_index, layout_count);35963597HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3598GetKeyboardLayoutList(layout_count, layouts);3599ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS);3600memfree(layouts);3601}36023603String DisplayServerWindows::keyboard_get_layout_language(int p_index) const {3604int layout_count = GetKeyboardLayoutList(0, nullptr);36053606ERR_FAIL_INDEX_V(p_index, layout_count, "");36073608HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3609GetKeyboardLayoutList(layout_count, layouts);36103611WCHAR buf[LOCALE_NAME_MAX_LENGTH];3612memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));3613LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);36143615memfree(layouts);36163617return String::utf16((const char16_t *)buf).substr(0, 2);3618}36193620Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) const {3621Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;3622Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);36233624if (keycode_no_mod == Key::PRINT ||3625keycode_no_mod == Key::KP_ADD ||3626keycode_no_mod == Key::KP_5 ||3627(keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {3628return p_keycode;3629}36303631unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);3632if (scancode == 0) {3633return p_keycode;3634}36353636HKL current_layout = GetKeyboardLayout(0);3637UINT vk = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK, current_layout);3638if (vk == 0) {3639return p_keycode;3640}36413642UINT char_code = MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, current_layout) & 0x7FFF;3643// Unlike a similar Linux/BSD check which matches full Latin-1 range,3644// we limit these to ASCII to fix some layouts, including Arabic ones3645if (char_code >= 32 && char_code <= 127) {3646// Godot uses 'braces' instead of 'brackets'3647if (char_code == (unsigned int)Key::BRACKETLEFT || char_code == (unsigned int)Key::BRACKETRIGHT) {3648char_code += 32;3649}3650return (Key)(char_code | (unsigned int)modifiers);3651}36523653return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers);3654}36553656Key DisplayServerWindows::keyboard_get_label_from_physical(Key p_keycode) const {3657Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;3658Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);36593660if (keycode_no_mod == Key::PRINT ||3661keycode_no_mod == Key::KP_ADD ||3662keycode_no_mod == Key::KP_5 ||3663(keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {3664return p_keycode;3665}36663667unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);3668if (scancode == 0) {3669return p_keycode;3670}36713672Key keycode = KeyMappingWindows::get_keysym(MapVirtualKey(scancode, MAPVK_VSC_TO_VK));36733674HKL current_layout = GetKeyboardLayout(0);3675static BYTE keyboard_state[256];3676memset(keyboard_state, 0, 256);3677wchar_t chars[256] = {};3678UINT extended_code = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);3679if (ToUnicodeEx(extended_code, scancode, keyboard_state, chars, 255, 4, current_layout) > 0) {3680String keysym = String::utf16((char16_t *)chars, 255);3681if (!keysym.is_empty()) {3682return fix_key_label(keysym[0], keycode) | modifiers;3683}3684}3685return p_keycode;3686}36873688void DisplayServerWindows::show_emoji_and_symbol_picker() const {3689// Send Win + Period shortcut, there's no non-WinRT public API.36903691INPUT input[4] = {};3692input[0].type = INPUT_KEYBOARD; // Win down.3693input[0].ki.wVk = VK_LWIN;36943695input[1].type = INPUT_KEYBOARD; // Period down.3696input[1].ki.wVk = VK_OEM_PERIOD;36973698input[2].type = INPUT_KEYBOARD; // Win up.3699input[2].ki.wVk = VK_LWIN;3700input[2].ki.dwFlags = KEYEVENTF_KEYUP;37013702input[3].type = INPUT_KEYBOARD; // Period up.3703input[3].ki.wVk = VK_OEM_PERIOD;3704input[3].ki.dwFlags = KEYEVENTF_KEYUP;37053706SendInput(4, input, sizeof(INPUT));3707}37083709String DisplayServerWindows::_get_keyboard_layout_display_name(const String &p_klid) const {3710String ret;3711HKEY key;3712if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {3713return String();3714}37153716WCHAR buffer[MAX_PATH] = {};3717DWORD buffer_size = MAX_PATH;3718if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Display Name", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {3719if (SHLoadIndirectString(buffer, buffer, buffer_size, nullptr) == S_OK) {3720ret = String::utf16((const char16_t *)buffer, buffer_size);3721}3722} else {3723if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Text", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {3724ret = String::utf16((const char16_t *)buffer, buffer_size);3725}3726}37273728RegCloseKey(key);3729return ret;3730}37313732String DisplayServerWindows::_get_klid(HKL p_hkl) const {3733String ret;37343735WORD device = HIWORD(p_hkl);3736if ((device & 0xf000) == 0xf000) {3737WORD layout_id = device & 0x0fff;37383739HKEY key;3740if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {3741return String();3742}37433744DWORD index = 0;3745wchar_t klid_buffer[KL_NAMELENGTH];3746DWORD klid_buffer_size = KL_NAMELENGTH;3747while (RegEnumKeyExW(key, index, klid_buffer, &klid_buffer_size, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) {3748wchar_t layout_id_buf[MAX_PATH] = {};3749DWORD layout_id_size = MAX_PATH;3750if (RegGetValueW(key, klid_buffer, L"Layout Id", RRF_RT_REG_SZ, nullptr, layout_id_buf, &layout_id_size) == ERROR_SUCCESS) {3751if (layout_id == String::utf16((char16_t *)layout_id_buf, layout_id_size).hex_to_int()) {3752ret = String::utf16((const char16_t *)klid_buffer, klid_buffer_size).lpad(8, "0");3753break;3754}3755}3756klid_buffer_size = KL_NAMELENGTH;3757++index;3758}37593760RegCloseKey(key);3761} else {3762if (device == 0) {3763device = LOWORD(p_hkl);3764}3765ret = (String::num_uint64((uint64_t)device, 16, false)).lpad(8, "0");3766}37673768return ret;3769}37703771String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {3772int layout_count = GetKeyboardLayoutList(0, nullptr);37733774ERR_FAIL_INDEX_V(p_index, layout_count, "");37753776HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));3777GetKeyboardLayoutList(layout_count, layouts);37783779String 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).3780if (ret.is_empty()) {3781WCHAR buf[LOCALE_NAME_MAX_LENGTH];3782memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));3783LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);37843785WCHAR name[1024];3786memset(name, 0, 1024 * sizeof(WCHAR));3787GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024);37883789ret = String::utf16((const char16_t *)name);3790}3791memfree(layouts);37923793return ret;3794}37953796void DisplayServerWindows::process_events() {3797ERR_FAIL_COND(!Thread::is_main_thread());37983799if (!drop_events) {3800#ifdef SDL_ENABLED3801if (joypad_sdl) {3802joypad_sdl->process_events();3803}3804#endif3805}38063807_THREAD_SAFE_LOCK_3808MSG msg = {};3809while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {3810TranslateMessage(&msg);3811DispatchMessageW(&msg);3812}3813_THREAD_SAFE_UNLOCK_38143815if (tts) {3816tts->process_events();3817}38183819if (!drop_events) {3820_process_key_events();3821Input::get_singleton()->flush_buffered_events();3822}38233824LocalVector<List<FileDialogData *>::Element *> to_remove;3825for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {3826FileDialogData *fd = E->get();3827if (fd->finished.is_set()) {3828if (fd->listener_thread.is_started()) {3829fd->listener_thread.wait_to_finish();3830}3831to_remove.push_back(E);3832}3833}3834for (List<FileDialogData *>::Element *E : to_remove) {3835memdelete(E->get());3836E->erase();3837}3838process_file_dialog_callbacks();3839}38403841void DisplayServerWindows::force_process_and_drop_events() {3842ERR_FAIL_COND(!Thread::is_main_thread());38433844drop_events = true;3845process_events();3846drop_events = false;3847}38483849void DisplayServerWindows::release_rendering_thread() {3850#if defined(GLES3_ENABLED)3851if (gl_manager_angle) {3852gl_manager_angle->release_current();3853}3854if (gl_manager_native) {3855gl_manager_native->release_current();3856}3857#endif3858}38593860void DisplayServerWindows::swap_buffers() {3861#if defined(GLES3_ENABLED)3862if (gl_manager_angle) {3863gl_manager_angle->swap_buffers();3864}3865if (gl_manager_native) {3866gl_manager_native->swap_buffers();3867}3868#endif3869}38703871void DisplayServerWindows::set_native_icon(const String &p_filename) {3872_THREAD_SAFE_METHOD_38733874Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);3875ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'.");38763877ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));3878int pos = 0;38793880icon_dir->idReserved = f->get_32();3881pos += sizeof(WORD);3882f->seek(pos);38833884icon_dir->idType = f->get_32();3885pos += sizeof(WORD);3886f->seek(pos);38873888ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!");38893890icon_dir->idCount = f->get_32();3891pos += sizeof(WORD);3892f->seek(pos);38933894icon_dir = (ICONDIR *)memrealloc(icon_dir, sizeof(ICONDIR) - sizeof(ICONDIRENTRY) + icon_dir->idCount * sizeof(ICONDIRENTRY));3895f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY));38963897int small_icon_index = -1; // Select 16x16 with largest color count.3898int small_icon_cc = 0;3899int big_icon_index = -1; // Select largest.3900int big_icon_width = 16;3901int big_icon_cc = 0;39023903for (int i = 0; i < icon_dir->idCount; i++) {3904int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount;3905int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth;3906if (width == 16) {3907if (colors >= small_icon_cc) {3908small_icon_index = i;3909small_icon_cc = colors;3910}3911}3912if (width >= big_icon_width) {3913if (colors >= big_icon_cc) {3914big_icon_index = i;3915big_icon_width = width;3916big_icon_cc = colors;3917}3918}3919}39203921ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!");39223923if (small_icon_index == -1) {3924WARN_PRINT("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!");3925small_icon_index = big_icon_index;3926small_icon_cc = big_icon_cc;3927}39283929// Read the big icon.3930DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes;3931Vector<uint8_t> data_big;3932data_big.resize(bytecount_big);3933pos = icon_dir->idEntries[big_icon_index].dwImageOffset;3934f->seek(pos);3935f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big);3936HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000);3937ERR_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()) + ".");39383939// Read the small icon.3940DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;3941Vector<uint8_t> data_small;3942data_small.resize(bytecount_small);3943pos = icon_dir->idEntries[small_icon_index].dwImageOffset;3944f->seek(pos);3945f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small);3946HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000);3947ERR_FAIL_NULL_MSG(icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");39483949// Online tradition says to be sure last error is cleared and set the small icon first.3950int err = 0;3951SetLastError(err);39523953SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);3954err = GetLastError();3955ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + ".");39563957SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);3958err = GetLastError();3959ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");39603961memdelete(icon_dir);3962}39633964void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {3965_THREAD_SAFE_METHOD_39663967if (p_icon.is_valid()) {3968ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);39693970Ref<Image> img = p_icon;3971if (img != icon) {3972img = img->duplicate();3973img->convert(Image::FORMAT_RGBA8);3974}39753976int w = img->get_width();3977int h = img->get_height();39783979// Create temporary bitmap buffer.3980int icon_len = 40 + h * w * 4;3981Vector<BYTE> v;3982v.resize(icon_len);3983BYTE *icon_bmp = v.ptrw();39843985encode_uint32(40, &icon_bmp[0]);3986encode_uint32(w, &icon_bmp[4]);3987encode_uint32(h * 2, &icon_bmp[8]);3988encode_uint16(1, &icon_bmp[12]);3989encode_uint16(32, &icon_bmp[14]);3990encode_uint32(BI_RGB, &icon_bmp[16]);3991encode_uint32(w * h * 4, &icon_bmp[20]);3992encode_uint32(0, &icon_bmp[24]);3993encode_uint32(0, &icon_bmp[28]);3994encode_uint32(0, &icon_bmp[32]);3995encode_uint32(0, &icon_bmp[36]);39963997uint8_t *wr = &icon_bmp[40];3998const uint8_t *r = img->get_data().ptr();39994000for (int i = 0; i < h; i++) {4001for (int j = 0; j < w; j++) {4002const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4003uint8_t *wpx = &wr[(i * w + j) * 4];4004wpx[0] = rpx[2];4005wpx[1] = rpx[1];4006wpx[2] = rpx[0];4007wpx[3] = rpx[3];4008}4009}40104011HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);4012ERR_FAIL_NULL(hicon);40134014icon = img;40154016// Set the icon for the window.4017SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);40184019// Set the icon in the task manager (should we do this?).4020SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);4021} else {4022icon = Ref<Image>();4023SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, 0);4024SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, 0);4025}4026}40274028DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) {4029HICON hicon = nullptr;4030if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {4031Ref<Image> img = p_icon->get_image();4032img = img->duplicate();4033if (img->is_compressed()) {4034img->decompress();4035}4036img->convert(Image::FORMAT_RGBA8);40374038int w = img->get_width();4039int h = img->get_height();40404041// Create temporary bitmap buffer.4042int icon_len = 40 + h * w * 4;4043Vector<BYTE> v;4044v.resize(icon_len);4045BYTE *icon_bmp = v.ptrw();40464047encode_uint32(40, &icon_bmp[0]);4048encode_uint32(w, &icon_bmp[4]);4049encode_uint32(h * 2, &icon_bmp[8]);4050encode_uint16(1, &icon_bmp[12]);4051encode_uint16(32, &icon_bmp[14]);4052encode_uint32(BI_RGB, &icon_bmp[16]);4053encode_uint32(w * h * 4, &icon_bmp[20]);4054encode_uint32(0, &icon_bmp[24]);4055encode_uint32(0, &icon_bmp[28]);4056encode_uint32(0, &icon_bmp[32]);4057encode_uint32(0, &icon_bmp[36]);40584059uint8_t *wr = &icon_bmp[40];4060const uint8_t *r = img->get_data().ptr();40614062for (int i = 0; i < h; i++) {4063for (int j = 0; j < w; j++) {4064const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4065uint8_t *wpx = &wr[(i * w + j) * 4];4066wpx[0] = rpx[2];4067wpx[1] = rpx[1];4068wpx[2] = rpx[0];4069wpx[3] = rpx[3];4070}4071}40724073hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);4074}40754076IndicatorData idat;4077idat.callback = p_callback;40784079NOTIFYICONDATAW ndat;4080ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4081ndat.cbSize = sizeof(NOTIFYICONDATAW);4082ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4083ndat.uID = indicator_id_counter;4084ndat.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;4085ndat.uCallbackMessage = WM_INDICATOR_CALLBACK_MESSAGE;4086ndat.hIcon = hicon;4087memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));4088ndat.uVersion = NOTIFYICON_VERSION;40894090Shell_NotifyIconW(NIM_ADD, &ndat);4091Shell_NotifyIconW(NIM_SETVERSION, &ndat);40924093IndicatorID iid = indicator_id_counter++;4094indicators[iid] = idat;40954096return iid;4097}40984099void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon) {4100ERR_FAIL_COND(!indicators.has(p_id));41014102HICON hicon = nullptr;4103if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {4104Ref<Image> img = p_icon->get_image();4105img = img->duplicate();4106if (img->is_compressed()) {4107img->decompress();4108}4109img->convert(Image::FORMAT_RGBA8);41104111int w = img->get_width();4112int h = img->get_height();41134114// Create temporary bitmap buffer.4115int icon_len = 40 + h * w * 4;4116Vector<BYTE> v;4117v.resize(icon_len);4118BYTE *icon_bmp = v.ptrw();41194120encode_uint32(40, &icon_bmp[0]);4121encode_uint32(w, &icon_bmp[4]);4122encode_uint32(h * 2, &icon_bmp[8]);4123encode_uint16(1, &icon_bmp[12]);4124encode_uint16(32, &icon_bmp[14]);4125encode_uint32(BI_RGB, &icon_bmp[16]);4126encode_uint32(w * h * 4, &icon_bmp[20]);4127encode_uint32(0, &icon_bmp[24]);4128encode_uint32(0, &icon_bmp[28]);4129encode_uint32(0, &icon_bmp[32]);4130encode_uint32(0, &icon_bmp[36]);41314132uint8_t *wr = &icon_bmp[40];4133const uint8_t *r = img->get_data().ptr();41344135for (int i = 0; i < h; i++) {4136for (int j = 0; j < w; j++) {4137const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];4138uint8_t *wpx = &wr[(i * w + j) * 4];4139wpx[0] = rpx[2];4140wpx[1] = rpx[1];4141wpx[2] = rpx[0];4142wpx[3] = rpx[3];4143}4144}41454146hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);4147}41484149NOTIFYICONDATAW ndat;4150ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4151ndat.cbSize = sizeof(NOTIFYICONDATAW);4152ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4153ndat.uID = p_id;4154ndat.uFlags = NIF_ICON;4155ndat.hIcon = hicon;4156ndat.uVersion = NOTIFYICON_VERSION;41574158Shell_NotifyIconW(NIM_MODIFY, &ndat);4159}41604161void DisplayServerWindows::status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) {4162ERR_FAIL_COND(!indicators.has(p_id));41634164NOTIFYICONDATAW ndat;4165ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4166ndat.cbSize = sizeof(NOTIFYICONDATAW);4167ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4168ndat.uID = p_id;4169ndat.uFlags = NIF_TIP;4170memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));4171ndat.uVersion = NOTIFYICON_VERSION;41724173Shell_NotifyIconW(NIM_MODIFY, &ndat);4174}41754176void DisplayServerWindows::status_indicator_set_menu(IndicatorID p_id, const RID &p_menu_rid) {4177ERR_FAIL_COND(!indicators.has(p_id));41784179indicators[p_id].menu_rid = p_menu_rid;4180}41814182void DisplayServerWindows::status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) {4183ERR_FAIL_COND(!indicators.has(p_id));41844185indicators[p_id].callback = p_callback;4186}41874188Rect2 DisplayServerWindows::status_indicator_get_rect(IndicatorID p_id) const {4189ERR_FAIL_COND_V(!indicators.has(p_id), Rect2());41904191NOTIFYICONIDENTIFIER nid;4192ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));4193nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);4194nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;4195nid.uID = p_id;4196nid.guidItem = GUID_NULL;41974198RECT rect;4199if (Shell_NotifyIconGetRect(&nid, &rect) != S_OK) {4200return Rect2();4201}4202Rect2 ind_rect = Rect2(Point2(rect.left, rect.top) - _get_screens_origin(), Size2(rect.right - rect.left, rect.bottom - rect.top));4203for (int i = 0; i < get_screen_count(); i++) {4204Rect2 screen_rect = Rect2(screen_get_position(i), screen_get_size(i));4205if (screen_rect.encloses(ind_rect)) {4206return ind_rect;4207}4208}4209return Rect2();4210}42114212void DisplayServerWindows::delete_status_indicator(IndicatorID p_id) {4213ERR_FAIL_COND(!indicators.has(p_id));42144215NOTIFYICONDATAW ndat;4216ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));4217ndat.cbSize = sizeof(NOTIFYICONDATAW);4218ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;4219ndat.uID = p_id;4220ndat.uVersion = NOTIFYICON_VERSION;42214222Shell_NotifyIconW(NIM_DELETE, &ndat);4223indicators.erase(p_id);4224}42254226void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {4227_THREAD_SAFE_METHOD_4228#if defined(RD_ENABLED)4229if (rendering_context) {4230rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);4231}4232#endif42334234#if defined(GLES3_ENABLED)4235if (gl_manager_native) {4236gl_manager_native->set_use_vsync(p_window, p_vsync_mode != DisplayServer::VSYNC_DISABLED);4237}4238if (gl_manager_angle) {4239gl_manager_angle->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);4240}4241#endif4242}42434244DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {4245_THREAD_SAFE_METHOD_4246#if defined(RD_ENABLED)4247if (rendering_context) {4248return rendering_context->window_get_vsync_mode(p_window);4249}4250#endif42514252#if defined(GLES3_ENABLED)4253if (gl_manager_native) {4254return gl_manager_native->is_using_vsync(p_window) ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;4255}4256if (gl_manager_angle) {4257return gl_manager_angle->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;4258}4259#endif4260return DisplayServer::VSYNC_ENABLED;4261}42624263void DisplayServerWindows::window_start_drag(WindowID p_window) {4264_THREAD_SAFE_METHOD_42654266ERR_FAIL_COND(!windows.has(p_window));4267WindowData &wd = windows[p_window];42684269if (wd.parent_hwnd) {4270return; // Embedded window.4271}42724273ReleaseCapture();42744275POINT coords;4276GetCursorPos(&coords);4277ScreenToClient(wd.hWnd, &coords);42784279SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(coords.x, coords.y));42804281for (int btn = (int)MouseButton::LEFT; btn <= (int)MouseButton::MB_XBUTTON2; btn++) {4282if (Input::get_singleton()->is_mouse_button_pressed(MouseButton(btn))) {4283Ref<InputEventMouseButton> mb;4284mb.instantiate();4285mb->set_window_id(p_window);4286mb->set_pressed(false);4287mb->set_button_index(MouseButton::LEFT);4288mb->set_position(Vector2(coords.x, coords.y));4289mb->set_global_position(mb->get_position());4290Input::get_singleton()->parse_input_event(mb);4291}4292}4293}42944295void DisplayServerWindows::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {4296_THREAD_SAFE_METHOD_42974298ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);4299ERR_FAIL_COND(!windows.has(p_window));4300WindowData &wd = windows[p_window];43014302if (wd.parent_hwnd) {4303return; // Embedded window.4304}43054306ReleaseCapture();43074308POINT coords;4309GetCursorPos(&coords);4310ScreenToClient(wd.hWnd, &coords);43114312DWORD op = 0;4313switch (p_edge) {4314case DisplayServer::WINDOW_EDGE_TOP_LEFT: {4315op = WMSZ_TOPLEFT;4316} break;4317case DisplayServer::WINDOW_EDGE_TOP: {4318op = WMSZ_TOP;4319} break;4320case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {4321op = WMSZ_TOPRIGHT;4322} break;4323case DisplayServer::WINDOW_EDGE_LEFT: {4324op = WMSZ_LEFT;4325} break;4326case DisplayServer::WINDOW_EDGE_RIGHT: {4327op = WMSZ_RIGHT;4328} break;4329case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {4330op = WMSZ_BOTTOMLEFT;4331} break;4332case DisplayServer::WINDOW_EDGE_BOTTOM: {4333op = WMSZ_BOTTOM;4334} break;4335case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {4336op = WMSZ_BOTTOMRIGHT;4337} break;4338default:4339break;4340}43414342SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_SIZE | op, MAKELPARAM(coords.x, coords.y));43434344for (int btn = (int)MouseButton::LEFT; btn <= (int)MouseButton::MB_XBUTTON2; btn++) {4345if (Input::get_singleton()->is_mouse_button_pressed(MouseButton(btn))) {4346Ref<InputEventMouseButton> mb;4347mb.instantiate();4348mb->set_window_id(p_window);4349mb->set_pressed(false);4350mb->set_button_index(MouseButton::LEFT);4351mb->set_position(Vector2(coords.x, coords.y));4352mb->set_global_position(mb->get_position());4353Input::get_singleton()->parse_input_event(mb);4354}4355}4356}43574358void DisplayServerWindows::set_context(Context p_context) {4359}43604361bool DisplayServerWindows::is_window_transparency_available() const {4362#if defined(RD_ENABLED)4363if (rendering_device && !rendering_device->is_composite_alpha_supported()) {4364return false;4365}4366#endif4367return OS::get_singleton()->is_layered_allowed();4368}43694370#define MI_WP_SIGNATURE 0xFF5157004371#define SIGNATURE_MASK 0xFFFFFF004372// Keeping the name suggested by Microsoft, but this macro really answers:4373// Is this mouse event emulated from touch or pen input?4374#define IsPenEvent(dw) (((dw) & SIGNATURE_MASK) == MI_WP_SIGNATURE)4375// This one tells whether the event comes from touchscreen (and not from pen).4376#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw) & 0x80))43774378void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx) {4379if (touch_state.has(idx) == p_pressed) {4380return;4381}43824383if (p_pressed) {4384touch_state.insert(idx, Vector2(p_x, p_y));4385} else {4386touch_state.erase(idx);4387}43884389Ref<InputEventScreenTouch> event;4390event.instantiate();4391event->set_index(idx);4392event->set_window_id(p_window);4393event->set_pressed(p_pressed);4394event->set_position(Vector2(p_x, p_y));43954396Input::get_singleton()->parse_input_event(event);4397}43984399void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) {4400RBMap<int, Vector2>::Element *curr = touch_state.find(idx);4401if (!curr) {4402return;4403}44044405if (curr->get() == Vector2(p_x, p_y)) {4406return;4407}44084409Ref<InputEventScreenDrag> event;4410event.instantiate();4411event->set_window_id(p_window);4412event->set_index(idx);4413event->set_position(Vector2(p_x, p_y));4414event->set_relative(Vector2(p_x, p_y) - curr->get());4415event->set_relative_screen_position(event->get_relative());44164417Input::get_singleton()->parse_input_event(event);44184419curr->get() = Vector2(p_x, p_y);4420}44214422void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent p_event) {4423if (wd.event_callback.is_valid()) {4424Variant event = int(p_event);4425wd.event_callback.call(event);4426}4427}44284429void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {4430static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event);4431}44324433void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {4434if (in_dispatch_input_event) {4435return;4436}4437in_dispatch_input_event = true;44384439{4440List<WindowID>::Element *E = popup_list.back();4441if (E && Object::cast_to<InputEventKey>(*p_event)) {4442// Redirect keyboard input to active popup.4443if (windows.has(E->get())) {4444Callable callable = windows[E->get()].input_event_callback;4445if (callable.is_valid()) {4446callable.call(p_event);4447}4448}4449in_dispatch_input_event = false;4450return;4451}4452}44534454Ref<InputEventFromWindow> event_from_window = p_event;4455if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {4456// Send to a single window.4457if (windows.has(event_from_window->get_window_id())) {4458Callable callable = windows[event_from_window->get_window_id()].input_event_callback;4459if (callable.is_valid()) {4460callable.call(p_event);4461}4462}4463} else {4464// Send to all windows. Copy all pending callbacks, since callback can erase window.4465Vector<Callable> cbs;4466for (KeyValue<WindowID, WindowData> &E : windows) {4467Callable callable = E.value.input_event_callback;4468if (callable.is_valid()) {4469cbs.push_back(callable);4470}4471}4472for (const Callable &cb : cbs) {4473cb.call(p_event);4474}4475}44764477in_dispatch_input_event = false;4478}44794480LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {4481DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());4482if (ds_win) {4483return ds_win->MouseProc(code, wParam, lParam);4484} else {4485return ::CallNextHookEx(nullptr, code, wParam, lParam);4486}4487}44884489DisplayServer::WindowID DisplayServerWindows::window_get_active_popup() const {4490const List<WindowID>::Element *E = popup_list.back();4491if (E) {4492return E->get();4493} else {4494return INVALID_WINDOW_ID;4495}4496}44974498void DisplayServerWindows::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {4499_THREAD_SAFE_METHOD_45004501ERR_FAIL_COND(!windows.has(p_window));4502WindowData &wd = windows[p_window];4503wd.parent_safe_rect = p_rect;4504}45054506Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const {4507_THREAD_SAFE_METHOD_45084509ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());4510const WindowData &wd = windows[p_window];4511return wd.parent_safe_rect;4512}45134514void DisplayServerWindows::popup_open(WindowID p_window) {4515_THREAD_SAFE_METHOD_45164517bool has_popup_ancestor = false;4518WindowID transient_root = p_window;4519while (true) {4520WindowID parent = windows[transient_root].transient_parent;4521if (parent == INVALID_WINDOW_ID) {4522break;4523} else {4524transient_root = parent;4525if (windows[parent].is_popup) {4526has_popup_ancestor = true;4527break;4528}4529}4530}45314532// Detect tooltips and other similar popups that shouldn't block input to their parent.4533bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);45344535WindowData &wd = windows[p_window];4536if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {4537// Find current popup parent, or root popup if new window is not transient.4538List<WindowID>::Element *C = nullptr;4539List<WindowID>::Element *E = popup_list.back();4540while (E) {4541if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {4542C = E;4543E = E->prev();4544} else {4545break;4546}4547}4548if (C) {4549_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4550}45514552time_since_popup = OS::get_singleton()->get_ticks_msec();4553popup_list.push_back(p_window);4554}4555}45564557void DisplayServerWindows::popup_close(WindowID p_window) {4558_THREAD_SAFE_METHOD_45594560List<WindowID>::Element *E = popup_list.find(p_window);4561while (E) {4562List<WindowID>::Element *F = E->next();4563WindowID win_id = E->get();4564popup_list.erase(E);45654566if (win_id != p_window) {4567// Only request close on related windows, not this window. We are already processing it.4568_send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4569}4570E = F;4571}4572}45734574BitField<DisplayServerWindows::WinKeyModifierMask> DisplayServerWindows::_get_mods() const {4575BitField<WinKeyModifierMask> mask = {};4576static unsigned char keyboard_state[256];4577if (GetKeyboardState((PBYTE)&keyboard_state)) {4578if ((keyboard_state[VK_LSHIFT] & 0x80) || (keyboard_state[VK_RSHIFT] & 0x80)) {4579mask.set_flag(WinKeyModifierMask::SHIFT);4580}4581if ((keyboard_state[VK_LCONTROL] & 0x80) || (keyboard_state[VK_RCONTROL] & 0x80)) {4582mask.set_flag(WinKeyModifierMask::CTRL);4583}4584if ((keyboard_state[VK_LMENU] & 0x80) || (keyboard_state[VK_RMENU] & 0x80)) {4585mask.set_flag(WinKeyModifierMask::ALT);4586}4587if ((keyboard_state[VK_RMENU] & 0x80)) {4588mask.set_flag(WinKeyModifierMask::ALT_GR);4589}4590if ((keyboard_state[VK_LWIN] & 0x80) || (keyboard_state[VK_RWIN] & 0x80)) {4591mask.set_flag(WinKeyModifierMask::META);4592}4593}45944595return mask;4596}45974598LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) {4599_THREAD_SAFE_METHOD_46004601uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;4602if (delta > 250) {4603switch (wParam) {4604case WM_NCLBUTTONDOWN:4605case WM_NCRBUTTONDOWN:4606case WM_NCMBUTTONDOWN:4607case WM_LBUTTONDOWN:4608case WM_RBUTTONDOWN:4609case WM_MBUTTONDOWN: {4610MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam;4611Point2i pos = Point2i(ms->pt.x, ms->pt.y) - _get_screens_origin();4612List<WindowID>::Element *C = nullptr;4613List<WindowID>::Element *E = popup_list.back();4614// Find top popup to close.4615while (E) {4616// Popup window area.4617Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));4618// Area of the parent window, which responsible for opening sub-menu.4619Rect2i safe_rect = window_get_popup_safe_rect(E->get());4620if (win_rect.has_point(pos)) {4621break;4622} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {4623break;4624} else {4625C = E;4626E = E->prev();4627}4628}4629if (C) {4630_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);4631return 1;4632}4633} break;4634}4635}4636return ::CallNextHookEx(mouse_monitor, code, wParam, lParam);4637}46384639// Handle a single window message received while CreateWindowEx is still on the stack and our data4640// structures are not fully initialized.4641LRESULT DisplayServerWindows::_handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {4642switch (uMsg) {4643case WM_GETMINMAXINFO: {4644// We receive this during CreateWindowEx and we haven't initialized the window4645// struct, so let Windows figure out the maximized size.4646// Silently forward to user/default.4647} break;4648case WM_NCCREATE: {4649// We tunnel an unowned pointer to our window context (WindowData) through the4650// first possible message (WM_NCCREATE) to fix up our window context collection.4651CREATESTRUCTW *pCreate = (CREATESTRUCTW *)lParam;4652WindowData *pWindowData = reinterpret_cast<WindowData *>(pCreate->lpCreateParams);46534654// Fix this up so we can recognize the remaining messages.4655pWindowData->hWnd = hWnd;46564657#ifdef ACCESSKIT_ENABLED4658if (accessibility_driver && !accessibility_driver->window_create(pWindowData->id, (void *)hWnd)) {4659if (OS::get_singleton()->is_stdout_verbose()) {4660ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");4661}4662memdelete(accessibility_driver);4663accessibility_driver = nullptr;4664}4665#endif4666} break;4667default: {4668// Additional messages during window creation should happen after we fixed4669// up the data structures on WM_NCCREATE, but this might change in the future,4670// so report an error here and then we can implement them.4671ERR_PRINT_ONCE(vformat("Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error.", uMsg));4672} break;4673}46744675if (user_proc) {4676return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);4677}4678return DefWindowProcW(hWnd, uMsg, wParam, lParam);4679}46804681// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events.4682// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures4683LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {4684if (drop_events) {4685if (user_proc) {4686return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);4687} else {4688return DefWindowProcW(hWnd, uMsg, wParam, lParam);4689}4690}46914692WindowID window_id = INVALID_WINDOW_ID;4693bool window_created = false;46944695// Check whether window exists4696// FIXME this is O(n), where n is the set of currently open windows and subwindows4697// we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below4698for (const KeyValue<WindowID, WindowData> &E : windows) {4699if (E.value.hWnd == hWnd) {4700window_id = E.key;4701window_created = true;4702break;4703}4704}47054706// WARNING: We get called with events before the window is registered in our collection4707// specifically, even the call to CreateWindowEx already calls here while still on the stack,4708// so there is no way to store the window handle in our collection before we get here.4709if (!window_created) {4710// don't let code below operate on incompletely initialized window objects or missing window_id4711return _handle_early_window_message(hWnd, uMsg, wParam, lParam);4712}47134714// Process window messages.4715switch (uMsg) {4716case WM_GETOBJECT: {4717get_object_received = true;4718} break;4719case WM_MENUCOMMAND: {4720native_menu->_menu_activate(HMENU(lParam), (int)wParam);4721} break;4722case WM_CREATE: {4723{4724DWORD value = windows[window_id].sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;4725::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));4726}4727if (is_dark_mode_supported() && dark_title_available) {4728BOOL value = is_dark_mode();47294730::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));4731SendMessageW(windows[window_id].hWnd, WM_PAINT, 0, 0);4732}4733} break;4734case WM_NCHITTEST: {4735if (windows[window_id].mpass) {4736return HTTRANSPARENT;4737}4738} break;4739case WM_MOUSEACTIVATE: {4740if (windows[window_id].no_focus || windows[window_id].is_popup) {4741return MA_NOACTIVATE; // Do not activate, but process mouse messages.4742}4743// When embedded, the window is a child of the parent and is not activated4744// by default because it lacks native controls.4745if (windows[window_id].parent_hwnd) {4746SetFocus(windows[window_id].hWnd);4747return MA_ACTIVATE;4748}4749} break;4750case WM_ACTIVATEAPP: {4751bool new_app_focused = (bool)wParam;4752if (new_app_focused == app_focused) {4753break;4754}4755app_focused = new_app_focused;4756if (OS::get_singleton()->get_main_loop()) {4757OS::get_singleton()->get_main_loop()->notification(app_focused ? MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN : MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);4758}4759} break;4760case WM_ACTIVATE: {4761// Activation can happen just after the window has been created, even before the callbacks are set.4762// Therefore, it's safer to defer the delivery of the event.4763// It's important to set an nIDEvent different from the SetTimer for move_timer_id because4764// if the same nIDEvent is passed, the timer is replaced and the same timer_id is returned.4765// The problem with the timer is that the window cannot be resized or the buttons cannot be used correctly4766// if the window is not activated first. This happens because the code in the activation process runs4767// after the mouse click is handled. To address this, the timer is now used only when the window is created.4768windows[window_id].activate_state = GET_WM_ACTIVATE_STATE(wParam, lParam);4769if (windows[window_id].first_activation_done) {4770_process_activate_event(window_id);4771} else {4772windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_WINDOW_ACTIVATION, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);4773}4774return 0;4775} break;4776case WM_GETMINMAXINFO: {4777if (windows[window_id].resizable && !windows[window_id].fullscreen) {4778// Size of window decorations.4779Size2 decor = window_get_size_with_decorations(window_id) - window_get_size(window_id);47804781MINMAXINFO *min_max_info = (MINMAXINFO *)lParam;4782if (windows[window_id].min_size != Size2()) {4783min_max_info->ptMinTrackSize.x = windows[window_id].min_size.x + decor.x;4784min_max_info->ptMinTrackSize.y = windows[window_id].min_size.y + decor.y;4785}4786if (windows[window_id].max_size != Size2()) {4787min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x;4788min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y;4789}4790if (windows[window_id].borderless) {4791Rect2i screen_rect = screen_get_usable_rect(window_get_current_screen(window_id));47924793// Set the size of (borderless) maximized mode to exclude taskbar (or any other panel) if present.4794min_max_info->ptMaxPosition.x = screen_rect.position.x;4795min_max_info->ptMaxPosition.y = screen_rect.position.y;4796min_max_info->ptMaxSize.x = screen_rect.size.x;4797min_max_info->ptMaxSize.y = screen_rect.size.y;4798}4799return 0;4800}4801} break;4802case WM_ERASEBKGND: {4803Color early_color;4804if (!_get_window_early_clear_override(early_color)) {4805break;4806}4807bool must_recreate_brush = !window_bkg_brush || window_bkg_brush_color != early_color.to_argb32();4808if (must_recreate_brush) {4809if (window_bkg_brush) {4810DeleteObject(window_bkg_brush);4811}4812window_bkg_brush = CreateSolidBrush(RGB(early_color.get_r8(), early_color.get_g8(), early_color.get_b8()));4813}4814HDC hdc = (HDC)wParam;4815RECT rect = {};4816if (GetUpdateRect(hWnd, &rect, true)) {4817FillRect(hdc, &rect, window_bkg_brush);4818}4819return 1;4820} break;4821case WM_PAINT: {4822Main::force_redraw();4823} break;4824case WM_SETTINGCHANGE:4825case WM_SYSCOLORCHANGE: {4826if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {4827if (is_dark_mode_supported() && dark_title_available) {4828BOOL value = is_dark_mode();4829::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));4830}4831}4832if (system_theme_changed.is_valid()) {4833Variant ret;4834Callable::CallError ce;4835system_theme_changed.callp(nullptr, 0, ret, ce);4836if (ce.error != Callable::CallError::CALL_OK) {4837ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));4838}4839}4840} break;4841case WM_THEMECHANGED: {4842if (is_dark_mode_supported() && dark_title_available) {4843BOOL value = is_dark_mode();4844::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));4845}4846} break;4847case WM_SYSCOMMAND: // Intercept system commands.4848{4849switch (wParam) // Check system calls.4850{4851case SC_SCREENSAVE: // Screensaver trying to start?4852case SC_MONITORPOWER: // Monitor trying to enter powersave?4853return 0; // Prevent from happening.4854case SC_KEYMENU:4855Engine *engine = Engine::get_singleton();4856if (((lParam >> 16) <= 0) && !engine->is_project_manager_hint() && !engine->is_editor_hint() && !GLOBAL_GET_CACHED(bool, "application/run/enable_alt_space_menu")) {4857return 0;4858}4859if (!_get_mods().has_flag(WinKeyModifierMask::ALT) || !(GetAsyncKeyState(VK_SPACE) & (1 << 15))) {4860return 0;4861}4862SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_SPACE, 0);4863SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_MENU, 0);4864}4865} break;4866case WM_INDICATOR_CALLBACK_MESSAGE: {4867if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN || lParam == WM_MBUTTONDOWN || lParam == WM_XBUTTONDOWN) {4868IndicatorID iid = (IndicatorID)wParam;4869MouseButton mb = MouseButton::LEFT;4870if (lParam == WM_RBUTTONDOWN) {4871mb = MouseButton::RIGHT;4872} else if (lParam == WM_MBUTTONDOWN) {4873mb = MouseButton::MIDDLE;4874} else if (lParam == WM_XBUTTONDOWN) {4875mb = MouseButton::MB_XBUTTON1;4876}4877if (indicators.has(iid)) {4878if (lParam == WM_RBUTTONDOWN && indicators[iid].menu_rid.is_valid() && native_menu->has_menu(indicators[iid].menu_rid)) {4879NOTIFYICONIDENTIFIER nid;4880ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));4881nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);4882nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;4883nid.uID = iid;4884nid.guidItem = GUID_NULL;48854886RECT rect;4887if (Shell_NotifyIconGetRect(&nid, &rect) == S_OK) {4888native_menu->popup(indicators[iid].menu_rid, Vector2i((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2));4889}4890} else if (indicators[iid].callback.is_valid()) {4891Variant v_button = mb;4892Variant v_pos = mouse_get_position();4893const Variant *v_args[2] = { &v_button, &v_pos };4894Variant ret;4895Callable::CallError ce;4896indicators[iid].callback.callp((const Variant **)&v_args, 2, ret, ce);4897if (ce.error != Callable::CallError::CALL_OK) {4898ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(indicators[iid].callback, v_args, 2, ce)));4899}4900}4901}4902return 0;4903}4904} break;4905case WM_CLOSE: {4906if (windows[window_id].activate_timer_id) {4907KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);4908windows[window_id].activate_timer_id = 0;4909}4910_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);4911return 0;4912}4913case WM_MOUSELEAVE: {4914if (window_mouseover_id == window_id) {4915old_invalid = true;4916window_mouseover_id = INVALID_WINDOW_ID;49174918_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);4919} else if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {4920// This is reached during drag and drop, after dropping in a different window.4921// Once-off notification, must call again.4922track_mouse_leave_event(windows[window_mouseover_id].hWnd);4923}49244925} break;4926case WM_INPUT: {4927if (!use_raw_input) {4928break;4929}49304931UINT dwSize;49324933GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER));4934LPBYTE lpb = new BYTE[dwSize];4935if (lpb == nullptr) {4936return 0;4937}49384939if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {4940OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));4941}49424943RAWINPUT *raw = (RAWINPUT *)lpb;49444945const BitField<WinKeyModifierMask> &mods = _get_mods();4946if (raw->header.dwType == RIM_TYPEKEYBOARD) {4947if (raw->data.keyboard.VKey == VK_SHIFT) {4948// If multiple Shifts are held down at the same time,4949// Windows natively only sends a KEYUP for the last one to be released.4950if (raw->data.keyboard.Flags & RI_KEY_BREAK) {4951// Make sure to check the latest key state since4952// we're in the middle of the message queue.4953if (GetAsyncKeyState(VK_SHIFT) < 0) {4954// A Shift is released, but another Shift is still held4955ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);49564957KeyEvent ke;4958ke.shift = false;4959ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);4960ke.alt = mods.has_flag(WinKeyModifierMask::ALT);4961ke.control = mods.has_flag(WinKeyModifierMask::CTRL);4962ke.meta = mods.has_flag(WinKeyModifierMask::META);4963ke.uMsg = WM_KEYUP;4964ke.window_id = window_id;49654966ke.wParam = VK_SHIFT;4967// data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.4968// Bit 30 -> key was previously down, bit 31 -> key is being released.4969ke.lParam = raw->data.keyboard.MakeCode << 16 | 1 << 30 | 1 << 31;4970key_event_buffer[key_event_pos++] = ke;4971}4972}4973}4974} else if (mouse_mode == MOUSE_MODE_CAPTURED && raw->header.dwType == RIM_TYPEMOUSE) {4975Ref<InputEventMouseMotion> mm;4976mm.instantiate();49774978mm->set_window_id(window_id);4979mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));4980mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));4981mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));4982mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));49834984mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f);49854986mm->set_button_mask(mouse_get_button_state());49874988Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);49894990// Centering just so it works as before.4991POINT pos = { (int)c.x, (int)c.y };4992ClientToScreen(windows[window_id].hWnd, &pos);4993SetCursorPos(pos.x, pos.y);49944995mm->set_position(c);4996mm->set_global_position(c);4997mm->set_velocity(Vector2(0, 0));4998mm->set_screen_velocity(Vector2(0, 0));49995000if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {5001mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));50025003} else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {5004int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);5005int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);5006int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);5007int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);50085009Vector2 abs_pos(5010(double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,5011(double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);50125013POINT coords; // Client coords.5014coords.x = abs_pos.x;5015coords.y = abs_pos.y;50165017ScreenToClient(hWnd, &coords);50185019mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));5020old_x = coords.x;5021old_y = coords.y;5022}5023mm->set_relative_screen_position(mm->get_relative());50245025if ((windows[window_id].window_focused || windows[window_id].is_popup) && mm->get_relative() != Vector2()) {5026Input::get_singleton()->parse_input_event(mm);5027}5028}5029delete[] lpb;5030} break;5031case WT_CSRCHANGE:5032case WT_PROXIMITY: {5033if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5034AXIS pressure;5035if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {5036windows[window_id].min_pressure = int(pressure.axMin);5037windows[window_id].max_pressure = int(pressure.axMax);5038}5039AXIS orientation[3];5040if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {5041windows[window_id].tilt_supported = orientation[0].axResolution && orientation[1].axResolution;5042}5043return 0;5044}5045} break;5046case WT_PACKET: {5047if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5048PACKET packet;5049if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) {5050POINT coords;5051GetCursorPos(&coords);5052ScreenToClient(windows[window_id].hWnd, &coords);50535054windows[window_id].last_pressure_update = 0;50555056float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);5057double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math::PI / 180);5058double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math::PI / 180));5059bool inverted = packet.pkStatus & TPS_INVERT;50605061Vector2 tilt = (windows[window_id].tilt_supported) ? Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)) : Vector2();50625063// Nothing changed, ignore event.5064if (!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) {5065break;5066}50675068windows[window_id].last_pressure = pressure;5069windows[window_id].last_tilt = tilt;5070windows[window_id].last_pen_inverted = inverted;50715072// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5073if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5074break;5075}50765077const BitField<WinKeyModifierMask> &mods = _get_mods();5078Ref<InputEventMouseMotion> mm;5079mm.instantiate();5080mm->set_window_id(window_id);5081mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5082mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5083mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5084mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));50855086mm->set_pressure(windows[window_id].last_pressure);5087mm->set_tilt(windows[window_id].last_tilt);5088mm->set_pen_inverted(windows[window_id].last_pen_inverted);50895090mm->set_button_mask(mouse_get_button_state());50915092mm->set_position(Vector2(coords.x, coords.y));5093mm->set_global_position(Vector2(coords.x, coords.y));50945095if (mouse_mode == MOUSE_MODE_CAPTURED) {5096Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5097old_x = c.x;5098old_y = c.y;50995100if (mm->get_position() == c) {5101center = c;5102return 0;5103}51045105Point2i ncenter = mm->get_position();5106center = ncenter;5107POINT pos = { (int)c.x, (int)c.y };5108ClientToScreen(windows[window_id].hWnd, &pos);5109SetCursorPos(pos.x, pos.y);5110}51115112mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5113mm->set_screen_velocity(mm->get_velocity());51145115if (old_invalid) {5116old_x = mm->get_position().x;5117old_y = mm->get_position().y;5118old_invalid = false;5119}51205121mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5122mm->set_relative_screen_position(mm->get_relative());5123old_x = mm->get_position().x;5124old_y = mm->get_position().y;51255126if (windows[window_id].window_focused || window_get_active_popup() == window_id) {5127Input::get_singleton()->parse_input_event(mm);5128}5129}5130return 0;5131}5132} break;5133case WM_POINTERENTER: {5134if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5135break;5136}51375138if (tablet_get_current_driver() != "winink") {5139break;5140}51415142uint32_t pointer_id = LOWORD(wParam);5143POINTER_INPUT_TYPE pointer_type = PT_POINTER;5144if (!GetPointerType(pointer_id, &pointer_type)) {5145break;5146}51475148if (pointer_type != PT_PEN) {5149break;5150}51515152pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;5153windows[window_id].block_mm = true;5154return 0;5155} break;5156case WM_POINTERLEAVE: {5157pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;5158windows[window_id].block_mm = false;5159return 0;5160} break;5161case WM_POINTERDOWN:5162case WM_POINTERUP: {5163if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5164break;5165}51665167if (tablet_get_current_driver() != "winink") {5168break;5169}51705171uint32_t pointer_id = LOWORD(wParam);5172POINTER_INPUT_TYPE pointer_type = PT_POINTER;5173if (!GetPointerType(pointer_id, &pointer_type)) {5174break;5175}51765177if (pointer_type != PT_PEN) {5178break;5179}51805181Ref<InputEventMouseButton> mb;5182mb.instantiate();5183mb->set_window_id(window_id);51845185BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;5186if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {5187last_button_state.set_flag(MouseButtonMask::LEFT);5188mb->set_button_index(MouseButton::LEFT);5189}5190if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {5191last_button_state.set_flag(MouseButtonMask::RIGHT);5192mb->set_button_index(MouseButton::RIGHT);5193}5194if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {5195last_button_state.set_flag(MouseButtonMask::MIDDLE);5196mb->set_button_index(MouseButton::MIDDLE);5197}5198if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {5199last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);5200mb->set_button_index(MouseButton::MB_XBUTTON1);5201}5202if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {5203last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);5204mb->set_button_index(MouseButton::MB_XBUTTON2);5205}5206mb->set_button_mask(last_button_state);52075208const BitField<WinKeyModifierMask> &mods = _get_mods();5209mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5210mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5211mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5212mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));52135214POINT coords; // Client coords.5215coords.x = GET_X_LPARAM(lParam);5216coords.y = GET_Y_LPARAM(lParam);52175218// Note: Handle popup closing here, since mouse event is not emulated and hook will not be called.5219uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;5220if (delta > 250) {5221Point2i pos = Point2i(coords.x, coords.y) - _get_screens_origin();5222List<WindowID>::Element *C = nullptr;5223List<WindowID>::Element *E = popup_list.back();5224// Find top popup to close.5225while (E) {5226// Popup window area.5227Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));5228// Area of the parent window, which responsible for opening sub-menu.5229Rect2i safe_rect = window_get_popup_safe_rect(E->get());5230if (win_rect.has_point(pos)) {5231break;5232} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {5233break;5234} else {5235C = E;5236E = E->prev();5237}5238}5239if (C) {5240_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);5241}5242}52435244int64_t pen_id = GET_POINTERID_WPARAM(wParam);5245if (uMsg == WM_POINTERDOWN) {5246mb->set_pressed(true);5247if (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()) {5248mb->set_double_click(true);5249pointer_down_time[pen_id] = 0;5250} else {5251pointer_down_time[pen_id] = GetMessageTime();5252pointer_prev_button[pen_id] = mb->get_button_index();5253pointer_last_pos[pen_id] = Vector2(coords.x, coords.y);5254}5255pointer_button[pen_id] = mb->get_button_index();5256} else {5257if (!pointer_button.has(pen_id)) {5258return 0;5259}5260mb->set_pressed(false);5261mb->set_button_index(pointer_button[pen_id]);5262pointer_button[pen_id] = MouseButton::NONE;5263}52645265ScreenToClient(windows[window_id].hWnd, &coords);52665267mb->set_position(Vector2(coords.x, coords.y));5268mb->set_global_position(Vector2(coords.x, coords.y));52695270Input::get_singleton()->parse_input_event(mb);52715272return 0;5273} break;5274case WM_POINTERUPDATE: {5275if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5276break;5277}52785279if (tablet_get_current_driver() != "winink") {5280break;5281}52825283uint32_t pointer_id = LOWORD(wParam);5284POINTER_INPUT_TYPE pointer_type = PT_POINTER;5285if (!GetPointerType(pointer_id, &pointer_type)) {5286break;5287}52885289if (pointer_type != PT_PEN) {5290break;5291}52925293POINTER_PEN_INFO pen_info;5294if (!GetPointerPenInfo(pointer_id, &pen_info)) {5295break;5296}52975298if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5299// Universal translation enabled; ignore OS translation.5300LPARAM extra = GetMessageExtraInfo();5301if (IsTouchEvent(extra)) {5302break;5303}5304}53055306if (window_mouseover_id != window_id) {5307// Mouse enter.53085309if (mouse_mode != MOUSE_MODE_CAPTURED) {5310if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {5311// Leave previous window.5312_send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);5313}5314_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);5315}53165317CursorShape c = cursor_shape;5318cursor_shape = CURSOR_MAX;5319cursor_set_shape(c);5320window_mouseover_id = window_id;53215322// Once-off notification, must call again.5323track_mouse_leave_event(hWnd);5324}53255326// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5327if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5328break;5329}53305331Ref<InputEventMouseMotion> mm;5332mm.instantiate();53335334mm->set_window_id(window_id);5335if (pen_info.penMask & PEN_MASK_PRESSURE) {5336mm->set_pressure((float)pen_info.pressure / 1024);5337} else {5338mm->set_pressure((HIWORD(wParam) & POINTER_MESSAGE_FLAG_FIRSTBUTTON) ? 1.0f : 0.0f);5339}5340if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) {5341mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90));5342}5343mm->set_pen_inverted(pen_info.penFlags & (PEN_FLAG_INVERTED | PEN_FLAG_ERASER));53445345const BitField<WinKeyModifierMask> &mods = _get_mods();5346mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5347mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5348mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5349mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));53505351BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;5352if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {5353last_button_state.set_flag(MouseButtonMask::LEFT);5354}5355if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {5356last_button_state.set_flag(MouseButtonMask::RIGHT);5357}5358if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {5359last_button_state.set_flag(MouseButtonMask::MIDDLE);5360}5361if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {5362last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);5363}5364if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {5365last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);5366}5367mm->set_button_mask(last_button_state);53685369POINT coords; // Client coords.5370coords.x = GET_X_LPARAM(lParam);5371coords.y = GET_Y_LPARAM(lParam);53725373ScreenToClient(windows[window_id].hWnd, &coords);53745375mm->set_position(Vector2(coords.x, coords.y));5376mm->set_global_position(Vector2(coords.x, coords.y));53775378if (mouse_mode == MOUSE_MODE_CAPTURED) {5379Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5380old_x = c.x;5381old_y = c.y;53825383if (mm->get_position() == c) {5384center = c;5385return 0;5386}53875388Point2i ncenter = mm->get_position();5389center = ncenter;5390POINT pos = { (int)c.x, (int)c.y };5391ClientToScreen(hWnd, &pos);5392SetCursorPos(pos.x, pos.y);5393}53945395mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5396mm->set_screen_velocity(mm->get_velocity());53975398if (old_invalid) {5399old_x = mm->get_position().x;5400old_y = mm->get_position().y;5401old_invalid = false;5402}54035404mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5405mm->set_relative_screen_position(mm->get_relative());5406old_x = mm->get_position().x;5407old_y = mm->get_position().y;5408if (windows[window_id].window_focused || window_get_active_popup() == window_id) {5409Input::get_singleton()->parse_input_event(mm);5410}54115412return 0; // Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event.5413} break;5414case WM_MOUSEMOVE: {5415if (windows[window_id].block_mm) {5416break;5417}54185419if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {5420break;5421}54225423if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5424// Universal translation enabled; ignore OS translation.5425LPARAM extra = GetMessageExtraInfo();5426if (IsTouchEvent(extra)) {5427break;5428}5429}54305431DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position());5432if (windows.has(over_id) && !Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) {5433// Don't consider the windowborder as part of the window.5434over_id = INVALID_WINDOW_ID;5435}5436if (window_mouseover_id != over_id) {5437// Mouse enter.54385439if (mouse_mode != MOUSE_MODE_CAPTURED) {5440if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {5441// Leave previous window.5442_send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);5443}54445445if (over_id != INVALID_WINDOW_ID && windows.has(over_id)) {5446_send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER);5447}5448}54495450CursorShape c = cursor_shape;5451cursor_shape = CURSOR_MAX;5452cursor_set_shape(c);5453window_mouseover_id = over_id;54545455// Once-off notification, must call again.5456track_mouse_leave_event(hWnd);5457}54585459// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.5460if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {5461break;5462}54635464DisplayServer::WindowID receiving_window_id = window_id;5465if (!windows[window_id].no_focus) {5466receiving_window_id = _get_focused_window_or_popup();5467if (receiving_window_id == INVALID_WINDOW_ID) {5468receiving_window_id = window_id;5469}5470}54715472const BitField<WinKeyModifierMask> &mods = _get_mods();5473Ref<InputEventMouseMotion> mm;5474mm.instantiate();5475mm->set_window_id(receiving_window_id);5476mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5477mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5478mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5479mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));54805481if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {5482// 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.5483if (windows[window_id].last_pressure_update < 10) {5484windows[window_id].last_pressure_update++;5485} else {5486windows[window_id].last_tilt = Vector2();5487windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;5488windows[window_id].last_pen_inverted = false;5489}5490} else {5491windows[window_id].last_tilt = Vector2();5492windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;5493windows[window_id].last_pen_inverted = false;5494}54955496mm->set_pressure(windows[window_id].last_pressure);5497mm->set_tilt(windows[window_id].last_tilt);5498mm->set_pen_inverted(windows[window_id].last_pen_inverted);54995500mm->set_button_mask(mouse_get_button_state());55015502mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));5503mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));55045505if (mouse_mode == MOUSE_MODE_CAPTURED) {5506Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);5507old_x = c.x;5508old_y = c.y;55095510if (mm->get_position() == c) {5511center = c;5512return 0;5513}55145515Point2i ncenter = mm->get_position();5516center = ncenter;5517POINT pos = { (int)c.x, (int)c.y };5518ClientToScreen(windows[window_id].hWnd, &pos);5519SetCursorPos(pos.x, pos.y);5520}55215522mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());5523mm->set_screen_velocity(mm->get_velocity());55245525if (old_invalid) {5526old_x = mm->get_position().x;5527old_y = mm->get_position().y;5528old_invalid = false;5529}55305531mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));5532mm->set_relative_screen_position(mm->get_relative());5533old_x = mm->get_position().x;5534old_y = mm->get_position().y;55355536if (receiving_window_id != window_id) {5537// Adjust event position relative to window distance when event is sent to a different window.5538mm->set_position(mm->get_position() - window_get_position(receiving_window_id) + window_get_position(window_id));5539mm->set_global_position(mm->get_position());5540}55415542Input::get_singleton()->parse_input_event(mm);55435544} break;5545case WM_LBUTTONDOWN:5546case WM_LBUTTONUP:5547if (Input::get_singleton()->is_emulating_mouse_from_touch()) {5548// Universal translation enabled; ignore OS translations for left button.5549LPARAM extra = GetMessageExtraInfo();5550if (IsTouchEvent(extra)) {5551break;5552}5553}5554[[fallthrough]];5555case WM_MBUTTONDOWN:5556case WM_MBUTTONUP:5557case WM_RBUTTONDOWN:5558case WM_RBUTTONUP:5559case WM_MOUSEWHEEL:5560case WM_MOUSEHWHEEL:5561case WM_LBUTTONDBLCLK:5562case WM_MBUTTONDBLCLK:5563case WM_RBUTTONDBLCLK:5564case WM_XBUTTONDBLCLK:5565case WM_XBUTTONDOWN:5566case WM_XBUTTONUP: {5567Ref<InputEventMouseButton> mb;5568mb.instantiate();5569mb->set_window_id(window_id);55705571switch (uMsg) {5572case WM_LBUTTONDOWN: {5573mb->set_pressed(true);5574mb->set_button_index(MouseButton::LEFT);5575} break;5576case WM_LBUTTONUP: {5577mb->set_pressed(false);5578mb->set_button_index(MouseButton::LEFT);5579} break;5580case WM_MBUTTONDOWN: {5581mb->set_pressed(true);5582mb->set_button_index(MouseButton::MIDDLE);5583} break;5584case WM_MBUTTONUP: {5585mb->set_pressed(false);5586mb->set_button_index(MouseButton::MIDDLE);5587} break;5588case WM_RBUTTONDOWN: {5589mb->set_pressed(true);5590mb->set_button_index(MouseButton::RIGHT);5591} break;5592case WM_RBUTTONUP: {5593mb->set_pressed(false);5594mb->set_button_index(MouseButton::RIGHT);5595} break;5596case WM_LBUTTONDBLCLK: {5597mb->set_pressed(true);5598mb->set_button_index(MouseButton::LEFT);5599mb->set_double_click(true);5600} break;5601case WM_RBUTTONDBLCLK: {5602mb->set_pressed(true);5603mb->set_button_index(MouseButton::RIGHT);5604mb->set_double_click(true);5605} break;5606case WM_MBUTTONDBLCLK: {5607mb->set_pressed(true);5608mb->set_button_index(MouseButton::MIDDLE);5609mb->set_double_click(true);5610} break;5611case WM_MOUSEWHEEL: {5612mb->set_pressed(true);5613int motion = (short)HIWORD(wParam);5614if (!motion) {5615return 0;5616}56175618if (motion > 0) {5619mb->set_button_index(MouseButton::WHEEL_UP);5620} else {5621mb->set_button_index(MouseButton::WHEEL_DOWN);5622}5623mb->set_factor(std::fabs((double)motion / (double)WHEEL_DELTA));5624} break;5625case WM_MOUSEHWHEEL: {5626mb->set_pressed(true);5627int motion = (short)HIWORD(wParam);5628if (!motion) {5629return 0;5630}56315632if (motion < 0) {5633mb->set_button_index(MouseButton::WHEEL_LEFT);5634} else {5635mb->set_button_index(MouseButton::WHEEL_RIGHT);5636}5637mb->set_factor(std::fabs((double)motion / (double)WHEEL_DELTA));5638} break;5639case WM_XBUTTONDOWN: {5640mb->set_pressed(true);5641if (HIWORD(wParam) == XBUTTON1) {5642mb->set_button_index(MouseButton::MB_XBUTTON1);5643} else {5644mb->set_button_index(MouseButton::MB_XBUTTON2);5645}5646} break;5647case WM_XBUTTONUP: {5648mb->set_pressed(false);5649if (HIWORD(wParam) == XBUTTON1) {5650mb->set_button_index(MouseButton::MB_XBUTTON1);5651} else {5652mb->set_button_index(MouseButton::MB_XBUTTON2);5653}5654} break;5655case WM_XBUTTONDBLCLK: {5656mb->set_pressed(true);5657if (HIWORD(wParam) == XBUTTON1) {5658mb->set_button_index(MouseButton::MB_XBUTTON1);5659} else {5660mb->set_button_index(MouseButton::MB_XBUTTON2);5661}5662mb->set_double_click(true);5663} break;5664default: {5665return 0;5666}5667}56685669const BitField<WinKeyModifierMask> &mods = _get_mods();5670mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));5671mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));5672mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));5673mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));56745675if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) {5676MouseButtonMask mask = mouse_button_to_mask(mb->get_button_index());5677BitField<MouseButtonMask> scroll_mask = mouse_get_button_state();5678scroll_mask.set_flag(mask);5679mb->set_button_mask(scroll_mask);5680} else {5681mb->set_button_mask(mouse_get_button_state());5682}56835684mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));56855686if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {5687mb->set_position(Vector2(old_x, old_y));5688}56895690if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {5691if (mb->is_pressed()) {5692if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED) {5693SetCapture(hWnd);5694}5695} else {5696if (--pressrc <= 0 || mouse_get_button_state().is_empty()) {5697if (mouse_mode != MOUSE_MODE_CAPTURED) {5698ReleaseCapture();5699}5700pressrc = 0;5701}5702}5703} else {5704// For reasons unknown to humanity, wheel comes in screen coordinates.5705POINT coords;5706coords.x = mb->get_position().x;5707coords.y = mb->get_position().y;57085709ScreenToClient(hWnd, &coords);57105711mb->set_position(Vector2(coords.x, coords.y));5712}57135714mb->set_global_position(mb->get_position());57155716Input::get_singleton()->parse_input_event(mb);5717if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) {5718// Send release for mouse wheel.5719Ref<InputEventMouseButton> mbd = mb->duplicate();5720mbd->set_window_id(window_id);5721mbd->set_button_mask(mouse_get_button_state());5722mbd->set_pressed(false);5723Input::get_singleton()->parse_input_event(mbd);5724}57255726// Propagate the button up event to the window on which the button down5727// event was triggered. This is needed for drag & drop to work between windows,5728// because the engine expects events to keep being processed5729// on the same window dragging started.5730if (mb->is_pressed()) {5731last_mouse_button_down_window = window_id;5732} else if (last_mouse_button_down_window != INVALID_WINDOW_ID) {5733mb->set_window_id(last_mouse_button_down_window);5734last_mouse_button_down_window = INVALID_WINDOW_ID;5735}5736} break;57375738case WM_WINDOWPOSCHANGED: {5739WindowData &window = windows[window_id];57405741int off_x = (window.multiwindow_fs || (!window.fullscreen && window.borderless && IsZoomed(hWnd))) ? FS_TRANSP_BORDER : 0;5742Rect2i window_client_rect;5743Rect2i window_rect;5744{5745RECT rect;5746GetClientRect(hWnd, &rect);5747ClientToScreen(hWnd, (POINT *)&rect.left);5748ClientToScreen(hWnd, (POINT *)&rect.right);5749window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left - off_x, rect.bottom - rect.top);5750window_client_rect.position -= _get_screens_origin();57515752RECT wrect;5753GetWindowRect(hWnd, &wrect);5754window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left - off_x, wrect.bottom - wrect.top);5755window_rect.position -= _get_screens_origin();5756}57575758WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam;57595760bool rect_changed = false;5761if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) {5762int screen_id = window_get_current_screen(window_id);5763Size2i screen_size = screen_get_size(screen_id);5764Point2i screen_position = screen_get_position(screen_id);5765Rect2i usable = screen_get_usable_rect(screen_id);57665767window.maximized = false;5768window.minimized = false;5769window.fullscreen = false;57705771if (IsIconic(hWnd)) {5772window.minimized = true;5773} else if (IsZoomed(hWnd)) {5774window.maximized = true;57755776// If maximized_window_size == screen_size add 1px border to prevent switching to exclusive_fs.5777if (!window.maximized_fs && window.borderless && window_rect.position == screen_position && window_rect.size == screen_size) {5778// Window (borderless) was just maximized and the covers the entire screen.5779window.maximized_fs = true;5780_update_window_style(window_id, false);5781}5782if (window.borderless && (screen_size != usable.size || screen_position != usable.position)) {5783Point2 pos = usable.position + _get_screens_origin();5784Size2 size = usable.size;5785MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);5786}5787} else if (window_rect.position == screen_position && window_rect.size == screen_size) {5788window.fullscreen = true;5789} else if (window.borderless && usable.position == window_rect.position && usable.size == window_rect.size) {5790window.maximized = true;5791}57925793if (window.maximized_fs && !window.maximized) {5794// Window (maximized and covering fullscreen) was just non-maximized.5795window.maximized_fs = false;5796_update_window_style(window_id, false);5797}57985799if (!window.minimized) {5800window.width = window_client_rect.size.width;5801window.height = window_client_rect.size.height;58025803rect_changed = true;5804}5805#if defined(RD_ENABLED)5806if (window.create_completed && rendering_context && window.context_created) {5807// Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.5808rendering_context->window_set_size(window_id, window.width, window.height);5809}5810#endif5811#if defined(GLES3_ENABLED)5812if (window.create_completed && gl_manager_native) {5813gl_manager_native->window_resize(window_id, window.width, window.height);5814}5815if (window.create_completed && gl_manager_angle) {5816gl_manager_angle->window_resize(window_id, window.width, window.height);5817}5818#endif5819}58205821if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) {5822window.last_pos = window_client_rect.position;5823rect_changed = true;5824}58255826if (rect_changed) {5827if (window.rect_changed_callback.is_valid()) {5828window.rect_changed_callback.call(Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height));5829}58305831// Update cursor clip region after window rect has changed.5832if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {5833RECT crect;5834GetClientRect(window.hWnd, &crect);5835crect.right -= off_x;5836ClientToScreen(window.hWnd, (POINT *)&crect.left);5837ClientToScreen(window.hWnd, (POINT *)&crect.right);5838ClipCursor(&crect);5839}58405841if (!window.minimized && window.was_fullscreen_pre_min) {5842// Restore fullscreen mode if window was in fullscreen before it was minimized.5843int cs = window_get_current_screen(window_id);5844Point2 pos = screen_get_position(cs) + _get_screens_origin();5845Size2 size = screen_get_size(cs);58465847window.was_fullscreen_pre_min = false;5848window.fullscreen = true;5849window.maximized = false;5850window.minimized = false;58515852_update_window_style(window_id, false);58535854MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);5855}5856} else {5857if (window.parent_hwnd) {5858// WM_WINDOWPOSCHANGED is sent when the parent changes.5859// If we are supposed to have a parent and now we don't, it's likely5860// because the parent was closed. We will close our window as well.5861// This prevents an embedded game from staying alive when the editor is closed or crashes.5862if (!GetParent(window.hWnd)) {5863SendMessage(window.hWnd, WM_CLOSE, 0, 0);5864}5865}5866}58675868// Return here to prevent WM_MOVE and WM_SIZE from being sent5869// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks5870return 0;5871} break;58725873case WM_ENTERSIZEMOVE: {5874Input::get_singleton()->release_pressed_events();5875windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_MOVE_REDRAW, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);5876} break;5877case WM_EXITSIZEMOVE: {5878KillTimer(windows[window_id].hWnd, windows[window_id].move_timer_id);5879windows[window_id].move_timer_id = 0;5880// Reset the correct mouse mode because we couldn't call ReleaseCapture in5881// _set_mouse_mode_impl while in _process_activate_event (because the user was moving a window).5882_set_mouse_mode_impl(mouse_mode);5883} break;5884case WM_TIMER: {5885if (wParam == windows[window_id].move_timer_id) {5886_THREAD_SAFE_UNLOCK_5887_process_key_events();5888if (!Main::is_iterating()) {5889Main::iteration();5890}5891_THREAD_SAFE_LOCK_5892} else if (wParam == windows[window_id].activate_timer_id) {5893_process_activate_event(window_id);5894KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);5895windows[window_id].activate_timer_id = 0;5896windows[window_id].first_activation_done = true;5897}5898} break;5899case WM_SYSKEYUP:5900case WM_KEYUP:5901case WM_SYSKEYDOWN:5902case WM_KEYDOWN: {5903if (windows[window_id].ime_suppress_next_keyup && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)) {5904windows[window_id].ime_suppress_next_keyup = false;5905break;5906}5907if (windows[window_id].ime_in_progress) {5908break;5909}59105911if (mouse_mode == MOUSE_MODE_CAPTURED) {5912// When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves5913if (wParam == VK_F4 && _get_mods().has_flag(WinKeyModifierMask::ALT) && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) {5914_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);5915}5916}5917[[fallthrough]];5918}5919case WM_CHAR: {5920ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);5921const BitField<WinKeyModifierMask> &mods = _get_mods();59225923KeyEvent ke;5924ke.shift = mods.has_flag(WinKeyModifierMask::SHIFT);5925ke.alt = mods.has_flag(WinKeyModifierMask::ALT);5926ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);5927ke.control = mods.has_flag(WinKeyModifierMask::CTRL);5928ke.meta = mods.has_flag(WinKeyModifierMask::META);5929ke.uMsg = uMsg;5930ke.window_id = window_id;59315932if (ke.uMsg == WM_SYSKEYDOWN) {5933ke.uMsg = WM_KEYDOWN;5934}5935if (ke.uMsg == WM_SYSKEYUP) {5936ke.uMsg = WM_KEYUP;5937}59385939ke.wParam = wParam;5940ke.lParam = lParam;5941key_event_buffer[key_event_pos++] = ke;59425943} break;5944case WM_IME_COMPOSITION: {5945CANDIDATEFORM cf;5946cf.dwIndex = 0;59475948cf.dwStyle = CFS_CANDIDATEPOS;5949cf.ptCurrentPos.x = windows[window_id].im_position.x;5950cf.ptCurrentPos.y = windows[window_id].im_position.y;5951ImmSetCandidateWindow(windows[window_id].im_himc, &cf);59525953cf.dwStyle = CFS_EXCLUDE;5954cf.rcArea.left = windows[window_id].im_position.x;5955cf.rcArea.right = windows[window_id].im_position.x;5956cf.rcArea.top = windows[window_id].im_position.y;5957cf.rcArea.bottom = windows[window_id].im_position.y;5958ImmSetCandidateWindow(windows[window_id].im_himc, &cf);59595960if (windows[window_id].ime_active) {5961SetCaretPos(windows[window_id].im_position.x, windows[window_id].im_position.y);5962OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);5963}5964} break;5965case WM_INPUTLANGCHANGEREQUEST: {5966// FIXME: Do something?5967} break;5968case WM_IME_STARTCOMPOSITION: {5969if (windows[window_id].ime_active) {5970windows[window_id].ime_in_progress = true;5971if (key_event_pos > 0) {5972key_event_pos--;5973}5974}5975return 0;5976} break;5977case WM_IME_ENDCOMPOSITION: {5978if (windows[window_id].ime_active) {5979windows[window_id].ime_in_progress = false;5980windows[window_id].ime_suppress_next_keyup = true;5981}5982return 0;5983} break;5984case WM_IME_NOTIFY: {5985return 0;5986} break;5987case WM_TOUCH: {5988BOOL bHandled = FALSE;5989UINT cInputs = LOWORD(wParam);5990PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);5991if (pInputs) {5992if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {5993for (UINT i = 0; i < cInputs; i++) {5994TOUCHINPUT ti = pInputs[i];5995POINT touch_pos = {5996TOUCH_COORD_TO_PIXEL(ti.x),5997TOUCH_COORD_TO_PIXEL(ti.y),5998};5999ScreenToClient(hWnd, &touch_pos);6000// Do something with each touch input entry.6001if (ti.dwFlags & TOUCHEVENTF_MOVE) {6002_drag_event(window_id, touch_pos.x, touch_pos.y, ti.dwID);6003} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {6004_touch_event(window_id, ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);6005}6006}6007bHandled = TRUE;6008} else {6009// TODO: Handle the error here.6010}6011memdelete_arr(pInputs);6012} else {6013// TODO: Handle the error here, probably out of memory.6014}6015if (bHandled) {6016CloseTouchInputHandle((HTOUCHINPUT)lParam);6017return 0;6018}60196020} break;6021case WM_DESTROY: {6022#ifdef ACCESSKIT_ENABLED6023if (accessibility_driver) {6024accessibility_driver->window_destroy(window_id);6025}6026#endif6027Input::get_singleton()->flush_buffered_events();6028if (window_mouseover_id == window_id) {6029window_mouseover_id = INVALID_WINDOW_ID;6030_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);6031}6032} break;6033case WM_SETCURSOR: {6034if (LOWORD(lParam) == HTCLIENT) {6035if (windows[window_id].window_focused && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) {6036// Hide the cursor.6037if (hCursor == nullptr) {6038hCursor = SetCursor(nullptr);6039} else {6040SetCursor(nullptr);6041}6042} else {6043if (hCursor != nullptr) {6044CursorShape c = cursor_shape;6045cursor_shape = CURSOR_MAX;6046cursor_set_shape(c);6047hCursor = nullptr;6048}6049}6050}6051} break;6052default: {6053if (user_proc) {6054return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);6055}6056}6057}60586059return DefWindowProcW(hWnd, uMsg, wParam, lParam);6060}60616062LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {6063DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());6064if (ds_win) {6065return ds_win->WndProc(hWnd, uMsg, wParam, lParam);6066} else {6067return DefWindowProcW(hWnd, uMsg, wParam, lParam);6068}6069}60706071void DisplayServerWindows::_process_activate_event(WindowID p_window_id) {6072WindowData &wd = windows[p_window_id];6073if (wd.activate_state == WA_ACTIVE || wd.activate_state == WA_CLICKACTIVE) {6074last_focused_window = p_window_id;6075_set_mouse_mode_impl(mouse_mode);6076if (!IsIconic(wd.hWnd)) {6077SetFocus(wd.hWnd);6078}6079wd.window_focused = true;6080#ifdef ACCESSKIT_ENABLED6081if (accessibility_driver) {6082accessibility_driver->accessibility_set_window_focused(p_window_id, true);6083}6084#endif6085_send_window_event(wd, WINDOW_EVENT_FOCUS_IN);6086} else { // WM_INACTIVE.6087Input::get_singleton()->release_pressed_events();6088track_mouse_leave_event(wd.hWnd);6089// Release capture unconditionally because it can be set due to dragging, in addition to captured mode.6090// When the user is moving a window, it's important to not ReleaseCapture because it will cause6091// the window movement to stop and if the user tries to move the Windows when it's not activated,6092// it will prevent the window movement. If we are here and a window is moving, it's because we had multiple6093// opened windows in the editor and we are definitively not in a middle of dragging.6094if (!_has_moving_window()) {6095ReleaseCapture();6096}6097wd.window_focused = false;6098#ifdef ACCESSKIT_ENABLED6099if (accessibility_driver) {6100accessibility_driver->accessibility_set_window_focused(p_window_id, false);6101}6102#endif6103_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);6104}61056106if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {6107wintab_WTEnable(wd.wtctx, wd.activate_state);6108}6109}61106111void DisplayServerWindows::_process_key_events() {6112for (int i = 0; i < key_event_pos; i++) {6113KeyEvent &ke = key_event_buffer[i];6114switch (ke.uMsg) {6115case WM_CHAR: {6116// Extended keys should only be processed as WM_KEYDOWN message.6117if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) {6118static char32_t prev_wc = 0;6119char32_t unicode = ke.wParam;6120if ((unicode & 0xfffffc00) == 0xd800) {6121if (prev_wc != 0) {6122ERR_PRINT("invalid utf16 surrogate input");6123}6124prev_wc = unicode;6125break; // Skip surrogate.6126} else if ((unicode & 0xfffffc00) == 0xdc00) {6127if (prev_wc == 0) {6128ERR_PRINT("invalid utf16 surrogate input");6129break; // Skip invalid surrogate.6130}6131unicode = (prev_wc << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);6132prev_wc = 0;6133} else {6134prev_wc = 0;6135}6136Ref<InputEventKey> k;6137k.instantiate();61386139UINT vk = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK);6140bool is_oem = (vk >= 0xB8) && (vk <= 0xE6);6141Key keycode = KeyMappingWindows::get_keysym(vk);6142Key key_label = keycode;6143Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));61446145static BYTE keyboard_state[256];6146memset(keyboard_state, 0, 256);6147wchar_t chars[256] = {};6148UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX);6149if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) {6150String keysym = String::utf16((char16_t *)chars, 255);6151if (!keysym.is_empty()) {6152char32_t unicode_value = keysym[0];6153// For printable ASCII characters (0x20-0x7E), override the original keycode with the character value.6154if (is_oem && Key::SPACE <= (Key)unicode_value && (Key)unicode_value <= Key::ASCIITILDE) {6155keycode = fix_keycode(unicode_value, (Key)unicode_value);6156}6157key_label = fix_key_label(unicode_value, keycode);6158}6159}61606161k->set_window_id(ke.window_id);6162if (keycode != Key::SHIFT) {6163k->set_shift_pressed(ke.shift);6164}6165if (keycode != Key::ALT) {6166k->set_alt_pressed(ke.alt);6167}6168if (keycode != Key::CTRL) {6169k->set_ctrl_pressed(ke.control);6170}6171if (keycode != Key::META) {6172k->set_meta_pressed(ke.meta);6173}6174k->set_pressed(true);6175k->set_keycode(keycode);6176k->set_physical_keycode(physical_keycode);6177k->set_key_label(key_label);6178k->set_unicode(fix_unicode(unicode));6179if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {6180k->set_alt_pressed(false);6181k->set_ctrl_pressed(false);6182}61836184Input::get_singleton()->parse_input_event(k);6185} else {6186// Do nothing.6187}6188} break;6189case WM_KEYUP:6190case WM_KEYDOWN: {6191Ref<InputEventKey> k;6192k.instantiate();61936194k->set_window_id(ke.window_id);6195k->set_pressed(ke.uMsg == WM_KEYDOWN);61966197bool is_oem = (ke.wParam >= 0xB8) && (ke.wParam <= 0xE6);6198Key keycode = KeyMappingWindows::get_keysym(ke.wParam);6199if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) {6200// Special case for Numpad Enter key.6201keycode = Key::KP_ENTER;6202}6203Key key_label = keycode;6204Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));6205KeyLocation location = KeyMappingWindows::get_location((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));62066207static BYTE keyboard_state[256];6208memset(keyboard_state, 0, 256);6209wchar_t chars[256] = {};6210UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX);6211if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) {6212String keysym = String::utf16((char16_t *)chars, 255);6213if (!keysym.is_empty()) {6214char32_t unicode_value = keysym[0];6215// For printable ASCII characters (0x20-0x7E), override the original keycode with the character value.6216if (is_oem && Key::SPACE <= (Key)unicode_value && (Key)unicode_value <= Key::ASCIITILDE) {6217keycode = fix_keycode(unicode_value, (Key)unicode_value);6218}6219key_label = fix_key_label(unicode_value, keycode);6220}6221}62226223if (keycode != Key::SHIFT) {6224k->set_shift_pressed(ke.shift);6225}6226if (keycode != Key::ALT) {6227k->set_alt_pressed(ke.alt);6228}6229if (keycode != Key::CTRL) {6230k->set_ctrl_pressed(ke.control);6231}6232if (keycode != Key::META) {6233k->set_meta_pressed(ke.meta);6234}6235k->set_keycode(keycode);6236k->set_physical_keycode(physical_keycode);6237k->set_location(location);6238k->set_key_label(key_label);62396240if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {6241char32_t unicode = key_event_buffer[i + 1].wParam;6242static char32_t prev_wck = 0;6243if ((unicode & 0xfffffc00) == 0xd800) {6244if (prev_wck != 0) {6245ERR_PRINT("invalid utf16 surrogate input");6246}6247prev_wck = unicode;6248break; // Skip surrogate.6249} else if ((unicode & 0xfffffc00) == 0xdc00) {6250if (prev_wck == 0) {6251ERR_PRINT("invalid utf16 surrogate input");6252break; // Skip invalid surrogate.6253}6254unicode = (prev_wck << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);6255prev_wck = 0;6256} else {6257prev_wck = 0;6258}6259k->set_unicode(fix_unicode(unicode));6260}6261if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {6262k->set_alt_pressed(false);6263k->set_ctrl_pressed(false);6264}62656266k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));62676268Input::get_singleton()->parse_input_event(k);62696270} break;6271}6272}62736274key_event_pos = 0;6275}62766277void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const String &p_new_driver) {6278for (KeyValue<WindowID, WindowData> &E : windows) {6279WindowData &wd = E.value;6280wd.block_mm = false;6281if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) {6282wintab_WTEnable(wd.wtctx, false);6283wintab_WTClose(wd.wtctx);6284wd.wtctx = nullptr;6285}6286if ((p_new_driver == "wintab") && wintab_available) {6287wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);6288wd.wtlc.lcOptions |= CXO_MESSAGES;6289wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;6290wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;6291wd.wtlc.lcPktMode = 0;6292wd.wtlc.lcOutOrgX = 0;6293wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;6294wd.wtlc.lcOutOrgY = 0;6295wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;6296wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);6297if (wd.wtctx) {6298wintab_WTEnable(wd.wtctx, true);6299AXIS pressure;6300if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {6301wd.min_pressure = int(pressure.axMin);6302wd.max_pressure = int(pressure.axMax);6303}6304AXIS orientation[3];6305if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {6306wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;6307}6308wintab_WTEnable(wd.wtctx, true);6309} else {6310print_verbose("WinTab context creation failed.");6311}6312}6313}6314}63156316DisplayServer::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) {6317DWORD dwExStyle;6318DWORD dwStyle;63196320_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);63216322int rq_screen = get_screen_from_rect(p_rect);6323if (rq_screen < 0) {6324rq_screen = get_primary_screen(); // Requested window rect is outside any screen bounds.6325}6326Rect2i usable_rect = screen_get_usable_rect(rq_screen);63276328Point2i offset = _get_screens_origin();63296330RECT WindowRect;63316332int off_x = (p_mode == WINDOW_MODE_FULLSCREEN || ((p_flags & WINDOW_FLAG_BORDERLESS_BIT) && p_mode == WINDOW_MODE_MAXIMIZED)) ? FS_TRANSP_BORDER : 0;63336334WindowRect.left = p_rect.position.x;6335WindowRect.right = p_rect.position.x + p_rect.size.x + off_x;6336WindowRect.top = p_rect.position.y;6337WindowRect.bottom = p_rect.position.y + p_rect.size.y;63386339if (!p_parent_hwnd) {6340if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6341Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));63426343WindowRect.left = screen_rect.position.x;6344WindowRect.right = screen_rect.position.x + screen_rect.size.x + off_x;6345WindowRect.top = screen_rect.position.y;6346WindowRect.bottom = screen_rect.position.y + screen_rect.size.y;6347} else {6348Rect2i srect = screen_get_usable_rect(rq_screen);6349Point2i wpos = p_rect.position;6350if (srect != Rect2i()) {6351wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);6352}63536354WindowRect.left = wpos.x;6355WindowRect.right = wpos.x + p_rect.size.x + off_x;6356WindowRect.top = wpos.y;6357WindowRect.bottom = wpos.y + p_rect.size.y;6358}6359}63606361WindowRect.left += offset.x;6362WindowRect.right += offset.x;6363WindowRect.top += offset.y;6364WindowRect.bottom += offset.y;63656366if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6367AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);6368}63696370WindowID id = window_id_counter;6371{6372WindowData *wd_transient_parent = nullptr;6373HWND owner_hwnd = nullptr;6374if (p_parent_hwnd) {6375owner_hwnd = p_parent_hwnd;6376} else if (p_transient_parent != INVALID_WINDOW_ID) {6377if (!windows.has(p_transient_parent)) {6378ERR_PRINT("Condition \"!windows.has(p_transient_parent)\" is true.");6379p_transient_parent = INVALID_WINDOW_ID;6380} else {6381wd_transient_parent = &windows[p_transient_parent];6382if (p_exclusive) {6383owner_hwnd = wd_transient_parent->hWnd;6384}6385}6386}63876388WindowData &wd = windows[id];63896390wd.id = id;6391wd.hWnd = CreateWindowExW(6392dwExStyle,6393L"Engine", L"",6394dwStyle,6395WindowRect.left,6396WindowRect.top,6397WindowRect.right - WindowRect.left,6398WindowRect.bottom - WindowRect.top,6399owner_hwnd,6400nullptr,6401hInstance,6402// tunnel the WindowData we need to handle creation message6403// lifetime is ensured because we are still on the stack when this is6404// processed in the window proc6405reinterpret_cast<void *>(&wd));6406if (!wd.hWnd) {6407MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);6408windows.erase(id);6409ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window.");6410}64116412wd.parent_hwnd = p_parent_hwnd;64136414// Detach the input queue from the parent window.6415// This prevents the embedded window from waiting on the main window's input queue,6416// causing lags input lags when resizing or moving the main window.6417if (p_parent_hwnd) {6418DWORD mainThreadId = GetWindowThreadProcessId(owner_hwnd, nullptr);6419DWORD embeddedThreadId = GetCurrentThreadId();6420AttachThreadInput(embeddedThreadId, mainThreadId, FALSE);6421}64226423if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6424wd.fullscreen = true;6425if (p_mode == WINDOW_MODE_FULLSCREEN) {6426wd.multiwindow_fs = true;6427}6428}64296430if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {6431// Save initial non-fullscreen rect.6432Rect2i srect = screen_get_usable_rect(rq_screen);6433Point2i wpos = p_rect.position;6434if (srect != Rect2i()) {6435wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);6436}64376438wd.pre_fs_rect.left = wpos.x + offset.x;6439wd.pre_fs_rect.right = wpos.x + p_rect.size.x + offset.x;6440wd.pre_fs_rect.top = wpos.y + offset.y;6441wd.pre_fs_rect.bottom = wpos.y + p_rect.size.y + offset.y;6442wd.pre_fs_valid = true;6443}64446445wd.exclusive = p_exclusive;6446if (wd_transient_parent) {6447wd.transient_parent = p_transient_parent;6448wd_transient_parent->transient_children.insert(id);6449}64506451wd.sharp_corners = p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT;6452{6453DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;6454::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));6455}64566457if (is_dark_mode_supported() && dark_title_available) {6458BOOL value = is_dark_mode();6459::DwmSetWindowAttribute(wd.hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));6460}64616462RECT real_client_rect;6463GetClientRect(wd.hWnd, &real_client_rect);64646465#ifdef RD_ENABLED6466if (rendering_context) {6467union {6468#ifdef VULKAN_ENABLED6469RenderingContextDriverVulkanWindows::WindowPlatformData vulkan;6470#endif6471#ifdef D3D12_ENABLED6472RenderingContextDriverD3D12::WindowPlatformData d3d12;6473#endif6474} wpd;6475#ifdef VULKAN_ENABLED6476if (rendering_driver == "vulkan") {6477wpd.vulkan.window = wd.hWnd;6478wpd.vulkan.instance = hInstance;6479}6480#endif6481#ifdef D3D12_ENABLED6482if (rendering_driver == "d3d12") {6483wpd.d3d12.window = wd.hWnd;6484}6485#endif6486if (rendering_context->window_create(id, &wpd) != OK) {6487ERR_PRINT(vformat("Failed to create %s window.", rendering_driver));6488memdelete(rendering_context);6489rendering_context = nullptr;6490windows.erase(id);6491return INVALID_WINDOW_ID;6492}64936494rendering_context->window_set_size(id, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top);6495rendering_context->window_set_vsync_mode(id, p_vsync_mode);6496wd.context_created = true;6497}6498#endif64996500#ifdef GLES3_ENABLED6501if (gl_manager_native) {6502if (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) {6503memdelete(gl_manager_native);6504gl_manager_native = nullptr;6505windows.erase(id);6506ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");6507}6508window_set_vsync_mode(p_vsync_mode, id);6509}65106511if (gl_manager_angle) {6512if (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) {6513memdelete(gl_manager_angle);6514gl_manager_angle = nullptr;6515windows.erase(id);6516ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");6517}6518window_set_vsync_mode(p_vsync_mode, id);6519}6520#endif65216522RegisterTouchWindow(wd.hWnd, 0);6523DragAcceptFiles(wd.hWnd, true);65246525if ((tablet_get_current_driver() == "wintab") && wintab_available) {6526wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);6527wd.wtlc.lcOptions |= CXO_MESSAGES;6528wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;6529wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;6530wd.wtlc.lcPktMode = 0;6531wd.wtlc.lcOutOrgX = 0;6532wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;6533wd.wtlc.lcOutOrgY = 0;6534wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;6535wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);6536if (wd.wtctx) {6537wintab_WTEnable(wd.wtctx, true);6538AXIS pressure;6539if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {6540wd.min_pressure = int(pressure.axMin);6541wd.max_pressure = int(pressure.axMax);6542}6543AXIS orientation[3];6544if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {6545wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;6546}6547} else {6548print_verbose("WinTab context creation failed.");6549}6550} else {6551wd.wtctx = nullptr;6552}65536554if (p_mode == WINDOW_MODE_MAXIMIZED) {6555wd.maximized = true;6556wd.minimized = false;6557}65586559if (p_mode == WINDOW_MODE_MINIMIZED) {6560wd.maximized = false;6561wd.minimized = true;6562}65636564wd.last_pressure = 0;6565wd.last_pressure_update = 0;6566wd.last_tilt = Vector2();65676568IPropertyStore *prop_store;6569HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);6570if (hr == S_OK) {6571PROPVARIANT val;6572String appname;6573if (Engine::get_singleton()->is_editor_hint()) {6574appname = "Godot.GodotEditor." + String(GODOT_VERSION_FULL_CONFIG);6575} else {6576String name = GLOBAL_GET("application/config/name");6577String version = GLOBAL_GET("application/config/version");6578if (version.is_empty()) {6579version = "0";6580}6581String clean_app_name = name.to_pascal_case();6582for (int i = 0; i < clean_app_name.length(); i++) {6583if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {6584clean_app_name[i] = '_';6585}6586}6587clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");6588appname = "Godot." + clean_app_name + "." + version;6589}6590InitPropVariantFromString((PCWSTR)appname.utf16().get_data(), &val);6591prop_store->SetValue(PKEY_AppUserModel_ID, val);6592prop_store->Release();6593}65946595// IME.6596wd.im_himc = ImmGetContext(wd.hWnd);6597ImmAssociateContext(wd.hWnd, (HIMC) nullptr);65986599wd.im_position = Vector2();66006601if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN || p_mode == WINDOW_MODE_MAXIMIZED) {6602RECT r;6603GetClientRect(wd.hWnd, &r);6604ClientToScreen(wd.hWnd, (POINT *)&r.left);6605ClientToScreen(wd.hWnd, (POINT *)&r.right);6606wd.last_pos = Point2i(r.left, r.top) - _get_screens_origin();6607wd.width = r.right - r.left - off_x;6608wd.height = r.bottom - r.top;6609} else {6610wd.last_pos = p_rect.position;6611wd.width = p_rect.size.width;6612wd.height = p_rect.size.height;6613}66146615wd.create_completed = true;6616// Set size of maximized borderless window (by default it covers the entire screen).6617if (!p_parent_hwnd && p_mode == WINDOW_MODE_MAXIMIZED && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {6618SetWindowPos(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);6619}6620_update_window_mouse_passthrough(id);6621window_id_counter++;6622}66236624return id;6625}66266627BitField<DisplayServerWindows::DriverID> DisplayServerWindows::tested_drivers = 0;66286629// WinTab API.6630bool DisplayServerWindows::wintab_available = false;6631WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr;6632WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr;6633WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr;6634WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;6635WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;66366637// UXTheme API.6638bool DisplayServerWindows::dark_title_available = false;6639bool DisplayServerWindows::use_legacy_dark_mode_before_20H1 = false;6640bool DisplayServerWindows::ux_theme_available = false;6641ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;6642GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;6643GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr;6644GetImmersiveUserColorSetPreferencePtr DisplayServerWindows::GetImmersiveUserColorSetPreference = nullptr;66456646Vector2i _get_device_ids_reg(const String &p_device_name) {6647Vector2i out;66486649String subkey = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}";6650HKEY hkey = nullptr;6651LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)subkey.utf16().get_data(), 0, KEY_READ, &hkey);6652if (result != ERROR_SUCCESS) {6653return Vector2i();6654}66556656DWORD subkeys = 0;6657result = RegQueryInfoKeyW(hkey, nullptr, nullptr, nullptr, &subkeys, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);6658if (result != ERROR_SUCCESS) {6659RegCloseKey(hkey);6660return Vector2i();6661}6662for (DWORD i = 0; i < subkeys; i++) {6663WCHAR key_name[MAX_PATH] = L"";6664DWORD key_name_size = MAX_PATH;6665result = RegEnumKeyExW(hkey, i, key_name, &key_name_size, nullptr, nullptr, nullptr, nullptr);6666if (result != ERROR_SUCCESS) {6667continue;6668}6669String id = String::utf16((const char16_t *)key_name, key_name_size);6670if (!id.is_empty()) {6671HKEY sub_hkey = nullptr;6672result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(subkey + "\\" + id).utf16().get_data(), 0, KEY_QUERY_VALUE, &sub_hkey);6673if (result != ERROR_SUCCESS) {6674continue;6675}66766677WCHAR buffer[4096];6678DWORD buffer_len = 4096;6679DWORD vtype = REG_SZ;6680if (RegQueryValueExW(sub_hkey, L"DriverDesc", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) != ERROR_SUCCESS || buffer_len == 0) {6681buffer_len = 4096;6682if (RegQueryValueExW(sub_hkey, L"HardwareInformation.AdapterString", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) != ERROR_SUCCESS || buffer_len == 0) {6683RegCloseKey(sub_hkey);6684continue;6685}6686}66876688String driver_name = String::utf16((const char16_t *)buffer, buffer_len).strip_edges();6689if (driver_name == p_device_name) {6690String driver_id;66916692buffer_len = 4096;6693if (RegQueryValueExW(sub_hkey, L"MatchingDeviceId", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) == ERROR_SUCCESS && buffer_len != 0) {6694driver_id = String::utf16((const char16_t *)buffer, buffer_len).strip_edges();66956696Vector<String> id_parts = driver_id.to_lower().split("&");6697for (const String &id_part : id_parts) {6698int ven_off = id_part.find("ven_");6699if (ven_off >= 0) {6700out.x = id_part.substr(ven_off + 4).hex_to_int();6701}6702int dev_off = id_part.find("dev_");6703if (dev_off >= 0) {6704out.y = id_part.substr(dev_off + 4).hex_to_int();6705}6706}67076708RegCloseKey(sub_hkey);6709break;6710}6711}6712RegCloseKey(sub_hkey);6713}6714}6715RegCloseKey(hkey);6716return out;6717}67186719Vector2i _get_device_ids_wmi(const String &p_device_name) {6720if (p_device_name.is_empty()) {6721return Vector2i();6722}67236724REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID6725REFIID uuid = IID_IWbemLocator; // Interface UUID6726IWbemLocator *wbemLocator = nullptr; // to get the services6727IWbemServices *wbemServices = nullptr; // to get the class6728IEnumWbemClassObject *iter = nullptr;6729IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.67306731HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);6732if (hr != S_OK) {6733return Vector2i();6734}6735BSTR resource_name = SysAllocString(L"root\\CIMV2");6736hr = wbemLocator->ConnectServer(resource_name, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemServices);6737SysFreeString(resource_name);67386739SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices`6740if (hr != S_OK) {6741SAFE_RELEASE(wbemServices)6742return Vector2i();6743}67446745Vector2i ids;67466747const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", p_device_name);6748BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data());6749BSTR query_lang = SysAllocString(L"WQL");6750hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, nullptr, &iter);6751SysFreeString(query_lang);6752SysFreeString(query);6753if (hr == S_OK) {6754ULONG resultCount;6755hr = iter->Next(5000, 1, pnpSDriverObject, &resultCount); // Get exactly 1. Wait max 5 seconds.67566757if (hr == S_OK && resultCount > 0) {6758VARIANT did;6759VariantInit(&did);6760BSTR object_name = SysAllocString(L"DeviceID");6761hr = pnpSDriverObject[0]->Get(object_name, 0, &did, nullptr, nullptr);6762SysFreeString(object_name);6763if (hr == S_OK) {6764String device_id = String(V_BSTR(&did));6765ids.x = device_id.get_slicec('&', 0).lstrip("PCI\\VEN_").hex_to_int();6766ids.y = device_id.get_slicec('&', 1).lstrip("DEV_").hex_to_int();6767}67686769for (ULONG i = 0; i < resultCount; i++) {6770SAFE_RELEASE(pnpSDriverObject[i])6771}6772}6773}67746775SAFE_RELEASE(wbemServices)6776SAFE_RELEASE(iter)67776778return ids;6779}67806781Vector2i _get_device_ids(const String &p_device_name) {6782Vector2i out = _get_device_ids_reg(p_device_name);6783if (out == Vector2i()) {6784out = _get_device_ids_wmi(p_device_name);6785}6786return out;6787}67886789bool DisplayServerWindows::is_dark_mode_supported() const {6790return ux_theme_available;6791}67926793bool DisplayServerWindows::is_dark_mode() const {6794return ux_theme_available && ShouldAppsUseDarkMode();6795}67966797Color DisplayServerWindows::get_accent_color() const {6798if (!ux_theme_available) {6799return Color(0, 0, 0, 0);6800}68016802int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"), false, 0);6803return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);6804}68056806Color DisplayServerWindows::get_base_color() const {6807if (!ux_theme_available) {6808return Color(0, 0, 0, 0);6809}68106811int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(ShouldAppsUseDarkMode() ? L"ImmersiveDarkChromeMediumLow" : L"ImmersiveLightChromeMediumLow"), false, 0);6812return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);6813}68146815void DisplayServerWindows::set_system_theme_change_callback(const Callable &p_callable) {6816system_theme_changed = p_callable;6817}68186819int DisplayServerWindows::tablet_get_driver_count() const {6820return tablet_drivers.size();6821}68226823String DisplayServerWindows::tablet_get_driver_name(int p_driver) const {6824if (p_driver < 0 || p_driver >= tablet_drivers.size()) {6825return "";6826} else {6827return tablet_drivers[p_driver];6828}6829}68306831String DisplayServerWindows::tablet_get_current_driver() const {6832return tablet_driver;6833}68346835void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {6836if (tablet_get_driver_count() == 0) {6837return;6838}68396840String driver = p_driver;6841if (driver == "auto") {6842if (!winink_disabled) {6843driver = "winink";6844} else if (wintab_available) {6845driver = "wintab";6846} else {6847driver = "dummy";6848}6849}68506851bool found = false;6852for (int i = 0; i < tablet_get_driver_count(); i++) {6853if (driver == tablet_get_driver_name(i)) {6854found = true;6855}6856}6857if (found) {6858_update_tablet_ctx(tablet_driver, driver);6859tablet_driver = driver;6860} else {6861ERR_PRINT("Unknown tablet driver " + p_driver + ".");6862}6863}68646865DisplayServerWindows::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) {6866KeyMappingWindows::initialize();68676868tested_drivers.clear();68696870drop_events = false;6871key_event_pos = 0;68726873hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance();68746875pressrc = 0;6876old_invalid = true;6877mouse_mode = MOUSE_MODE_VISIBLE;68786879rendering_driver = p_rendering_driver;68806881// Init TTS6882bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");6883if (tts_enabled) {6884initialize_tts();6885}6886native_menu = memnew(NativeMenuWindows);68876888#ifdef ACCESSKIT_ENABLED6889if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {6890accessibility_driver = memnew(AccessibilityDriverAccessKit);6891if (accessibility_driver->init() != OK) {6892if (OS::get_singleton()->is_stdout_verbose()) {6893ERR_PRINT("Can't create an accessibility driver, accessibility support disabled!");6894}6895memdelete(accessibility_driver);6896accessibility_driver = nullptr;6897}6898}6899#endif69006901// Enforce default keep screen on value.6902screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));69036904// Load Windows version info.6905ZeroMemory(&os_ver, sizeof(OSVERSIONINFOW));6906os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);69076908HMODULE nt_lib = LoadLibraryW(L"ntdll.dll");6909bool is_wine = false;6910if (nt_lib) {6911WineGetVersionPtr 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.6912if (wine_get_version) {6913is_wine = true;6914} else {6915RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)(void *)GetProcAddress(nt_lib, "RtlGetVersion");6916if (RtlGetVersion) {6917RtlGetVersion(&os_ver);6918}6919}6920FreeLibrary(nt_lib);6921}69226923// Load UXTheme.6924if (os_ver.dwBuildNumber >= 10240) { // Not available on Wine, use only if real Windows 10/11 detected.6925HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");6926if (ux_theme_lib) {6927ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132));6928GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));6929GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));6930GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));6931if (os_ver.dwBuildNumber >= 17763) { // Windows 10 Redstone 5 (1809)+ only.6932AllowDarkModeForAppPtr AllowDarkModeForApp = nullptr;6933SetPreferredAppModePtr SetPreferredAppMode = nullptr;6934FlushMenuThemesPtr FlushMenuThemes = nullptr;6935if (os_ver.dwBuildNumber < 18362) { // Windows 10 Redstone 5 (1809) and 19H1 (1903) only.6936AllowDarkModeForApp = (AllowDarkModeForAppPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));6937} else { // Windows 10 19H2 (1909)+ only.6938SetPreferredAppMode = (SetPreferredAppModePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));6939FlushMenuThemes = (FlushMenuThemesPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));6940}6941RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyState = (RefreshImmersiveColorPolicyStatePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(104));6942if (ShouldAppsUseDarkMode) {6943bool dark_mode = ShouldAppsUseDarkMode();6944if (SetPreferredAppMode) {6945SetPreferredAppMode(dark_mode ? APPMODE_ALLOWDARK : APPMODE_DEFAULT);6946} else if (AllowDarkModeForApp) {6947AllowDarkModeForApp(dark_mode);6948}6949if (RefreshImmersiveColorPolicyState) {6950RefreshImmersiveColorPolicyState();6951}6952if (FlushMenuThemes) {6953FlushMenuThemes();6954}6955}6956}69576958ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;6959if (os_ver.dwBuildNumber >= 18363) {6960dark_title_available = true;6961if (os_ver.dwBuildNumber < 19041) {6962use_legacy_dark_mode_before_20H1 = true;6963}6964}6965}6966}69676968tablet_drivers.push_back("auto");6969tablet_drivers.push_back("winink");69706971// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.6972HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");6973if (wintab_lib) {6974wintab_WTOpen = (WTOpenPtr)(void *)GetProcAddress(wintab_lib, "WTOpenW");6975wintab_WTClose = (WTClosePtr)(void *)GetProcAddress(wintab_lib, "WTClose");6976wintab_WTInfo = (WTInfoPtr)(void *)GetProcAddress(wintab_lib, "WTInfoW");6977wintab_WTPacket = (WTPacketPtr)(void *)GetProcAddress(wintab_lib, "WTPacket");6978wintab_WTEnable = (WTEnablePtr)(void *)GetProcAddress(wintab_lib, "WTEnable");69796980wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable;6981}69826983if (wintab_available) {6984tablet_drivers.push_back("wintab");6985}69866987tablet_drivers.push_back("dummy");69886989String wacom_cfg = OS::get_singleton()->get_config_path().path_join("WTablet").path_join("Wacom_Tablet.dat");6990if (FileAccess::exists(wacom_cfg)) {6991Ref<XMLParser> parser;6992parser.instantiate();6993if (parser->open(wacom_cfg) == OK) {6994while (parser->read() == OK) {6995if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {6996continue;6997}6998if (parser->get_node_name() == "WinUseInk") {6999parser->read();7000if (parser->get_node_type() == XMLParser::NODE_TEXT) {7001winink_disabled = (parser->get_node_data().to_lower().strip_edges() != "true");7002print_verbose(vformat("Wacom tablet config found at \"%s\", Windows Ink support is %s.", wacom_cfg, winink_disabled ? "disabled" : "enabled"));7003break;7004}7005}7006}7007}7008}70097010if (OS::get_singleton()->is_hidpi_allowed()) {7011SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE);7012}70137014HMODULE comctl32 = LoadLibraryW(L"comctl32.dll");7015if (comctl32) {7016typedef BOOL(WINAPI * InitCommonControlsExPtr)(_In_ const INITCOMMONCONTROLSEX *picce);7017InitCommonControlsExPtr init_common_controls_ex = (InitCommonControlsExPtr)(void *)GetProcAddress(comctl32, "InitCommonControlsEx");70187019// Fails if the incorrect version was loaded. Probably not a big enough deal to print an error about.7020if (init_common_controls_ex) {7021INITCOMMONCONTROLSEX icc = {};7022icc.dwICC = ICC_STANDARD_CLASSES;7023icc.dwSize = sizeof(INITCOMMONCONTROLSEX);7024if (!init_common_controls_ex(&icc)) {7025WARN_PRINT("Unable to initialize Windows common controls. Native dialogs may not work properly.");7026}7027}7028FreeLibrary(comctl32);7029}70307031OleInitialize(nullptr);70327033memset(&wc, 0, sizeof(WNDCLASSEXW));7034wc.cbSize = sizeof(WNDCLASSEXW);7035wc.style = CS_OWNDC | CS_DBLCLKS;7036wc.lpfnWndProc = (WNDPROC)::WndProc;7037wc.cbClsExtra = 0;7038wc.cbWndExtra = 0;7039wc.hInstance = hInstance ? hInstance : GetModuleHandle(nullptr);7040wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO);7041wc.hCursor = nullptr;7042wc.hbrBackground = nullptr;7043wc.lpszMenuName = nullptr;7044wc.lpszClassName = L"Engine";70457046if (!RegisterClassExW(&wc)) {7047r_error = ERR_UNAVAILABLE;7048return;7049}70507051_register_raw_input_devices(INVALID_WINDOW_ID);70527053// Init context and rendering device.7054if (rendering_driver == "dummy") {7055RasterizerDummy::make_current();7056}70577058#if defined(RD_ENABLED)7059[[maybe_unused]] bool fallback_to_vulkan = GLOBAL_GET("rendering/rendering_device/fallback_to_vulkan");7060[[maybe_unused]] bool fallback_to_d3d12 = GLOBAL_GET("rendering/rendering_device/fallback_to_d3d12");70617062#if defined(VULKAN_ENABLED)7063if (rendering_driver == "vulkan") {7064rendering_context = memnew(RenderingContextDriverVulkanWindows);7065tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);7066}7067#else7068fallback_to_d3d12 = true; // Always enable fallback if engine was built w/o other driver support.7069#endif7070#if defined(D3D12_ENABLED)7071if (rendering_driver == "d3d12") {7072rendering_context = memnew(RenderingContextDriverD3D12);7073tested_drivers.set_flag(DRIVER_ID_RD_D3D12);7074}7075#else7076fallback_to_vulkan = true; // Always enable fallback if engine was built w/o other driver support.7077#endif70787079if (rendering_context) {7080if (rendering_context->initialize() != OK) {7081bool failed = true;7082#if defined(VULKAN_ENABLED)7083if (failed && fallback_to_vulkan && rendering_driver != "vulkan") {7084memdelete(rendering_context);7085rendering_context = memnew(RenderingContextDriverVulkanWindows);7086tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);7087if (rendering_context->initialize() == OK) {7088WARN_PRINT("Your video card drivers seem not to support Direct3D 12, switching to Vulkan.");7089rendering_driver = "vulkan";7090OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);7091failed = false;7092}7093}7094#endif7095#if defined(D3D12_ENABLED)7096if (failed && fallback_to_d3d12 && rendering_driver != "d3d12") {7097memdelete(rendering_context);7098rendering_context = memnew(RenderingContextDriverD3D12);7099tested_drivers.set_flag(DRIVER_ID_RD_D3D12);7100if (rendering_context->initialize() == OK) {7101WARN_PRINT("Your video card drivers seem not to support Vulkan, switching to Direct3D 12.");7102rendering_driver = "d3d12";7103OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);7104failed = false;7105}7106}7107#endif7108#if defined(GLES3_ENABLED)7109bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");7110if (failed && fallback_to_opengl3 && rendering_driver != "opengl3") {7111memdelete(rendering_context);7112rendering_context = nullptr;7113tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);7114WARN_PRINT("Your video card drivers seem not to support Direct3D 12 or Vulkan, switching to OpenGL 3.");7115rendering_driver = "opengl3";7116OS::get_singleton()->set_current_rendering_method("gl_compatibility");7117OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);7118failed = false;7119}7120#endif7121if (failed) {7122memdelete(rendering_context);7123rendering_context = nullptr;7124r_error = ERR_UNAVAILABLE;7125return;7126}7127}7128}7129#endif71307131#if defined(GLES3_ENABLED)71327133bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_angle");7134bool show_warning = true;71357136if (rendering_driver == "opengl3") {7137// There's no native OpenGL drivers on Windows for ARM, always enable fallback.7138#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)7139fallback = true;7140show_warning = false;7141#else7142typedef BOOL(WINAPI * IsWow64Process2Ptr)(HANDLE, USHORT *, USHORT *);71437144IsWow64Process2Ptr IsWow64Process2 = (IsWow64Process2Ptr)(void *)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process2");7145if (IsWow64Process2) {7146USHORT process_arch = 0;7147USHORT machine_arch = 0;7148if (!IsWow64Process2(GetCurrentProcess(), &process_arch, &machine_arch)) {7149machine_arch = 0;7150}7151if (machine_arch == 0xAA64) {7152fallback = true;7153show_warning = false;7154}7155}7156#endif7157}71587159bool gl_supported = true;7160if (fallback && !is_wine && (rendering_driver == "opengl3")) {7161Dictionary gl_info = detect_wgl();71627163bool force_angle = false;7164gl_supported = gl_info["version"].operator int() >= 30003;71657166Vector2i device_id = Vector2i(-1, -1);7167Array device_list = GLOBAL_GET("rendering/gl_compatibility/force_angle_on_devices");7168for (int i = 0; i < device_list.size(); i++) {7169const Dictionary &device = device_list[i];7170if (device.has("vendor") && device.has("name")) {7171const String &vendor = device["vendor"];7172const String &name = device["name"];7173if (gl_info["vendor"].operator String().containsn(vendor) && (name == "*" || gl_info["name"].operator String().containsn(name))) {7174// Check vendor/device names.7175force_angle = true;7176break;7177} else if (vendor.begins_with("0x") && name.begins_with("0x")) {7178if (device_id == Vector2i(-1, -1)) {7179// Load device IDs.7180device_id = _get_device_ids(gl_info["name"]);7181}7182if (device_id.x == vendor.lstrip("0x").hex_to_int() && device_id.y == name.lstrip("0x").hex_to_int()) {7183// Check vendor/device IDs.7184force_angle = true;7185break;7186}7187}7188}7189}71907191if (force_angle || (gl_info["version"].operator int() < 30003)) {7192tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);7193if (show_warning) {7194if (gl_info["version"].operator int() < 30003) {7195WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE.");7196} else {7197WARN_PRINT("Your video card drivers are known to have low quality OpenGL 3.3 support, switching to ANGLE.");7198}7199}7200rendering_driver = "opengl3_angle";7201OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);7202}7203}72047205if (rendering_driver == "opengl3_angle") {7206gl_manager_angle = memnew(GLManagerANGLE_Windows);7207tested_drivers.set_flag(DRIVER_ID_COMPAT_ANGLE_D3D11);72087209if (gl_manager_angle->initialize() != OK) {7210memdelete(gl_manager_angle);7211gl_manager_angle = nullptr;7212bool fallback_to_native = GLOBAL_GET("rendering/gl_compatibility/fallback_to_native");7213if (fallback_to_native && gl_supported) {7214#ifdef EGL_STATIC7215WARN_PRINT("Your video card drivers seem not to support GLES3 / ANGLE, switching to native OpenGL.");7216#else7217WARN_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.");7218#endif7219rendering_driver = "opengl3";7220} else {7221r_error = ERR_UNAVAILABLE;7222ERR_FAIL_MSG("Could not initialize ANGLE OpenGL.");7223}7224}7225}7226if (rendering_driver == "opengl3") {7227gl_manager_native = memnew(GLManagerNative_Windows);7228tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);72297230if (gl_manager_native->initialize() != OK) {7231memdelete(gl_manager_native);7232gl_manager_native = nullptr;7233r_error = ERR_UNAVAILABLE;7234ERR_FAIL_MSG("Could not initialize native OpenGL.");7235}7236}72377238if (rendering_driver == "opengl3") {7239RasterizerGLES3::make_current(true);7240}7241if (rendering_driver == "opengl3_angle") {7242RasterizerGLES3::make_current(false);7243}7244#endif7245String appname;7246if (Engine::get_singleton()->is_editor_hint()) {7247appname = "Godot.GodotEditor." + String(GODOT_VERSION_FULL_CONFIG);7248} else {7249String name = GLOBAL_GET("application/config/name");7250String version = GLOBAL_GET("application/config/version");7251if (version.is_empty()) {7252version = "0";7253}7254String clean_app_name = name.to_pascal_case();7255for (int i = 0; i < clean_app_name.length(); i++) {7256if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {7257clean_app_name[i] = '_';7258}7259}7260clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");7261appname = "Godot." + clean_app_name + "." + version;72627263#ifndef TOOLS_ENABLED7264// Set for exported projects only.7265HKEY key;7266if (RegOpenKeyW(HKEY_CURRENT_USER_LOCAL_SETTINGS, L"Software\\Microsoft\\Windows\\Shell\\MuiCache", &key) == ERROR_SUCCESS) {7267Char16String cs_name = name.utf16();7268String value_name = OS::get_singleton()->get_executable_path().replace_char('/', '\\') + ".FriendlyAppName";7269RegSetValueExW(key, (LPCWSTR)value_name.utf16().get_data(), 0, REG_SZ, (const BYTE *)cs_name.get_data(), cs_name.size() * sizeof(WCHAR));7270RegCloseKey(key);7271}7272#endif7273}7274SetCurrentProcessExplicitAppUserModelID((PCWSTR)appname.utf16().get_data());72757276mouse_monitor = SetWindowsHookEx(WH_MOUSE, ::MouseProc, nullptr, GetCurrentThreadId());72777278Point2i window_position;7279if (p_position != nullptr) {7280window_position = *p_position;7281} else {7282if (p_screen == SCREEN_OF_MAIN_WINDOW) {7283p_screen = SCREEN_PRIMARY;7284}7285Rect2i scr_rect = screen_get_usable_rect(p_screen);7286window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2;7287}72887289HWND parent_hwnd = NULL;7290if (p_parent_window) {7291// Parented window.7292parent_hwnd = (HWND)p_parent_window;7293}72947295WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID, parent_hwnd);7296if (main_window == INVALID_WINDOW_ID) {7297r_error = ERR_UNAVAILABLE;7298ERR_FAIL_MSG("Failed to create main window.");7299}73007301#ifdef SDL_ENABLED7302joypad_sdl = memnew(JoypadSDL(windows[MAIN_WINDOW_ID].hWnd));7303if (joypad_sdl->initialize() != OK) {7304ERR_PRINT("Couldn't initialize SDL joypad input driver.");7305memdelete(joypad_sdl);7306joypad_sdl = nullptr;7307}7308#endif73097310for (int i = 0; i < WINDOW_FLAG_MAX; i++) {7311if (p_flags & (1 << i)) {7312window_set_flag(WindowFlags(i), true, main_window);7313}7314}73157316windows[MAIN_WINDOW_ID].initialized = true;73177318#ifdef ACCESSKIT_ENABLED7319if (accessibility_screen_reader_active()) {7320_THREAD_SAFE_LOCK_7321uint64_t time_wait = OS::get_singleton()->get_ticks_msec();7322while (true) {7323MSG msg = {};7324while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {7325TranslateMessage(&msg);7326DispatchMessageW(&msg);7327}73287329uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_wait;7330if (delta > 500 || get_object_received) {7331break;7332}7333}7334_THREAD_SAFE_UNLOCK_7335}7336#endif73377338#if defined(RD_ENABLED)7339if (rendering_context) {7340rendering_device = memnew(RenderingDevice);7341if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {7342memdelete(rendering_device);7343rendering_device = nullptr;7344memdelete(rendering_context);7345rendering_context = nullptr;7346r_error = ERR_UNAVAILABLE;7347return;7348}7349rendering_device->screen_create(MAIN_WINDOW_ID);73507351RendererCompositorRD::make_current();7352}7353#endif73547355if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) {7356// Increase priority for projects that are not in low-processor mode (typically games)7357// to reduce the risk of frame stuttering.7358// This is not done for the editor to prevent importers or resource bakers7359// from making the system unresponsive.7360SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);7361DWORD index = 0;7362HANDLE handle = AvSetMmThreadCharacteristicsW(L"Games", &index);7363if (handle) {7364AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);7365}73667367// This is needed to make sure that background work does not starve the main thread.7368// This is only setting the priority of this thread, not the whole process.7369SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);7370}73717372cursor_shape = CURSOR_ARROW;73737374_update_real_mouse_position(MAIN_WINDOW_ID);73757376r_error = OK;73777378static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);7379Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);7380}73817382Vector<String> DisplayServerWindows::get_rendering_drivers_func() {7383Vector<String> drivers;73847385#ifdef VULKAN_ENABLED7386drivers.push_back("vulkan");7387#endif7388#ifdef D3D12_ENABLED7389drivers.push_back("d3d12");7390#endif7391#ifdef GLES3_ENABLED7392drivers.push_back("opengl3");7393drivers.push_back("opengl3_angle");7394#endif7395drivers.push_back("dummy");73967397return drivers;7398}73997400DisplayServer *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) {7401DisplayServer *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));7402if (r_error != OK) {7403if (tested_drivers == 0) {7404OS::get_singleton()->alert("Failed to register the window class.", "Unable to initialize DisplayServer");7405} else if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN) || tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {7406Vector<String> drivers;7407if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN)) {7408drivers.push_back("Vulkan");7409}7410if (tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {7411drivers.push_back("Direct3D 12");7412}7413String executable_name = OS::get_singleton()->get_executable_path().get_file();7414OS::get_singleton()->alert(7415vformat("Your video card drivers seem not to support the required %s version.\n\n"7416"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"7417"You can enable the OpenGL 3 driver by starting the engine from the\n"7418"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"7419"If you have recently updated your video card drivers, try rebooting.",7420String(" or ").join(drivers),7421executable_name),7422"Unable to initialize video driver");7423} else {7424Vector<String> drivers;7425if (tested_drivers.has_flag(DRIVER_ID_COMPAT_OPENGL3)) {7426drivers.push_back("OpenGL 3.3");7427}7428if (tested_drivers.has_flag(DRIVER_ID_COMPAT_ANGLE_D3D11)) {7429drivers.push_back("Direct3D 11");7430}7431OS::get_singleton()->alert(7432vformat(7433"Your video card drivers seem not to support the required %s version.\n\n"7434"If possible, consider updating your video card drivers.\n\n"7435"If you have recently updated your video card drivers, try rebooting.",7436String(" or ").join(drivers)),7437"Unable to initialize video driver");7438}7439}7440return ds;7441}74427443void DisplayServerWindows::register_windows_driver() {7444register_create_function("windows", create_func, get_rendering_drivers_func);7445}74467447DisplayServerWindows::~DisplayServerWindows() {7448LocalVector<List<FileDialogData *>::Element *> to_remove;7449for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {7450FileDialogData *fd = E->get();7451if (fd->listener_thread.is_started()) {7452fd->close_requested.set();7453fd->listener_thread.wait_to_finish();7454}7455to_remove.push_back(E);7456}7457for (List<FileDialogData *>::Element *E : to_remove) {7458memdelete(E->get());7459E->erase();7460}74617462#ifdef SDL_ENABLED7463if (joypad_sdl) {7464memdelete(joypad_sdl);7465}7466#endif7467touch_state.clear();74687469cursors_cache.clear();74707471// Destroy all status indicators.7472for (HashMap<IndicatorID, IndicatorData>::Iterator E = indicators.begin(); E; ++E) {7473NOTIFYICONDATAW ndat;7474ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));7475ndat.cbSize = sizeof(NOTIFYICONDATAW);7476ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;7477ndat.uID = E->key;7478ndat.uVersion = NOTIFYICON_VERSION;74797480Shell_NotifyIconW(NIM_DELETE, &ndat);7481}74827483if (mouse_monitor) {7484UnhookWindowsHookEx(mouse_monitor);7485}74867487if (user_proc) {7488SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);7489}74907491// Close power request handle.7492screen_set_keep_on(false);74937494if (native_menu) {7495memdelete(native_menu);7496native_menu = nullptr;7497}74987499#ifdef GLES3_ENABLED7500// destroy windows .. NYI?7501// FIXME wglDeleteContext is never called7502#endif75037504if (windows.has(MAIN_WINDOW_ID)) {7505#ifdef RD_ENABLED7506if (rendering_device) {7507rendering_device->screen_free(MAIN_WINDOW_ID);7508}75097510if (rendering_context) {7511rendering_context->window_destroy(MAIN_WINDOW_ID);7512}7513#endif7514if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) {7515wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);7516windows[MAIN_WINDOW_ID].wtctx = nullptr;7517}75187519if (windows[MAIN_WINDOW_ID].drop_target != nullptr) {7520RevokeDragDrop(windows[MAIN_WINDOW_ID].hWnd);7521windows[MAIN_WINDOW_ID].drop_target->Release();7522}75237524DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);7525}75267527#ifdef RD_ENABLED7528if (rendering_device) {7529memdelete(rendering_device);7530rendering_device = nullptr;7531}75327533if (rendering_context) {7534memdelete(rendering_context);7535rendering_context = nullptr;7536}7537#endif75387539if (restore_mouse_trails > 1) {7540SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);7541}7542#ifdef GLES3_ENABLED7543if (gl_manager_angle) {7544memdelete(gl_manager_angle);7545gl_manager_angle = nullptr;7546}7547if (gl_manager_native) {7548memdelete(gl_manager_native);7549gl_manager_native = nullptr;7550}7551#endif7552#ifdef ACCESSKIT_ENABLED7553if (accessibility_driver) {7554memdelete(accessibility_driver);7555}7556#endif7557if (tts) {7558memdelete(tts);7559}75607561OleUninitialize();7562}756375647565