diff options
Diffstat (limited to 'platform/windows')
-rw-r--r-- | platform/windows/SCsub | 25 | ||||
-rw-r--r-- | platform/windows/console_wrapper_windows.cpp | 181 | ||||
-rw-r--r-- | platform/windows/detect.py | 119 | ||||
-rw-r--r-- | platform/windows/display_server_windows.cpp | 393 | ||||
-rw-r--r-- | platform/windows/display_server_windows.h | 20 | ||||
-rw-r--r-- | platform/windows/export/export.cpp | 2 | ||||
-rw-r--r-- | platform/windows/export/export_plugin.cpp | 58 | ||||
-rw-r--r-- | platform/windows/export/export_plugin.h | 3 | ||||
-rw-r--r-- | platform/windows/gl_manager_windows.cpp | 17 | ||||
-rw-r--r-- | platform/windows/gl_manager_windows.h | 3 | ||||
-rw-r--r-- | platform/windows/godot.natvis | 84 | ||||
-rw-r--r-- | platform/windows/godot_res_wrap.rc | 31 | ||||
-rw-r--r-- | platform/windows/godot_windows.cpp | 4 | ||||
-rw-r--r-- | platform/windows/joypad_windows.cpp | 2 | ||||
-rw-r--r-- | platform/windows/joypad_windows.h | 2 | ||||
-rw-r--r-- | platform/windows/os_windows.cpp | 202 | ||||
-rw-r--r-- | platform/windows/os_windows.h | 26 | ||||
-rw-r--r-- | platform/windows/platform_config.h | 2 | ||||
-rw-r--r-- | platform/windows/vulkan_context_win.cpp | 4 | ||||
-rw-r--r-- | platform/windows/vulkan_context_win.h | 6 |
20 files changed, 800 insertions, 384 deletions
diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 7e412b140f..efbb47d965 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -19,19 +19,44 @@ common_win = [ "gl_manager_windows.cpp", ] +common_win_wrap = [ + "console_wrapper_windows.cpp", +] + res_file = "godot_res.rc" res_target = "godot_res" + env["OBJSUFFIX"] res_obj = env.RES(res_target, res_file) prog = env.add_program("#bin/godot", common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"]) +# Build console wrapper app. +if env["windows_subsystem"] == "gui": + env_wrap = env.Clone() + res_wrap_file = "godot_res_wrap.rc" + res_wrap_target = "godot_res_wrap" + env["OBJSUFFIX"] + res_wrap_obj = env_wrap.RES(res_wrap_target, res_wrap_file) + + if env.msvc: + env_wrap.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) + env_wrap.Append(LINKFLAGS=["version.lib"]) + else: + env_wrap.Append(LINKFLAGS=["-Wl,--subsystem,console"]) + env_wrap.Append(LIBS=["version"]) + + prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"]) + # Microsoft Visual Studio Project Generation if env["vsproj"]: env.vs_srcs += ["platform/windows/" + res_file] env.vs_srcs += ["platform/windows/godot.natvis"] for x in common_win: env.vs_srcs += ["platform/windows/" + str(x)] + if env["windows_subsystem"] == "gui": + for x in common_win_wrap: + env.vs_srcs += ["platform/windows/" + str(x)] if not os.getenv("VCINSTALLDIR"): if env["debug_symbols"] and env["separate_debug_symbols"]: env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw)) + if env["windows_subsystem"] == "gui": + env.AddPostAction(prog_wrap, run_in_subprocess(platform_windows_builders.make_debug_mingw)) diff --git a/platform/windows/console_wrapper_windows.cpp b/platform/windows/console_wrapper_windows.cpp new file mode 100644 index 0000000000..258176426b --- /dev/null +++ b/platform/windows/console_wrapper_windows.cpp @@ -0,0 +1,181 @@ +/*************************************************************************/ +/* console_wrapper_windows.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include <windows.h> + +#include <shlwapi.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 +#endif + +int main(int argc, char *argv[]) { + // Get executable name. + WCHAR exe_name[MAX_PATH] = {}; + if (!GetModuleFileNameW(nullptr, exe_name, MAX_PATH)) { + wprintf(L"GetModuleFileName failed, error %d\n", GetLastError()); + return -1; + } + + // Get product name from the resources and set console title. + DWORD ver_info_handle = 0; + DWORD ver_info_size = GetFileVersionInfoSizeW(exe_name, &ver_info_handle); + if (ver_info_size > 0) { + LPBYTE ver_info = (LPBYTE)malloc(ver_info_size); + if (ver_info) { + if (GetFileVersionInfoW(exe_name, ver_info_handle, ver_info_size, ver_info)) { + LPCWSTR text_ptr = nullptr; + UINT text_size = 0; + if (VerQueryValueW(ver_info, L"\\StringFileInfo\\040904b0\\ProductName", (void **)&text_ptr, &text_size) && (text_size > 0)) { + SetConsoleTitleW(text_ptr); + } + } + free(ver_info); + } + } + + // Enable virtual terminal sequences processing. + HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD out_mode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(stdout_handle, out_mode); + + // Find main executable name and check if it exist. + static PCWSTR exe_renames[] = { + L".console.exe", + L"_console.exe", + L" console.exe", + L"console.exe", + nullptr, + }; + + bool rename_found = false; + for (int i = 0; exe_renames[i]; i++) { + PWSTR c = StrRStrIW(exe_name, nullptr, exe_renames[i]); + if (c) { + CopyMemory(c, L".exe", sizeof(WCHAR) * 5); + rename_found = true; + break; + } + } + if (!rename_found) { + wprintf(L"Invalid wrapper executable name.\n"); + return -1; + } + + DWORD file_attrib = GetFileAttributesW(exe_name); + if (file_attrib == INVALID_FILE_ATTRIBUTES || (file_attrib & FILE_ATTRIBUTE_DIRECTORY)) { + wprintf(L"Main executable %ls not found.\n", exe_name); + return -1; + } + + // Create job to monitor process tree. + HANDLE job_handle = CreateJobObjectW(nullptr, nullptr); + if (!job_handle) { + wprintf(L"CreateJobObject failed, error %d\n", GetLastError()); + return -1; + } + + HANDLE io_port_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1); + if (!io_port_handle) { + wprintf(L"CreateIoCompletionPort failed, error %d\n", GetLastError()); + return -1; + } + + JOBOBJECT_ASSOCIATE_COMPLETION_PORT compl_port; + ZeroMemory(&compl_port, sizeof(compl_port)); + compl_port.CompletionKey = job_handle; + compl_port.CompletionPort = io_port_handle; + + if (!SetInformationJobObject(job_handle, JobObjectAssociateCompletionPortInformation, &compl_port, sizeof(compl_port))) { + wprintf(L"SetInformationJobObject(AssociateCompletionPortInformation) failed, error %d\n", GetLastError()); + return -1; + } + + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; + ZeroMemory(&jeli, sizeof(jeli)); + jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + + if (!SetInformationJobObject(job_handle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) { + wprintf(L"SetInformationJobObject(ExtendedLimitInformation) failed, error %d\n", GetLastError()); + return -1; + } + + // Start the main process. + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + STARTUPINFOW si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + WCHAR new_command_line[32767]; + _snwprintf_s(new_command_line, 32767, _TRUNCATE, L"%ls %ls", exe_name, PathGetArgsW(GetCommandLineW())); + + if (!CreateProcessW(nullptr, new_command_line, nullptr, nullptr, true, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)) { + wprintf(L"CreateProcess failed, error %d\n", GetLastError()); + return -1; + } + + if (!AssignProcessToJobObject(job_handle, pi.hProcess)) { + wprintf(L"AssignProcessToJobObject failed, error %d\n", GetLastError()); + return -1; + } + + ResumeThread(pi.hThread); + CloseHandle(pi.hThread); + + // Wait until main process and all of its children are finished. + DWORD completion_code = 0; + ULONG_PTR completion_key = 0; + LPOVERLAPPED overlapped = nullptr; + + while (GetQueuedCompletionStatus(io_port_handle, &completion_code, &completion_key, &overlapped, INFINITE)) { + if ((HANDLE)completion_key == job_handle && completion_code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) { + break; + } + } + + CloseHandle(job_handle); + CloseHandle(io_port_handle); + + // Get exit code of the main process. + DWORD exit_code = 0; + GetExitCodeProcess(pi.hProcess, &exit_code); + + CloseHandle(pi.hProcess); + + return exit_code; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + return main(0, nullptr); +} diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 5607eab342..705e83dace 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -4,6 +4,11 @@ import subprocess import sys from platform_methods import detect_arch +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from SCons import Environment + # To match other platforms STACK_SIZE = 8388608 @@ -173,17 +178,7 @@ def get_opts(): "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601", ), - BoolVariable( - "debug_symbols", - "Add debugging symbols to release/release_debug builds", - True, - ), EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("gui", "console")), - BoolVariable( - "separate_debug_symbols", - "Create a separate file containing debugging symbols", - False, - ), ( "msvc_version", "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.", @@ -191,7 +186,6 @@ def get_opts(): ), BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False), BoolVariable("use_llvm", "Use the LLVM compiler", False), - BoolVariable("use_thinlto", "Use ThinLTO", False), BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True), BoolVariable("use_asan", "Use address sanitizer (ASAN)", False), ] @@ -331,32 +325,11 @@ def setup_mingw(env): def configure_msvc(env, vcvars_msvc_config): """Configure env to work with MSVC""" - # Build type - if env["target"] == "release": - if env["optimize"] == "speed": # optimize for speed (default) - env.Append(CCFLAGS=["/O2"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - elif env["optimize"] == "size": # optimize for size - env.Append(CCFLAGS=["/O1"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) - - elif env["target"] == "release_debug": - if env["optimize"] == "speed": # optimize for speed (default) - env.Append(CCFLAGS=["/O2"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - elif env["optimize"] == "size": # optimize for size - env.Append(CCFLAGS=["/O1"]) - env.Append(LINKFLAGS=["/OPT:REF"]) - - elif env["target"] == "debug": - env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"]) - # Allow big objects. Only needed for debug, see MinGW branch for rationale. - env.Append(LINKFLAGS=["/DEBUG"]) + ## Build type - if env["debug_symbols"]: - env.AppendUnique(CCFLAGS=["/Zi", "/FS"]) - env.AppendUnique(LINKFLAGS=["/DEBUG"]) + # TODO: Re-evaluate the need for this / streamline with common config. + if env["target"] == "template_release": + env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) if env["windows_subsystem"] == "gui": env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"]) @@ -428,6 +401,7 @@ def configure_msvc(env, vcvars_msvc_config): "Avrt", "dwmapi", "dwrite", + "wbemuuid", ] if env["vulkan"]: @@ -449,7 +423,13 @@ def configure_msvc(env, vcvars_msvc_config): ## LTO - if env["use_lto"]: + if env["lto"] == "auto": # No LTO by default for MSVC, doesn't help. + env["lto"] = "none" + + if env["lto"] != "none": + if env["lto"] == "thin": + print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") + sys.exit(255) env.AppendUnique(CCFLAGS=["/GL"]) env.AppendUnique(ARFLAGS=["/LTCG"]) if env["progress"]: @@ -487,31 +467,10 @@ def configure_mingw(env): if env["use_llvm"] and not try_cmd("clang --version", env["mingw_prefix"], env["arch"]): env["use_llvm"] = False - if env["target"] == "release": + # TODO: Re-evaluate the need for this / streamline with common config. + if env["target"] == "template_release": env.Append(CCFLAGS=["-msse2"]) - - if env["optimize"] == "speed": # optimize for speed (default) - if env["arch"] == "x86_32": - env.Append(CCFLAGS=["-O2"]) - else: - env.Append(CCFLAGS=["-O3"]) - else: # optimize for size - env.Prepend(CCFLAGS=["-Os"]) - - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - - elif env["target"] == "release_debug": - env.Append(CCFLAGS=["-O2"]) - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - if env["optimize"] == "speed": # optimize for speed (default) - env.Append(CCFLAGS=["-O2"]) - else: # optimize for size - env.Prepend(CCFLAGS=["-Os"]) - - elif env["target"] == "debug": - env.Append(CCFLAGS=["-g3"]) + elif env.dev_build: # Allow big objects. It's supposed not to have drawbacks but seems to break # GCC LTO, so enabling for debug builds only (which are not built with LTO # and are the only ones with too big objects). @@ -562,17 +521,24 @@ def configure_mingw(env): if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]): env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib" - if env["use_lto"]: - if not env["use_llvm"] and env.GetOption("num_jobs") > 1: + ## LTO + + if env["lto"] == "auto": # Full LTO for production with MinGW. + env["lto"] = "full" + + if env["lto"] != "none": + if env["lto"] == "thin": + if not env["use_llvm"]: + print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") + sys.exit(255) + env.Append(CCFLAGS=["-flto=thin"]) + env.Append(LINKFLAGS=["-flto=thin"]) + elif not env["use_llvm"] and env.GetOption("num_jobs") > 1: env.Append(CCFLAGS=["-flto"]) env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))]) else: - if env["use_thinlto"]: - env.Append(CCFLAGS=["-flto=thin"]) - env.Append(LINKFLAGS=["-flto=thin"]) - else: - env.Append(CCFLAGS=["-flto"]) - env.Append(LINKFLAGS=["-flto"]) + env.Append(CCFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-flto"]) env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)]) @@ -612,15 +578,18 @@ def configure_mingw(env): "uuid", "dwmapi", "dwrite", + "wbemuuid", ] ) - env.Append(CPPDEFINES=["VULKAN_ENABLED"]) - if not env["use_volk"]: - env.Append(LIBS=["vulkan"]) + if env["vulkan"]: + env.Append(CPPDEFINES=["VULKAN_ENABLED"]) + if not env["use_volk"]: + env.Append(LIBS=["vulkan"]) - env.Append(CPPDEFINES=["GLES3_ENABLED"]) - env.Append(LIBS=["opengl32"]) + if env["opengl3"]: + env.Append(CPPDEFINES=["GLES3_ENABLED"]) + env.Append(LIBS=["opengl32"]) env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)]) @@ -628,7 +597,7 @@ def configure_mingw(env): env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")}) -def configure(env): +def configure(env: "Environment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64"] if env["arch"] not in supported_arches: diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index b4949de3f7..af80a07da9 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -104,7 +104,10 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) { // Mouse is grabbed (captured or confined). - WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } WindowData &wd = windows[window_id]; @@ -119,11 +122,15 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { ClientToScreen(wd.hWnd, &pos); SetCursorPos(pos.x, pos.y); SetCapture(wd.hWnd); + + _register_raw_input_devices(window_id); } } else { // Mouse is free to move around (not captured or confined). ReleaseCapture(); ClipCursor(nullptr); + + _register_raw_input_devices(INVALID_WINDOW_ID); } if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) { @@ -139,6 +146,37 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { } } +DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() const { + const List<WindowID>::Element *E = popup_list.back(); + if (E) { + return E->get(); + } + + return last_focused_window; +} + +void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) { + use_raw_input = true; + + RAWINPUTDEVICE rid[1] = {}; + rid[0].usUsagePage = 0x01; + rid[0].usUsage = 0x02; + rid[0].dwFlags = 0; + + if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) { + // Follow the defined window + rid[0].hwndTarget = windows[p_target_window].hWnd; + } else { + // Follow the keyboard focus + rid[0].hwndTarget = 0; + } + + if (RegisterRawInputDevices(rid, 1, sizeof(rid[0])) == FALSE) { + // Registration failed. + use_raw_input = false; + } +} + bool DisplayServerWindows::tts_is_speaking() const { ERR_FAIL_COND_V(!tts, false); return tts->is_speaking(); @@ -194,7 +232,9 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const { void DisplayServerWindows::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ - if (!windows.has(last_focused_window)) { + WindowID window_id = _get_focused_window_or_popup(); + + if (!windows.has(window_id)) { return; // No focused window? } @@ -205,7 +245,7 @@ void DisplayServerWindows::warp_mouse(const Point2i &p_position) { POINT p; p.x = p_position.x; p.y = p_position.y; - ClientToScreen(windows[last_focused_window].hWnd, &p); + ClientToScreen(windows[window_id].hWnd, &p); SetCursorPos(p.x, p.y); } @@ -521,20 +561,6 @@ float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const { return data.rate; } -bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const { -#ifndef _MSC_VER -#warning touchscreen not working -#endif - return false; -} - -void DisplayServerWindows::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) { -} - -DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(int p_screen) const { - return SCREEN_LANDSCAPE; -} - void DisplayServerWindows::screen_set_keep_on(bool p_enable) { if (keep_screen_on == p_enable) { return; @@ -645,7 +671,13 @@ void DisplayServerWindows::show_window(WindowID p_id) { _update_window_style(p_id); } - if (wd.no_focus || wd.is_popup) { + if (wd.maximized) { + ShowWindow(wd.hWnd, SW_SHOWMAXIMIZED); + SetForegroundWindow(wd.hWnd); // Slightly higher priority. + SetFocus(wd.hWnd); // Set keyboard focus. + } else if (wd.minimized) { + ShowWindow(wd.hWnd, SW_SHOWMINIMIZED); + } else if (wd.no_focus || wd.is_popup) { // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow ShowWindow(wd.hWnd, SW_SHOWNA); } else { @@ -709,9 +741,20 @@ int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, case WINDOW_HANDLE: { return (int64_t)windows[p_window].hWnd; } +#if defined(GLES3_ENABLED) case WINDOW_VIEW: { - return 0; // Not supported. + if (gl_manager) { + return (int64_t)gl_manager->get_hdc(p_window); + } + return 0; + } + case OPENGL_CONTEXT: { + if (gl_manager) { + return (int64_t)gl_manager->get_hglrc(p_window); + } + return 0; } +#endif default: { return 0; } @@ -886,7 +929,7 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - if (wd.fullscreen) { + if (wd.fullscreen || wd.maximized) { return; } @@ -1019,6 +1062,10 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; + if (wd.fullscreen || wd.maximized) { + return; + } + int w = p_size.width; int h = p_size.height; @@ -1036,10 +1083,6 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo } #endif - if (wd.fullscreen) { - return; - } - RECT rect; GetWindowRect(wd.hWnd, &rect); @@ -1315,7 +1358,8 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W if (p_enabled) { //enable per-pixel alpha - DWM_BLURBEHIND bb = { 0 }; + DWM_BLURBEHIND bb; + ZeroMemory(&bb, sizeof(bb)); HRGN hRgn = CreateRectRgn(0, 0, -1, -1); bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; bb.hRgnBlur = hRgn; @@ -1327,7 +1371,8 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W //disable per-pixel alpha wd.layered_window = false; - DWM_BLURBEHIND bb = { 0 }; + DWM_BLURBEHIND bb; + ZeroMemory(&bb, sizeof(bb)); HRGN hRgn = CreateRectRgn(0, 0, -1, -1); bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; bb.hRgnBlur = hRgn; @@ -1344,7 +1389,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened."); wd.is_popup = p_enabled; } break; - case WINDOW_FLAG_MAX: + default: break; } } @@ -1373,7 +1418,7 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window case WINDOW_FLAG_POPUP: { return wd.is_popup; } break; - case WINDOW_FLAG_MAX: + default: break; } @@ -1496,7 +1541,7 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { IDC_HELP }; - if (cursors[p_shape] != nullptr) { + if (cursors_cache.has(p_shape)) { SetCursor(cursors[p_shape]); } else { SetCursor(LoadCursor(hInstance, win_cursors[p_shape])); @@ -1509,55 +1554,6 @@ DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const { return cursor_shape; } -void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) { - // Get the system display DC. - HDC hDC = GetDC(nullptr); - - // Create helper DC. - HDC hMainDC = CreateCompatibleDC(hDC); - HDC hAndMaskDC = CreateCompatibleDC(hDC); - HDC hXorMaskDC = CreateCompatibleDC(hDC); - - // Get the dimensions of the source bitmap. - BITMAP bm; - GetObject(hSourceBitmap, sizeof(BITMAP), &bm); - - // Create the mask bitmaps. - hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color. - hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color. - - // Release the system display DC. - ReleaseDC(nullptr, hDC); - - // Select the bitmaps to helper DC. - HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap); - HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap); - HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap); - - // Assign the monochrome AND mask bitmap pixels so that the pixels of the source bitmap - // with 'clrTransparent' will be white pixels of the monochrome bitmap. - SetBkColor(hMainDC, clrTransparent); - BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY); - - // Assign the color XOR mask bitmap pixels so that the pixels of the source bitmap - // with 'clrTransparent' will be black and rest the pixels same as corresponding - // pixels of the source bitmap. - SetBkColor(hXorMaskDC, RGB(0, 0, 0)); - SetTextColor(hXorMaskDC, RGB(255, 255, 255)); - BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY); - BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND); - - // Deselect bitmaps from the helper DC. - SelectObject(hMainDC, hOldMainBitmap); - SelectObject(hAndMaskDC, hOldAndMaskBitmap); - SelectObject(hXorMaskDC, hOldXorMaskBitmap); - - // Delete the helper DC. - DeleteDC(hXorMaskDC); - DeleteDC(hAndMaskDC); - DeleteDC(hMainDC); -} - void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { _THREAD_SAFE_METHOD_ @@ -1610,8 +1606,26 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor UINT image_size = texture_size.width * texture_size.height; // Create the BITMAP with alpha channel. - COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size); - + COLORREF *buffer = nullptr; + + BITMAPV5HEADER bi; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = texture_size.width; + bi.bV5Height = -texture_size.height; + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00ff0000; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5BlueMask = 0x000000ff; + bi.bV5AlphaMask = 0xff000000; + + HDC dc = GetDC(nullptr); + HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0); + HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr); + + bool fully_transparent = true; for (UINT index = 0; index < image_size; index++) { int row_index = floor(index / texture_size.width) + atlas_rect.position.y; int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; @@ -1620,39 +1634,28 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor column_index = MIN(column_index, atlas_rect.size.width - 1); row_index = MIN(row_index, atlas_rect.size.height - 1); } + const Color &c = image->get_pixel(column_index, row_index); + fully_transparent = fully_transparent && (c.a == 0.f); - *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32(); - } - - // Using 4 channels, so 4 * 8 bits. - HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer); - COLORREF clrTransparent = -1; - - // Create the AND and XOR masks for the bitmap. - HBITMAP hAndMask = nullptr; - HBITMAP hXorMask = nullptr; - - GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask); - - if (nullptr == hAndMask || nullptr == hXorMask) { - memfree(buffer); - DeleteObject(bitmap); - return; + *(buffer + index) = c.to_argb32(); } // Finally, create the icon. - ICONINFO iconinfo; - iconinfo.fIcon = FALSE; - iconinfo.xHotspot = p_hotspot.x; - iconinfo.yHotspot = p_hotspot.y; - iconinfo.hbmMask = hAndMask; - iconinfo.hbmColor = hXorMask; - if (cursors[p_shape]) { DestroyIcon(cursors[p_shape]); } - cursors[p_shape] = CreateIconIndirect(&iconinfo); + if (fully_transparent) { + cursors[p_shape] = nullptr; + } else { + ICONINFO iconinfo; + iconinfo.fIcon = FALSE; + iconinfo.xHotspot = p_hotspot.x; + iconinfo.yHotspot = p_hotspot.y; + iconinfo.hbmMask = mask; + iconinfo.hbmColor = bitmap; + cursors[p_shape] = CreateIconIndirect(&iconinfo); + } Vector<Variant> params; params.push_back(p_cursor); @@ -1665,17 +1668,15 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor } } - DeleteObject(hAndMask); - DeleteObject(hXorMask); - - memfree(buffer); + DeleteObject(mask); DeleteObject(bitmap); + ReleaseDC(nullptr, dc); } else { // Reset to default system cursor. if (cursors[p_shape]) { DestroyIcon(cursors[p_shape]); - cursors[p_shape] = nullptr; } + cursors[p_shape] = nullptr; CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; @@ -1891,7 +1892,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { pos += sizeof(WORD); f->seek(pos); - icon_dir = (ICONDIR *)memrealloc(icon_dir, 3 * sizeof(WORD) + icon_dir->idCount * sizeof(ICONDIRENTRY)); + icon_dir = (ICONDIR *)memrealloc(icon_dir, sizeof(ICONDIR) - sizeof(ICONDIRENTRY) + icon_dir->idCount * sizeof(ICONDIRENTRY)); f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY)); int small_icon_index = -1; // Select 16x16 with largest color count. @@ -2419,14 +2420,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_SETTINGCHANGE: { if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) { - if (is_dark_mode_supported()) { + if (is_dark_mode_supported() && dark_title_available) { BOOL value = is_dark_mode(); ::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value)); } } } break; case WM_THEMECHANGED: { - if (is_dark_mode_supported()) { + if (is_dark_mode_supported() && dark_title_available) { BOOL value = is_dark_mode(); ::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value)); } @@ -2454,10 +2455,16 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return 0; // Jump back. } case WM_MOUSELEAVE: { - old_invalid = true; - windows[window_id].mouse_outside = true; + if (window_mouseover_id == window_id) { + old_invalid = true; + window_mouseover_id = INVALID_WINDOW_ID; - _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + } else if (window_mouseover_id != INVALID_WINDOW_ID) { + // This is reached during drag and drop, after dropping in a different window. + // Once-off notification, must call again. + track_mouse_leave_event(windows[window_mouseover_id].hWnd); + } } break; case WM_INPUT: { @@ -2527,7 +2534,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA old_y = coords.y; } - if (windows[window_id].window_has_focus && mm->get_relative() != Vector2()) { + if ((windows[window_id].window_has_focus || windows[window_id].is_popup) && mm->get_relative() != Vector2()) { Input::get_singleton()->parse_input_event(mm); } } @@ -2552,24 +2559,27 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { PACKET packet; if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) { - float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure); - windows[window_id].last_pressure = pressure; + POINT coords; + GetCursorPos(&coords); + ScreenToClient(windows[window_id].hWnd, &coords); + windows[window_id].last_pressure_update = 0; + float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure); double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180); double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180)); + bool inverted = packet.pkStatus & TPS_INVERT; - if (windows[window_id].tilt_supported) { - windows[window_id].last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)); - } else { - windows[window_id].last_tilt = Vector2(); - } + Vector2 tilt = (windows[window_id].tilt_supported) ? Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)) : Vector2(); - windows[window_id].last_pen_inverted = packet.pkStatus & TPS_INVERT; + // Nothing changed, ignore event. + if (!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) { + break; + } - POINT coords; - GetCursorPos(&coords); - ScreenToClient(windows[window_id].hWnd, &coords); + windows[window_id].last_pressure = pressure; + windows[window_id].last_tilt = tilt; + windows[window_id].last_pen_inverted = inverted; // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) { @@ -2685,17 +2695,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } - if (windows[window_id].mouse_outside) { + if (window_mouseover_id != window_id) { // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { + if (window_mouseover_id != INVALID_WINDOW_ID) { + // Leave previous window. + _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); } CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; cursor_set_shape(c); - windows[window_id].mouse_outside = false; + window_mouseover_id = window_id; // Once-off notification, must call again. track_mouse_leave_event(hWnd); @@ -2786,17 +2800,29 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } - if (windows[window_id].mouse_outside) { + DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position()); + if (!Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) { + // Don't consider the windowborder as part of the window. + over_id = INVALID_WINDOW_ID; + } + if (window_mouseover_id != over_id) { // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { - _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); + if (window_mouseover_id != INVALID_WINDOW_ID) { + // Leave previous window. + _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } + + if (over_id != INVALID_WINDOW_ID) { + _send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER); + } } CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; cursor_set_shape(c); - windows[window_id].mouse_outside = false; + window_mouseover_id = over_id; // Once-off notification, must call again. track_mouse_leave_event(hWnd); @@ -2807,9 +2833,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA break; } + DisplayServer::WindowID receiving_window_id = _get_focused_window_or_popup(); + if (receiving_window_id == INVALID_WINDOW_ID) { + receiving_window_id = window_id; + } Ref<InputEventMouseMotion> mm; mm.instantiate(); - mm->set_window_id(window_id); + mm->set_window_id(receiving_window_id); mm->set_ctrl_pressed((wParam & MK_CONTROL) != 0); mm->set_shift_pressed((wParam & MK_SHIFT) != 0); mm->set_alt_pressed(alt_mem); @@ -2866,9 +2896,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y))); old_x = mm->get_position().x; old_y = mm->get_position().y; - if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { - Input::get_singleton()->parse_input_event(mm); + + if (!windows[receiving_window_id].window_has_focus) { + // In case of unfocused Popups, adjust event position. + Point2i pos = mm->get_position() - window_get_position(receiving_window_id) + window_get_position(window_id); + mm->set_position(pos); + mm->set_global_position(pos); } + Input::get_singleton()->parse_input_event(mm); } break; case WM_LBUTTONDOWN: @@ -3094,7 +3129,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA rect_changed = true; } #if defined(VULKAN_ENABLED) - if (context_vulkan && window_created) { + if (context_vulkan && window.context_created) { // Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed. context_vulkan->window_resize(window_id, window.width, window.height); } @@ -3350,7 +3385,7 @@ void DisplayServerWindows::_process_key_events() { k->set_ctrl_pressed(ke.control); k->set_meta_pressed(ke.meta); k->set_pressed(true); - k->set_keycode((Key)KeyMappingWindows::get_keysym(ke.wParam)); + k->set_keycode((Key)KeyMappingWindows::get_keysym(MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK))); k->set_physical_keycode((Key)(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)))); k->set_unicode(unicode); if (k->get_unicode() && gr_mem) { @@ -3541,28 +3576,26 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wd.pre_fs_valid = true; } - if (is_dark_mode_supported()) { + if (is_dark_mode_supported() && dark_title_available) { BOOL value = is_dark_mode(); ::DwmSetWindowAttribute(wd.hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value)); } #ifdef VULKAN_ENABLED if (context_vulkan) { - if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) { + if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) { memdelete(context_vulkan); context_vulkan = nullptr; windows.erase(id); ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Vulkan Window."); } + wd.context_created = true; } #endif #ifdef GLES3_ENABLED if (gl_manager) { - Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top); - - // shut down OpenGL, to mirror behavior of Vulkan code - if (err != OK) { + if (gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) { memdelete(gl_manager); gl_manager = nullptr; windows.erase(id); @@ -3603,6 +3636,16 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wd.wtctx = 0; } + if (p_mode == WINDOW_MODE_MAXIMIZED) { + wd.maximized = true; + wd.minimized = false; + } + + if (p_mode == WINDOW_MODE_MINIMIZED) { + wd.maximized = false; + wd.minimized = true; + } + wd.last_pressure = 0; wd.last_pressure_update = 0; wd.last_tilt = Vector2(); @@ -3633,6 +3676,7 @@ WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr; WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr; // UXTheme API. +bool DisplayServerWindows::dark_title_available = false; bool DisplayServerWindows::ux_theme_available = false; IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr; ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr; @@ -3702,7 +3746,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) { } } -DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerWindows::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, Error &r_error) { drop_events = false; key_event_pos = 0; @@ -3725,7 +3769,21 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win // Enforce default keep screen on value. screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); - // Load UXTheme + // Load Windows version info. + OSVERSIONINFOW os_ver; + ZeroMemory(&os_ver, sizeof(OSVERSIONINFOW)); + os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + + HMODULE nt_lib = LoadLibraryW(L"ntdll.dll"); + if (nt_lib) { + RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(nt_lib, "RtlGetVersion"); + if (RtlGetVersion) { + RtlGetVersion(&os_ver); + } + FreeLibrary(nt_lib); + } + + // Load UXTheme. HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll"); if (ux_theme_lib) { IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136)); @@ -3735,6 +3793,9 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98)); ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference; + if (os_ver.dwBuildNumber >= 22000) { + dark_title_available = true; + } } // Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. @@ -3799,19 +3860,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win return; } - use_raw_input = true; - - RAWINPUTDEVICE Rid[1]; - - Rid[0].usUsagePage = 0x01; - Rid[0].usUsage = 0x02; - Rid[0].dwFlags = 0; - Rid[0].hwndTarget = 0; - - if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) { - // Registration failed. - use_raw_input = false; - } + _register_raw_input_devices(INVALID_WINDOW_ID); #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { @@ -3849,6 +3898,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); + if (p_position != nullptr) { + window_position = *p_position; + } + WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution)); ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); @@ -3912,12 +3965,24 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *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, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.\n" - "Please update your drivers or if you have a very old or integrated GPU upgrade it.", - "Unable to initialize Video driver"); + if (p_rendering_driver == "vulkan") { + String executable_name = OS::get_singleton()->get_executable_path().get_file(); + OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n" + "Please try updating your GPU driver or try using the OpenGL 3 driver.\n" + "You can enable the OpenGL 3 driver by starting the engine from the\n" + "command line with the command:\n'./" + + executable_name + " --rendering-driver opengl3'.\n " + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } else { + OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n" + "Please try updating your GPU driver.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } } return ds; } diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index dbc9821970..8ac0086d69 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -61,9 +61,9 @@ #include "gl_manager_windows.h" #endif -#include <fcntl.h> #include <io.h> #include <stdio.h> + #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <windowsx.h> @@ -157,6 +157,7 @@ typedef bool(WINAPI *ShouldAppsUseDarkModePtr)(); typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode); typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name); typedef int(WINAPI *GetImmersiveUserColorSetPreferencePtr)(bool bForceCheckRegistry, bool bSkipCheckOnFail); +typedef HRESULT(WINAPI *RtlGetVersionPtr)(OSVERSIONINFOW *lpVersionInformation); // Windows Ink API #ifndef POINTER_STRUCTURES @@ -285,6 +286,7 @@ class DisplayServerWindows : public DisplayServer { _THREAD_SAFE_CLASS_ // UXTheme API + static bool dark_title_available; static bool ux_theme_available; static IsDarkModeAllowedForAppPtr IsDarkModeAllowedForApp; static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode; @@ -309,8 +311,6 @@ class DisplayServerWindows : public DisplayServer { String tablet_driver; Vector<String> tablet_drivers; - void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap); - enum { KEY_EVENT_BUFFER_SIZE = 512 }; @@ -323,6 +323,8 @@ class DisplayServerWindows : public DisplayServer { LPARAM lParam; }; + WindowID window_mouseover_id = INVALID_WINDOW_ID; + KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE]; int key_event_pos; @@ -369,6 +371,7 @@ class DisplayServerWindows : public DisplayServer { bool no_focus = false; bool window_has_focus = false; bool exclusive = false; + bool context_created = false; // Used to transfer data between events using timer. WPARAM saved_wparam; @@ -397,7 +400,6 @@ class DisplayServerWindows : public DisplayServer { Size2 window_rect; Point2 last_pos; - bool mouse_outside = true; ObjectID instance_id; @@ -466,6 +468,8 @@ class DisplayServerWindows : public DisplayServer { void _update_real_mouse_position(WindowID p_window); void _set_mouse_mode_impl(MouseMode p_mode); + WindowID _get_focused_window_or_popup() const; + void _register_raw_input_devices(WindowID p_target_window); void _process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam); void _process_key_events(); @@ -514,10 +518,6 @@ public: virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; - virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; - - virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW) override; - virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual void screen_set_keep_on(bool p_enable) override; //disable screensaver virtual bool screen_is_kept_on() const override; @@ -624,11 +624,11 @@ public: virtual void set_context(Context p_context) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *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, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_windows_driver(); - DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + 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, Error &r_error); ~DisplayServerWindows(); }; diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 20320470b8..8f91756c02 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -34,6 +34,7 @@ #include "export_plugin.h" void register_windows_exporter() { +#ifndef ANDROID_ENABLED EDITOR_DEF("export/windows/rcedit", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); #ifdef WINDOWS_ENABLED @@ -46,6 +47,7 @@ void register_windows_exporter() { EDITOR_DEF("export/windows/wine", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE)); #endif +#endif Ref<EditorExportPlatformWindows> platform; platform.instantiate(); diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 016d201f2c..d15380ac7a 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -41,24 +41,13 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres } } -Error EditorExportPlatformWindows::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) { - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE); - if (f.is_null()) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), vformat(TTR("Could not open file \"%s\"."), p_path)); - return ERR_CANT_CREATE; - } - - f->store_line("@echo off"); - f->store_line("title \"" + p_app_name + "\""); - f->store_line("\"%~dp0" + p_pkg_name + "\" \"%*\""); - f->store_line("pause > nul"); - - return OK; -} - Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { if (p_preset->get("application/modify_resources")) { - _rcedit_add_data(p_preset, p_path); + _rcedit_add_data(p_preset, p_path, true); + String wrapper_path = p_path.get_basename() + ".console.exe"; + if (FileAccess::exists(wrapper_path)) { + _rcedit_add_data(p_preset, wrapper_path, false); + } } return OK; } @@ -71,6 +60,10 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags); if (p_preset->get("codesign/enable") && err == OK) { _code_sign(p_preset, pck_path); + String wrapper_path = p_path.get_basename() + ".console.exe"; + if (FileAccess::exists(wrapper_path)) { + _code_sign(p_preset, wrapper_path); + } } if (p_preset->get("binary_format/embed_pck") && err == OK) { @@ -81,25 +74,6 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> } } - String app_name; - if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - app_name = String(ProjectSettings::get_singleton()->get("application/config/name")); - } else { - app_name = "Unnamed"; - } - app_name = OS::get_singleton()->get_safe_dir_name(app_name); - - // Save console script. - if (err == OK) { - int con_scr = p_preset->get("debug/export_console_script"); - if ((con_scr == 1 && p_debug) || (con_scr == 2)) { - String scr_path = p_path.get_basename() + ".cmd"; - if (_export_debug_script(p_preset, app_name, p_path.get_file(), scr_path) != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), TTR("Could not create console script.")); - } - } - } - return err; } @@ -146,8 +120,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); } -Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); +Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_set_icon) { + String rcedit_path = EDITOR_GET("export/windows/rcedit"); if (rcedit_path != String() && !FileAccess::exists(rcedit_path)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Could not find rcedit executable at \"%s\"."), rcedit_path)); @@ -160,7 +134,7 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset #ifndef WINDOWS_ENABLED // On non-Windows we need WINE to run rcedit - String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); + String wine_path = EDITOR_GET("export/windows/wine"); if (!wine_path.is_empty() && !FileAccess::exists(wine_path)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Could not find wine executable at \"%s\"."), wine_path)); @@ -184,7 +158,7 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset List<String> args; args.push_back(p_path); - if (!icon_path.is_empty()) { + if (!icon_path.is_empty() && p_set_icon) { args.push_back("--set-icon"); args.push_back(icon_path); } @@ -248,7 +222,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p List<String> args; #ifdef WINDOWS_ENABLED - String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); + String signtool_path = EDITOR_GET("export/windows/signtool"); if (!signtool_path.is_empty() && !FileAccess::exists(signtool_path)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Could not find signtool executable at \"%s\"."), signtool_path)); return ERR_FILE_NOT_FOUND; @@ -257,7 +231,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p signtool_path = "signtool"; // try to run signtool from PATH } #else - String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); + String signtool_path = EDITOR_GET("export/windows/osslsigncode"); if (!signtool_path.is_empty() && !FileAccess::exists(signtool_path)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Could not find osslsigncode executable at \"%s\"."), signtool_path)); return ERR_FILE_NOT_FOUND; @@ -420,7 +394,7 @@ bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<Edito String err = ""; bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates); - String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); + String rcedit_path = EDITOR_GET("export/windows/rcedit"); if (p_preset->get("application/modify_resources") && rcedit_path.is_empty()) { err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > rcedit) to change the icon or app information data.") + "\n"; } diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index f85331c898..ec3b60aa76 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -38,9 +38,8 @@ #include "platform/windows/logo.gen.h" class EditorExportPlatformWindows : public EditorExportPlatformPC { - Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path); + Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_set_icon); Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); - Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path); public: virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp index d509ff8c51..900bca8258 100644 --- a/platform/windows/gl_manager_windows.cpp +++ b/platform/windows/gl_manager_windows.cpp @@ -289,12 +289,7 @@ void GLManager_Windows::make_current() { } void GLManager_Windows::swap_buffers() { - // on other platforms, OpenGL swaps buffers for all windows (on all displays, really?) - // Windows swaps buffers on a per-window basis - // REVISIT: this could be structurally bad, should we have "dirty" flags then? - for (KeyValue<DisplayServer::WindowID, GLWindow> &entry : _windows) { - SwapBuffers(entry.value.hDC); - } + SwapBuffers(_current_window->hDC); } Error GLManager_Windows::initialize() { @@ -344,6 +339,16 @@ bool GLManager_Windows::is_using_vsync() const { return use_vsync; } +HDC GLManager_Windows::get_hdc(DisplayServer::WindowID p_window_id) { + return get_window(p_window_id).hDC; +} + +HGLRC GLManager_Windows::get_hglrc(DisplayServer::WindowID p_window_id) { + const GLWindow &win = get_window(p_window_id); + const GLDisplay &disp = get_display(win.gldisplay_id); + return disp.hRC; +} + GLManager_Windows::GLManager_Windows(ContextType p_context_type) { context_type = p_context_type; diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows.h index 5e43a3de2a..c6d5f9f855 100644 --- a/platform/windows/gl_manager_windows.h +++ b/platform/windows/gl_manager_windows.h @@ -113,6 +113,9 @@ public: void set_use_vsync(bool p_use); bool is_using_vsync() const; + HDC get_hdc(DisplayServer::WindowID p_window_id); + HGLRC get_hglrc(DisplayServer::WindowID p_window_id); + GLManager_Windows(ContextType p_context_type); ~GLManager_Windows(); }; diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis index cdd1c14978..36b0919185 100644 --- a/platform/windows/godot.natvis +++ b/platform/windows/godot.natvis @@ -32,6 +32,38 @@ </Expand> </Type> + <Type Name="HashMap<*,*>"> + <Expand> + <Item Name="[size]">num_elements</Item> + <LinkedListItems> + <Size>num_elements</Size> + <HeadPointer>head_element</HeadPointer> + <NextPointer>next</NextPointer> + <ValueNode>data</ValueNode> + </LinkedListItems> + </Expand> + </Type> + + <Type Name="VMap<*,*>"> + <Expand> + <Item Condition="_cowdata._ptr" Name="[size]">*(reinterpret_cast<int*>(_cowdata._ptr) - 1)</Item> + <ArrayItems Condition="_cowdata._ptr"> + <Size>*(reinterpret_cast<int*>(_cowdata._ptr) - 1)</Size> + <ValuePointer>reinterpret_cast<VMap<$T1,$T2>::Pair*>(_cowdata._ptr)</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="VMap<Callable,*>::Pair"> + <DisplayString Condition="dynamic_cast<CallableCustomMethodPointerBase*>(key.custom)">{dynamic_cast<CallableCustomMethodPointerBase*>(key.custom)->text}</DisplayString> + </Type> + + <!-- requires PR 64364 + <Type Name="GDScriptThreadContext"> + <DisplayString Condition="_is_main == true">main thread {_debug_thread_id}</DisplayString> + </Type> + --> + <Type Name="Variant"> <DisplayString Condition="type == Variant::NIL">nil</DisplayString> <DisplayString Condition="type == Variant::BOOL">{_data._bool}</DisplayString> @@ -55,15 +87,17 @@ <DisplayString Condition="type == Variant::OBJECT">{*(Object *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{*(PackedByteArray *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{*(PackedInt32Array *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<unsigned char>*>(_data.packed_array)->array}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<int>*>(_data.packed_array)->array}</DisplayString> + <!-- broken, will show incorrect data <DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*(PackedInt64Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{*(PackedFloat32Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{*(PackedFloat64Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{*(PackedStringArray *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{*(PackedVector2Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{*(PackedVector3Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{*(PackedColorArray *)_data._mem}</DisplayString> + --> + <DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<float>*>(_data.packed_array)->array}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<double>*>(_data.packed_array)->array}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<String>*>(_data.packed_array)->array}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Vector2>*>(_data.packed_array)->array}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Vector3>*>(_data.packed_array)->array}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Color>*>(_data.packed_array)->array}</DisplayString> <StringView Condition="type == Variant::STRING && ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</StringView> @@ -87,7 +121,7 @@ <Item Name="[value]" Condition="type == Variant::OBJECT">*(Object *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::ARRAY">*(Array *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">*(PackedByteArray *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<unsigned char>*>(_data.packed_array)->array</Item> <Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">*(PackedInt32Array *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*(PackedInt64Array *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">*(PackedFloat32Array *)_data._mem</Item> @@ -105,6 +139,14 @@ <StringView Condition="_cowdata._ptr != 0">_cowdata._ptr,s32</StringView> </Type> + <Type Name="godot::String"> + <DisplayString>{*reinterpret_cast<void**>(opaque),s32}</DisplayString> + <Expand> + <Item Name="opaque_ptr">*reinterpret_cast<void**>(opaque)</Item> + <Item Name="string">*reinterpret_cast<void**>(opaque),s32</Item> + </Expand> + </Type> + <Type Name="StringName"> <DisplayString Condition="_data && _data->cname">{_data->cname}</DisplayString> <DisplayString Condition="_data && !_data->cname">{_data->name,s32}</DisplayString> @@ -113,6 +155,22 @@ <StringView Condition="_data && !_data->cname">_data->name,s32</StringView> </Type> + <!-- can't cast the opaque to ::StringName because Natvis does not support global namespace specifier? --> + <Type Name="godot::StringName"> + <DisplayString Condition="(*reinterpret_cast<const char***>(opaque))[1]">{(*reinterpret_cast<const char***>(opaque))[1],s8}</DisplayString> + <DisplayString Condition="!(*reinterpret_cast<const char***>(opaque))[1]">{(*reinterpret_cast<const char***>(opaque))[2],s32}</DisplayString> + <Expand> + <Item Name="opaque_ptr">*reinterpret_cast<void**>(opaque)</Item> + <Item Name="&cname">(*reinterpret_cast<const char***>(opaque))+1</Item> + <Item Name="cname">(*reinterpret_cast<const char***>(opaque))[1],s8</Item> + </Expand> + </Type> + + <Type Name="Object::SignalData"> + <DisplayString Condition="user.name._cowdata._ptr">"{user.name}" {slot_map}</DisplayString> + <DisplayString Condition="!user.name._cowdata._ptr">"{slot_map}</DisplayString> + </Type> + <Type Name="Vector2"> <DisplayString>{{{x},{y}}}</DisplayString> <Expand> @@ -149,12 +207,4 @@ <Item Name="alpha">a</Item> </Expand> </Type> - - <Type Name="Node" Inheritable="false"> - <Expand> - <Item Name="Object">(Object*)this</Item> - <Item Name="class_name">(StringName*)(((char*)this) + sizeof(Object))</Item> - <Item Name="data">(Node::Data*)(((char*)this) + sizeof(Object) + sizeof(StringName))</Item> - </Expand> - </Type> </AutoVisualizer> diff --git a/platform/windows/godot_res_wrap.rc b/platform/windows/godot_res_wrap.rc new file mode 100644 index 0000000000..ed93bb1ec3 --- /dev/null +++ b/platform/windows/godot_res_wrap.rc @@ -0,0 +1,31 @@ +#include "core/version.h" +#ifndef _STR +#define _STR(m_x) #m_x +#define _MKSTR(m_x) _STR(m_x) +#endif + +1 VERSIONINFO +FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0 +PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0 +FILEOS 4 +FILETYPE 1 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Godot Engine" + VALUE "FileDescription", VERSION_NAME " (Console)" + VALUE "FileVersion", VERSION_NUMBER + VALUE "ProductName", VERSION_NAME " (Console)" + VALUE "Licence", "MIT" + VALUE "LegalCopyright", "Copyright (c) 2007-" _MKSTR(VERSION_YEAR) " Juan Linietsky, Ariel Manzur and contributors" + VALUE "Info", "https://godotengine.org" + VALUE "ProductVersion", VERSION_FULL_BUILD + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp index 72920d2816..13602f7cbc 100644 --- a/platform/windows/godot_windows.cpp +++ b/platform/windows/godot_windows.cpp @@ -87,7 +87,8 @@ CommandLineToArgvA( i = 0; j = 0; - while ((a = CmdLine[i])) { + a = CmdLine[i]; + while (a) { if (in_QM) { if (a == '\"') { in_QM = FALSE; @@ -130,6 +131,7 @@ CommandLineToArgvA( } } i++; + a = CmdLine[i]; } _argv[j] = '\0'; argv[argc] = nullptr; diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index d039fd13a7..2b5c8cad48 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -167,7 +167,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { const GUID &guid = instance->guidProduct; char uid[128]; - ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognised."); + ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognized."); WORD type = BSWAP16(0x03); WORD vendor = BSWAP16(LOWORD(guid.Data1)); WORD product = BSWAP16(HIWORD(guid.Data1)); diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h index d239471a5c..56a9f3e9c9 100644 --- a/platform/windows/joypad_windows.h +++ b/platform/windows/joypad_windows.h @@ -85,6 +85,8 @@ private: last_pad = -1; attached = false; confirmed = false; + di_joy = nullptr; + guid = {}; for (int i = 0; i < MAX_JOY_BUTTONS; i++) { last_buttons[i] = false; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 403d53ae53..08897bb190 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -53,6 +53,7 @@ #include <process.h> #include <regstr.h> #include <shlobj.h> +#include <wbemcli.h> extern "C" { __declspec(dllexport) DWORD NvOptimusEnablement = 1; @@ -102,8 +103,6 @@ void RedirectIOToConsole() { RedirectStream("CONIN$", "r", stdin, STD_INPUT_HANDLE); RedirectStream("CONOUT$", "w", stdout, STD_OUTPUT_HANDLE); RedirectStream("CONOUT$", "w", stderr, STD_ERROR_HANDLE); - - printf("\n"); // Make sure our output is starting from the new line. } } @@ -290,7 +289,123 @@ String OS_Windows::get_name() const { return "Windows"; } -OS::Date OS_Windows::get_date(bool p_utc) const { +String OS_Windows::get_distribution_name() const { + return get_name(); +} + +String OS_Windows::get_version() const { + typedef LONG NTSTATUS; + typedef NTSTATUS(WINAPI * RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + RtlGetVersionPtr version_ptr = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlGetVersion"); + if (version_ptr != nullptr) { + RTL_OSVERSIONINFOW fow; + ZeroMemory(&fow, sizeof(fow)); + fow.dwOSVersionInfoSize = sizeof(fow); + if (version_ptr(&fow) == 0x00000000) { + return vformat("%d.%d.%d", (int64_t)fow.dwMajorVersion, (int64_t)fow.dwMinorVersion, (int64_t)fow.dwBuildNumber); + } + } + return ""; +} + +Vector<String> OS_Windows::get_video_adapter_driver_info() const { + if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) { + return Vector<String>(); + } + + REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID + REFIID uuid = IID_IWbemLocator; // Interface UUID + IWbemLocator *wbemLocator = NULL; // to get the services + IWbemServices *wbemServices = NULL; // to get the class + IEnumWbemClassObject *iter = NULL; + IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc. + static String driver_name; + static String driver_version; + + const String device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); + if (device_name.is_empty()) { + return Vector<String>(); + } + + CoInitialize(nullptr); + + HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator); + if (hr != S_OK) { + return Vector<String>(); + } + BSTR resource_name = SysAllocString(L"root\\CIMV2"); + hr = wbemLocator->ConnectServer(resource_name, NULL, NULL, NULL, 0, NULL, NULL, &wbemServices); + SysFreeString(resource_name); + + SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices` + if (hr != S_OK) { + SAFE_RELEASE(wbemServices) + return Vector<String>(); + } + + const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", device_name); + BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data()); + BSTR query_lang = SysAllocString(L"WQL"); + hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &iter); + SysFreeString(query_lang); + SysFreeString(query); + if (hr == S_OK) { + ULONG resultCount; + hr = iter->Next(5000, 1, pnpSDriverObject, &resultCount); // Get exactly 1. Wait max 5 seconds. + + if (hr == S_OK && resultCount > 0) { + VARIANT dn; + VariantInit(&dn); + + BSTR object_name = SysAllocString(L"DriverName"); + hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL); + SysFreeString(object_name); + if (hr == S_OK) { + String d_name = String(V_BSTR(&dn)); + if (d_name.is_empty()) { + object_name = SysAllocString(L"DriverProviderName"); + hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL); + SysFreeString(object_name); + if (hr == S_OK) { + driver_name = String(V_BSTR(&dn)); + } + } else { + driver_name = d_name; + } + } else { + object_name = SysAllocString(L"DriverProviderName"); + hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL); + SysFreeString(object_name); + if (hr == S_OK) { + driver_name = String(V_BSTR(&dn)); + } + } + + VARIANT dv; + VariantInit(&dv); + object_name = SysAllocString(L"DriverVersion"); + hr = pnpSDriverObject[0]->Get(object_name, 0, &dv, NULL, NULL); + SysFreeString(object_name); + if (hr == S_OK) { + driver_version = String(V_BSTR(&dv)); + } + for (ULONG i = 0; i < resultCount; i++) { + SAFE_RELEASE(pnpSDriverObject[i]) + } + } + } + + SAFE_RELEASE(wbemServices) + SAFE_RELEASE(iter) + + Vector<String> info; + info.push_back(driver_name); + info.push_back(driver_version); + + return info; +} + +OS::DateTime OS_Windows::get_datetime(bool p_utc) const { SYSTEMTIME systemtime; if (p_utc) { GetSystemTime(&systemtime); @@ -305,28 +420,16 @@ OS::Date OS_Windows::get_date(bool p_utc) const { daylight = true; } - Date date; - date.day = systemtime.wDay; - date.month = Month(systemtime.wMonth); - date.weekday = Weekday(systemtime.wDayOfWeek); - date.year = systemtime.wYear; - date.dst = daylight; - return date; -} - -OS::Time OS_Windows::get_time(bool p_utc) const { - SYSTEMTIME systemtime; - if (p_utc) { - GetSystemTime(&systemtime); - } else { - GetLocalTime(&systemtime); - } - - Time time; - time.hour = systemtime.wHour; - time.minute = systemtime.wMinute; - time.second = systemtime.wSecond; - return time; + DateTime dt; + dt.year = systemtime.wYear; + dt.month = Month(systemtime.wMonth); + dt.day = systemtime.wDay; + dt.weekday = Weekday(systemtime.wDayOfWeek); + dt.hour = systemtime.wHour; + dt.minute = systemtime.wMinute; + dt.second = systemtime.wSecond; + dt.dst = daylight; + return dt; } OS::TimeZoneInfo OS_Windows::get_time_zone_info() const { @@ -864,17 +967,6 @@ BOOL is_wow64() { return wow64; } -int OS_Windows::get_processor_count() const { - SYSTEM_INFO sysinfo; - if (is_wow64()) { - GetNativeSystemInfo(&sysinfo); - } else { - GetSystemInfo(&sysinfo); - } - - return sysinfo.dwNumberOfProcessors; -} - String OS_Windows::get_processor_name() const { const String id = "Hardware\\Description\\System\\CentralProcessor\\0"; @@ -1063,11 +1155,11 @@ String OS_Windows::get_system_dir(SystemDir p_dir, bool p_shared_storage) const } String OS_Windows::get_user_data_dir() const { - String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); + String appname = get_safe_dir_name(GLOBAL_GET("application/config/name")); if (!appname.is_empty()) { - bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir"); + bool use_custom_dir = GLOBAL_GET("application/config/use_custom_user_dir"); if (use_custom_dir) { - String custom_dir = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/custom_user_dir_name"), true); + String custom_dir = get_safe_dir_name(GLOBAL_GET("application/config/custom_user_dir_name"), true); if (custom_dir.is_empty()) { custom_dir = appname; } @@ -1087,7 +1179,14 @@ String OS_Windows::get_unique_id() const { } bool OS_Windows::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; + if (p_feature == "system_fonts") { + return true; + } + if (p_feature == "pc") { + return true; + } + + return false; } void OS_Windows::disable_crash_handler() { @@ -1127,15 +1226,7 @@ Error OS_Windows::move_to_trash(const String &p_path) { } OS_Windows::OS_Windows(HINSTANCE _hInstance) { - ticks_per_second = 0; - ticks_start = 0; - main_loop = nullptr; - process_map = nullptr; - hInstance = _hInstance; -#ifdef STDOUT_FILE - stdo = fopen("stdout.txt", "wb"); -#endif #ifdef WASAPI_ENABLED AudioDriverManager::add_driver(&driver_wasapi); @@ -1146,13 +1237,22 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { DisplayServerWindows::register_windows_driver(); + // Enable ANSI escape code support on Windows 10 v1607 (Anniversary Update) and later. + // This lets the engine and projects use ANSI escape codes to color text just like on macOS and Linux. + // + // NOTE: The engine does not use ANSI escape codes to color error/warning messages; it uses Windows API calls instead. + // Therefore, error/warning messages are still colored on Windows versions older than 10. + HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD outMode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(stdoutHandle, outMode)) { + // Windows 8.1 or below, or Windows 10 prior to Anniversary Update. + print_verbose("Can't set the ENABLE_VIRTUAL_TERMINAL_PROCESSING Windows console mode. `print_rich()` will not work as expected."); + } + Vector<Logger *> loggers; loggers.push_back(memnew(WindowsTerminalLogger)); _set_logger(memnew(CompositeLogger(loggers))); } OS_Windows::~OS_Windows() { -#ifdef STDOUT_FILE - fclose(stdo); -#endif } diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 3e054c068c..6f89be699a 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -40,6 +40,7 @@ #include "drivers/winmidi/midi_driver_winmidi.h" #include "key_mapping_windows.h" #include "servers/audio_server.h" + #ifdef XAUDIO2_ENABLED #include "drivers/xaudio2/audio_driver_xaudio2.h" #endif @@ -49,10 +50,10 @@ #include "platform/windows/vulkan_context_win.h" #endif -#include <fcntl.h> #include <io.h> #include <shellapi.h> #include <stdio.h> + #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <windowsx.h> @@ -62,6 +63,10 @@ #define WINDOWS_DEBUG_OUTPUT_ENABLED #endif +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 +#endif + template <class T> class ComAutoreleaseRef { public: @@ -83,13 +88,10 @@ public: }; class JoypadWindows; -class OS_Windows : public OS { -#ifdef STDOUT_FILE - FILE *stdo = nullptr; -#endif - uint64_t ticks_start; - uint64_t ticks_per_second; +class OS_Windows : public OS { + uint64_t ticks_start = 0; + uint64_t ticks_per_second = 0; HINSTANCE hInstance; MainLoop *main_loop = nullptr; @@ -129,7 +131,7 @@ protected: STARTUPINFO si; PROCESS_INFORMATION pi; }; - HashMap<ProcessID, ProcessInfo> *process_map; + HashMap<ProcessID, ProcessInfo> *process_map = nullptr; public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; @@ -143,11 +145,14 @@ public: virtual MainLoop *get_main_loop() const override; virtual String get_name() const override; + virtual String get_distribution_name() const override; + virtual String get_version() const override; + + virtual Vector<String> get_video_adapter_driver_info() const override; virtual void initialize_joypads() override {} - virtual Date get_date(bool p_utc) const override; - virtual Time get_time(bool p_utc) const override; + virtual DateTime get_datetime(bool p_utc) const override; virtual TimeZoneInfo get_time_zone_info() const override; virtual double get_unix_time() const override; @@ -173,7 +178,6 @@ public: virtual String get_locale() const override; - virtual int get_processor_count() const override; virtual String get_processor_name() const override; virtual uint64_t get_embedded_pck_offset() const override; diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h index 8e80f8cacb..7f0042d76f 100644 --- a/platform/windows/platform_config.h +++ b/platform/windows/platform_config.h @@ -30,4 +30,4 @@ #include <malloc.h> -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define OPENGL_INCLUDE_H "thirdparty/glad/glad/gl.h" diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp index e62c6c1dc8..ff9318e47e 100644 --- a/platform/windows/vulkan_context_win.cpp +++ b/platform/windows/vulkan_context_win.cpp @@ -41,7 +41,7 @@ const char *VulkanContextWindows::_get_platform_surface_extension() const { return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; } -int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) { +Error VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) { VkWin32SurfaceCreateInfoKHR createInfo; createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; createInfo.pNext = nullptr; @@ -50,7 +50,7 @@ int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, Dis createInfo.hwnd = p_window; VkSurfaceKHR surface; VkResult err = vkCreateWin32SurfaceKHR(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, -1); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); } diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h index d5950a129a..9dedcabb2b 100644 --- a/platform/windows/vulkan_context_win.h +++ b/platform/windows/vulkan_context_win.h @@ -31,6 +31,8 @@ #ifndef VULKAN_CONTEXT_WIN_H #define VULKAN_CONTEXT_WIN_H +#ifdef VULKAN_ENABLED + #include "drivers/vulkan/vulkan_context.h" #define WIN32_LEAN_AND_MEAN @@ -40,10 +42,12 @@ class VulkanContextWindows : public VulkanContext { virtual const char *_get_platform_surface_extension() const; public: - int window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height); + Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height); VulkanContextWindows(); ~VulkanContextWindows(); }; +#endif // VULKAN_ENABLED + #endif // VULKAN_CONTEXT_WIN_H |