diff options
Diffstat (limited to 'platform/windows')
25 files changed, 1521 insertions, 1040 deletions
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp index 1b4dae207f..3b2c6fe9f6 100644 --- a/platform/windows/crash_handler_windows.cpp +++ b/platform/windows/crash_handler_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -33,7 +33,6 @@ #include "core/config/project_settings.h" #include "core/os/os.h" #include "core/version.h" -#include "core/version_hash.gen.h" #include "main/main.h" #ifdef CRASH_HANDLER_EXCEPTION @@ -81,8 +80,9 @@ public: std::string name() { return std::string(sym->Name); } std::string undecorated_name() { - if (*sym->Name == '\0') + if (*sym->Name == '\0') { return "<couldn't map PC to fn name>"; + } std::vector<char> und_name(max_name_len); UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE); return std::string(&und_name[0], strlen(&und_name[0])); @@ -132,12 +132,14 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { fprintf(stderr, "\n================================================================\n"); fprintf(stderr, "%s: Program crashed\n", __FUNCTION__); - if (OS::get_singleton()->get_main_loop()) + if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); + } // Load the symbols: - if (!SymInitialize(process, nullptr, false)) + if (!SymInitialize(process, nullptr, false)) { return EXCEPTION_CONTINUE_SEARCH; + } SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded); @@ -179,10 +181,10 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { } // Print the engine version just before, so that people are reminded to include the version in backtrace reports. - if (String(VERSION_HASH).length() != 0) { - fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n"); + if (String(VERSION_HASH).is_empty()) { + fprintf(stderr, "Engine version: %s\n", VERSION_FULL_NAME); } else { - fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n"); + fprintf(stderr, "Engine version: %s (%s)\n", VERSION_FULL_NAME, VERSION_HASH); } fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data()); @@ -194,18 +196,21 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { if (frame.AddrPC.Offset != 0) { std::string fnName = symbol(process, frame.AddrPC.Offset).undecorated_name(); - if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line)) + if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line)) { fprintf(stderr, "[%d] %s (%s:%d)\n", n, fnName.c_str(), line.FileName, line.LineNumber); - else + } else { fprintf(stderr, "[%d] %s\n", n, fnName.c_str()); - } else + } + } else { fprintf(stderr, "[%d] ???\n", n); + } n++; } - if (!StackWalk64(image_type, process, hThread, &frame, context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) + if (!StackWalk64(image_type, process, hThread, &frame, context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) { break; + } } while (frame.AddrReturn.Offset != 0 && n < 256); fprintf(stderr, "-- END OF BACKTRACE --\n"); @@ -226,8 +231,9 @@ CrashHandler::~CrashHandler() { } void CrashHandler::disable() { - if (disabled) + if (disabled) { return; + } disabled = true; } diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h index 5cdc6d3e05..ec8de3ffab 100644 --- a/platform/windows/crash_handler_windows.h +++ b/platform/windows/crash_handler_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 22fbbeb74f..249a0d2e79 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -65,7 +65,7 @@ def get_opts(): # Vista support dropped after EOL due to GH-10243 ("target_win_version", "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601"), BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), - EnumVariable("windows_subsystem", "Windows subsystem", "default", ("default", "console", "gui")), + 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.", None), BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False), @@ -178,15 +178,6 @@ def configure_msvc(env, manual_msvc_config): # Build type - if env["tests"]: - env["windows_subsystem"] = "console" - elif env["windows_subsystem"] == "default": - # Default means we use console for debug, gui for release. - if "debug" in env["target"]: - env["windows_subsystem"] = "console" - else: - env["windows_subsystem"] = "gui" - if env["target"] == "release": if env["optimize"] == "speed": # optimize for speed (default) env.Append(CCFLAGS=["/O2"]) @@ -206,6 +197,8 @@ def configure_msvc(env, manual_msvc_config): elif env["target"] == "debug": env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"]) + # Allow big objects. Only needed for debug, see MinGW branch for rationale. + env.AppendUnique(CCFLAGS=["/bigobj"]) env.Append(LINKFLAGS=["/DEBUG"]) if env["debug_symbols"]: @@ -226,9 +219,9 @@ def configure_msvc(env, manual_msvc_config): env.AppendUnique(CCFLAGS=["/MD"]) env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"]) - # Force to use Unicode encoding - env.AppendUnique(CCFLAGS=["/utf-8"]) + env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding. env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++ + if manual_msvc_config: # should be automatic if SCons found it if os.getenv("WindowsSdkDir") is not None: env.Prepend(CPPPATH=[os.getenv("WindowsSdkDir") + "/Include"]) @@ -306,7 +299,7 @@ def configure_msvc(env, manual_msvc_config): # Sanitizers if env["use_asan"]: - env.extra_suffix += ".s" + env.extra_suffix += ".san" env.Append(LINKFLAGS=["/INFERASANLIBS"]) env.Append(CCFLAGS=["/fsanitize=address"]) @@ -324,15 +317,6 @@ def configure_mingw(env): ## Build type - if env["tests"]: - env["windows_subsystem"] = "console" - elif env["windows_subsystem"] == "default": - # Default means we use console for debug, gui for release. - if "debug" in env["target"]: - env["windows_subsystem"] = "console" - else: - env["windows_subsystem"] = "gui" - if env["target"] == "release": env.Append(CCFLAGS=["-msse2"]) @@ -358,6 +342,10 @@ def configure_mingw(env): elif env["target"] == "debug": env.Append(CCFLAGS=["-g3"]) + # 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). + env.Append(CCFLAGS=["-Wa,-mbig-obj"]) if env["windows_subsystem"] == "gui": env.Append(LINKFLAGS=["-Wl,--subsystem,windows"]) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e2c9951692..736dc7d383 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -42,6 +42,11 @@ #include "drivers/gles3/rasterizer_gles3.h" #endif +#if defined(__GNUC__) +// Workaround GCC warning from -Wcast-function-type. +#define GetProcAddress (void *)GetProcAddress +#endif + static String format_error_message(DWORD id) { LPWSTR messageBuffer = nullptr; size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, @@ -54,6 +59,15 @@ static String format_error_message(DWORD id) { return msg; } +static void track_mouse_leave_event(HWND hWnd) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + tme.dwHoverTime = HOVER_DEFAULT; + TrackMouseEvent(&tme); +} + bool DisplayServerWindows::has_feature(Feature p_feature) const { switch (p_feature) { case FEATURE_SUBWINDOWS: @@ -63,7 +77,6 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const { case FEATURE_CLIPBOARD: case FEATURE_CURSOR_SHAPE: case FEATURE_CUSTOM_CURSOR_SHAPE: - case FEATURE_CONSOLE_WINDOW: case FEATURE_IME: case FEATURE_WINDOW_TRANSPARENCY: case FEATURE_HIDPI: @@ -84,7 +97,10 @@ String DisplayServerWindows::get_name() const { 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). - WindowData &wd = windows[MAIN_WINDOW_ID]; + + WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + + WindowData &wd = windows[window_id]; RECT clipRect; GetClientRect(wd.hWnd, &clipRect); @@ -134,7 +150,7 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const { return mouse_mode; } -void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { +void DisplayServerWindows::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { @@ -142,12 +158,12 @@ void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { } if (mouse_mode == MOUSE_MODE_CAPTURED) { - old_x = p_to.x; - old_y = p_to.y; + old_x = p_position.x; + old_y = p_position.y; } else { POINT p; - p.x = p_to.x; - p.y = p_to.y; + p.x = p_position.x; + p.y = p_position.y; ClientToScreen(windows[last_focused_window].hWnd, &p); SetCursorPos(p.x, p.y); @@ -215,7 +231,7 @@ String DisplayServerWindows::clipboard_get() const { String ret; if (!OpenClipboard(windows[last_focused_window].hWnd)) { ERR_FAIL_V_MSG("", "Unable to open clipboard."); - }; + } if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); @@ -224,8 +240,8 @@ String DisplayServerWindows::clipboard_get() const { if (ptr != nullptr) { ret = String::utf16((const char16_t *)ptr); GlobalUnlock(mem); - }; - }; + } + } } else if (IsClipboardFormatAvailable(CF_TEXT)) { HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); @@ -234,9 +250,9 @@ String DisplayServerWindows::clipboard_get() const { if (ptr != nullptr) { ret.parse_utf8((const char *)ptr); GlobalUnlock(mem); - }; - }; - }; + } + } + } CloseClipboard(); @@ -310,6 +326,12 @@ typedef struct { Rect2i rect; } EnumRectData; +typedef struct { + int count; + int screen; + float rate; +} EnumRefreshRateData; + static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { EnumSizeData *data = (EnumSizeData *)dwData; if (data->count == data->screen) { @@ -347,6 +369,26 @@ static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonito return TRUE; } +static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + EnumRefreshRateData *data = (EnumRefreshRateData *)dwData; + if (data->count == data->screen) { + MONITORINFOEXW minfo; + memset(&minfo, 0, sizeof(minfo)); + minfo.cbSize = sizeof(minfo); + GetMonitorInfoW(hMonitor, &minfo); + + DEVMODEW dm; + memset(&dm, 0, sizeof(dm)); + dm.dmSize = sizeof(dm); + EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm); + + data->rate = dm.dmDisplayFrequency; + } + + data->count++; + return TRUE; +} + Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const { _THREAD_SAFE_METHOD_ @@ -380,16 +422,16 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : nullptr; if ((Shcore == nullptr) || (getDPIForMonitor == nullptr)) { - if (Shcore) + if (Shcore) { FreeLibrary(Shcore); + } Shcore = (HMODULE)INVALID_HANDLE_VALUE; } } UINT x = 0, y = 0; - HRESULT hr = E_FAIL; if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) { - hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y); + HRESULT hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y); if (SUCCEEDED(hr) && (x > 0) && (y > 0)) { dpiX = (int)x; dpiY = (int)y; @@ -430,6 +472,13 @@ int DisplayServerWindows::screen_get_dpi(int p_screen) const { EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data); return data.dpi; } +float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const { + _THREAD_SAFE_METHOD_ + + EnumRefreshRateData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, SCREEN_REFRESH_RATE_FALLBACK }; + EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data); + return data.rate; +} bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const { #ifndef _MSC_VER @@ -490,13 +539,25 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) { wd.borderless = true; } - if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN) { + if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { wd.always_on_top = true; } if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) { wd.no_focus = true; } + if (p_flags & WINDOW_FLAG_POPUP_BIT) { + wd.is_popup = true; + } + // Inherit icons from MAIN_WINDOW for all sub windows. + HICON mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_SMALL, 0); + if (mainwindow_icon) { + SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon); + } + mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_BIG, 0); + if (mainwindow_icon) { + SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon); + } return window_id; } @@ -504,13 +565,14 @@ void DisplayServerWindows::show_window(WindowID p_id) { ERR_FAIL_COND(!windows.has(p_id)); WindowData &wd = windows[p_id]; + popup_open(p_id); if (p_id != MAIN_WINDOW_ID) { _update_window_style(p_id); } - ShowWindow(wd.hWnd, wd.no_focus ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window. - if (!wd.no_focus) { + ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window. + if (!wd.no_focus && !wd.is_popup) { SetForegroundWindow(wd.hWnd); // Slightly higher priority. SetFocus(wd.hWnd); // Set keyboard focus. } @@ -522,6 +584,8 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { ERR_FAIL_COND(!windows.has(p_window)); ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted."); + popup_close(p_window); + WindowData &wd = windows[p_window]; while (wd.transient_children.size()) { @@ -533,12 +597,12 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { } #ifdef VULKAN_ENABLED - if (rendering_driver == "vulkan") { + if (context_vulkan) { context_vulkan->window_destroy(p_window); } #endif #ifdef GLES3_ENABLED - if (rendering_driver == "opengl3") { + if (gl_manager) { gl_manager->window_destroy(p_window); } #endif @@ -557,6 +621,24 @@ void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_wind #endif } +int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const { + ERR_FAIL_COND_V(!windows.has(p_window), 0); + switch (p_handle_type) { + case DISPLAY_HANDLE: { + return 0; // Not supported. + } + case WINDOW_HANDLE: { + return (int64_t)windows[p_window].hWnd; + } + case WINDOW_VIEW: { + return 0; // Not supported. + } + default: { + return 0; + } + } +} + void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) { _THREAD_SAFE_METHOD_ @@ -661,8 +743,29 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi ERR_FAIL_COND(!windows.has(p_window)); ERR_FAIL_INDEX(p_screen, get_screen_count()); - Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(ofs + screen_get_position(p_screen), p_window); + const WindowData &wd = windows[p_window]; + if (wd.fullscreen) { + int cs = window_get_current_screen(p_window); + if (cs == p_screen) { + return; + } + Point2 pos = screen_get_position(p_screen); + Size2 size = screen_get_size(p_screen); + + MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); + } else { + Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); + window_set_position(ofs + screen_get_position(p_screen), p_window); + } + + // Don't let the mouse leave the window when resizing to a smaller resolution. + if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + RECT crect; + GetClientRect(wd.hWnd, &crect); + ClientToScreen(wd.hWnd, (POINT *)&crect.left); + ClientToScreen(wd.hWnd, (POINT *)&crect.right); + ClipCursor(&crect); + } } Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { @@ -733,6 +836,23 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window _update_real_mouse_position(p_window); } +void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclusive) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + if (wd.exclusive != p_exclusive) { + wd.exclusive = p_exclusive; + if (wd.transient_parent != INVALID_WINDOW_ID) { + if (wd.exclusive) { + WindowData &wd_parent = windows[wd.transient_parent]; + SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd); + } else { + SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr); + } + } + } +} + void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) { _THREAD_SAFE_METHOD_ @@ -755,7 +875,9 @@ void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_pa wd_window.transient_parent = INVALID_WINDOW_ID; wd_parent.transient_children.erase(p_window); - SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr); + if (wd_window.exclusive) { + SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr); + } } else { ERR_FAIL_COND(!windows.has(p_parent)); ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent"); @@ -764,7 +886,9 @@ void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_pa wd_window.transient_parent = p_parent; wd_parent.transient_children.insert(p_window); - SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd); + if (wd_window.exclusive) { + SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd); + } } } @@ -823,12 +947,12 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo wd.height = h; #if defined(VULKAN_ENABLED) - if (rendering_driver == "vulkan") { + if (context_vulkan) { context_vulkan->window_resize(p_window, w, h); } #endif #if defined(GLES3_ENABLED) - if (rendering_driver == "opengl3") { + if (gl_manager) { gl_manager->window_resize(p_window, w, h); } #endif @@ -891,7 +1015,7 @@ Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const { return Size2(); } -void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { +void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { // Windows docs for window styles: // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles // https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles @@ -900,10 +1024,14 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre r_style_ex = WS_EX_WINDOWEDGE; if (p_main_window) { r_style_ex |= WS_EX_APPWINDOW; + r_style |= WS_VISIBLE; } if (p_fullscreen || p_borderless) { r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past. + if (p_fullscreen && p_multiwindow_fs) { + r_style |= WS_BORDER; // Allows child windows to be displayed on top of full screen. + } } else { if (p_resizable) { if (p_maximized) { @@ -915,14 +1043,17 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU; } } - if (!p_borderless) { - r_style |= WS_VISIBLE; - } if (p_no_activate_focus) { r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE; } + + if (!p_borderless && !p_no_activate_focus) { + r_style |= WS_VISIBLE; + } + r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + r_style_ex |= WS_EX_ACCEPTFILES; } void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint) { @@ -934,12 +1065,12 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain DWORD style = 0; DWORD style_ex = 0; - _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.borderless, wd.resizable, wd.maximized, wd.no_focus, style, style_ex); + _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.no_focus || wd.is_popup, style, style_ex); SetWindowLongPtr(wd.hWnd, GWL_STYLE, style); SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex); - SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | (wd.no_focus ? SWP_NOACTIVATE : 0)); + SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0)); if (p_repaint) { RECT rect; @@ -954,10 +1085,11 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN) { + if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { RECT rect; wd.fullscreen = false; + wd.multiwindow_fs = false; wd.maximized = wd.was_maximized; if (wd.pre_fs_valid) { @@ -996,7 +1128,15 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) wd.minimized = true; } - if (p_mode == WINDOW_MODE_FULLSCREEN && !wd.fullscreen) { + if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + wd.multiwindow_fs = false; + _update_window_style(p_window, false); + } else { + wd.multiwindow_fs = true; + _update_window_style(p_window, false); + } + + if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) { if (wd.minimized) { ShowWindow(wd.hWnd, SW_RESTORE); } @@ -1014,7 +1154,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) wd.maximized = false; wd.minimized = false; - _update_window_style(false); + _update_window_style(p_window, false); MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); @@ -1025,6 +1165,15 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, 0, 0); } } + + // Don't let the mouse leave the window when resizing to a smaller resolution. + if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + RECT crect; + GetClientRect(wd.hWnd, &crect); + ClientToScreen(wd.hWnd, (POINT *)&crect.left); + ClientToScreen(wd.hWnd, (POINT *)&crect.right); + ClipCursor(&crect); + } } DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const { @@ -1034,7 +1183,11 @@ DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_windo const WindowData &wd = windows[p_window]; if (wd.fullscreen) { - return WINDOW_MODE_FULLSCREEN; + if (wd.multiwindow_fs) { + return WINDOW_MODE_FULLSCREEN; + } else { + return WINDOW_MODE_EXCLUSIVE_FULLSCREEN; + } } else if (wd.minimized) { return WINDOW_MODE_MINIMIZED; } else if (wd.maximized) { @@ -1068,6 +1221,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W wd.borderless = p_enabled; _update_window_style(p_window); _update_window_mouse_passthrough(p_window); + ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window. } break; case WINDOW_FLAG_ALWAYS_ON_TOP: { ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top"); @@ -1081,6 +1235,11 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W wd.no_focus = p_enabled; _update_window_style(p_window); } break; + case WINDOW_FLAG_POPUP: { + ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup."); + 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: break; } @@ -1107,6 +1266,9 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window case WINDOW_FLAG_NO_FOCUS: { return wd.no_focus; } break; + case WINDOW_FLAG_POPUP: { + return wd.is_popup; + } break; case WINDOW_FLAG_MAX: break; } @@ -1118,7 +1280,7 @@ void DisplayServerWindows::window_request_attention(WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; + const WindowData &wd = windows[p_window]; FLASHWINFO info; info.cbSize = sizeof(FLASHWINFO); @@ -1135,7 +1297,9 @@ void DisplayServerWindows::window_move_to_foreground(WindowID p_window) { ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - SetForegroundWindow(wd.hWnd); + if (!wd.no_focus && !wd.is_popup) { + SetForegroundWindow(wd.hWnd); + } } bool DisplayServerWindows::window_can_draw(WindowID p_window) const { @@ -1182,8 +1346,9 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI wd.im_position = p_pos; HIMC himc = ImmGetContext(wd.hWnd); - if (himc == (HIMC)0) + if (himc == (HIMC)0) { return; + } COMPOSITIONFORM cps; cps.dwStyle = CFS_FORCE_POSITION; @@ -1193,20 +1358,6 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI ImmReleaseContext(wd.hWnd, himc); } -void DisplayServerWindows::console_set_visible(bool p_enabled) { - _THREAD_SAFE_METHOD_ - - if (console_visible == p_enabled) { - return; - } - ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE); - console_visible = p_enabled; -} - -bool DisplayServerWindows::is_console_visible() const { - return console_visible; -} - void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { _THREAD_SAFE_METHOD_ @@ -1410,13 +1561,8 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh } } - if (hAndMask != nullptr) { - DeleteObject(hAndMask); - } - - if (hXorMask != nullptr) { - DeleteObject(hXorMask); - } + DeleteObject(hAndMask); + DeleteObject(hXorMask); memfree(buffer); DeleteObject(bitmap); @@ -1495,13 +1641,13 @@ String DisplayServerWindows::keyboard_get_layout_language(int p_index) const { } Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) const { - unsigned int modifiers = p_keycode & KEY_MODIFIER_MASK; - Key keycode_no_mod = (Key)(p_keycode & KEY_CODE_MASK); + Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK; + Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK); - if (keycode_no_mod == KEY_PRINT || - keycode_no_mod == KEY_KP_ADD || - keycode_no_mod == KEY_KP_5 || - (keycode_no_mod >= KEY_0 && keycode_no_mod <= KEY_9)) { + if (keycode_no_mod == Key::PRINT || + keycode_no_mod == Key::KP_ADD || + keycode_no_mod == Key::KP_5 || + (keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) { return p_keycode; } @@ -1521,10 +1667,10 @@ Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) cons // we limit these to ASCII to fix some layouts, including Arabic ones if (char_code >= 32 && char_code <= 127) { // Godot uses 'braces' instead of 'brackets' - if (char_code == KEY_BRACKETLEFT || char_code == KEY_BRACKETRIGHT) { + if (char_code == (unsigned int)Key::BRACKETLEFT || char_code == (unsigned int)Key::BRACKETRIGHT) { char_code += 32; } - return (Key)(char_code | modifiers); + return (Key)(char_code | (unsigned int)modifiers); } return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers); @@ -1560,7 +1706,7 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const { GetKeyboardLayoutList(layout_count, layouts); String ret = _get_full_layout_name_from_registry(layouts[p_index]); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine). - if (ret == String()) { + if (ret.is_empty()) { WCHAR buf[LOCALE_NAME_MAX_LENGTH]; memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR)); LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); @@ -1619,8 +1765,8 @@ void DisplayServerWindows::swap_buffers() { void DisplayServerWindows::set_native_icon(const String &p_filename) { _THREAD_SAFE_METHOD_ - FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); - ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'."); + Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'."); ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR)); int pos = 0; @@ -1706,7 +1852,6 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { err = GetLastError(); ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + "."); - memdelete(f); memdelete(icon_dir); } @@ -1765,16 +1910,14 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { _THREAD_SAFE_METHOD_ #if defined(VULKAN_ENABLED) - // TODO disabling for now - //context_vulkan->set_vsync_mode(p_window, p_vsync_mode); + context_vulkan->set_vsync_mode(p_window, p_vsync_mode); #endif } DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(VULKAN_ENABLED) - //TODO disabling for now - //return context_vulkan->get_vsync_mode(p_window); + return context_vulkan->get_vsync_mode(p_window); #endif return DisplayServer::VSYNC_ENABLED; } @@ -1844,7 +1987,7 @@ void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent } void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) { - ((DisplayServerWindows *)(get_singleton()))->_dispatch_input_event(p_event); + static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event); } void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) { @@ -1859,33 +2002,155 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) Variant ret; Callable::CallError ce; + { + List<WindowID>::Element *E = popup_list.front(); + if (E && Object::cast_to<InputEventKey>(*p_event)) { + // Redirect keyboard input to active popup. + if (windows.has(E->get())) { + Callable callable = windows[E->get()].input_event_callback; + if (callable.is_valid()) { + callable.call((const Variant **)&evp, 1, ret, ce); + } + } + in_dispatch_input_event = false; + return; + } + } + Ref<InputEventFromWindow> event_from_window = p_event; if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) { // Send to a single window. - if (!windows.has(event_from_window->get_window_id())) { - in_dispatch_input_event = false; - ERR_FAIL_MSG("DisplayServerWindows: Invalid window id in input event."); - } - Callable callable = windows[event_from_window->get_window_id()].input_event_callback; - if (callable.is_null()) { - in_dispatch_input_event = false; - return; + if (windows.has(event_from_window->get_window_id())) { + Callable callable = windows[event_from_window->get_window_id()].input_event_callback; + if (callable.is_valid()) { + callable.call((const Variant **)&evp, 1, ret, ce); + } } - callable.call((const Variant **)&evp, 1, ret, ce); } else { // Send to all windows. for (const KeyValue<WindowID, WindowData> &E : windows) { const Callable callable = E.value.input_event_callback; - if (callable.is_null()) { - continue; + if (callable.is_valid()) { + callable.call((const Variant **)&evp, 1, ret, ce); } - callable.call((const Variant **)&evp, 1, ret, ce); } } in_dispatch_input_event = false; } +LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) { + DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton()); + if (ds_win) { + return ds_win->MouseProc(code, wParam, lParam); + } else { + return ::CallNextHookEx(nullptr, code, wParam, lParam); + } +} + +DisplayServer::WindowID DisplayServerWindows::window_get_active_popup() const { + const List<WindowID>::Element *E = popup_list.back(); + if (E) { + return E->get(); + } else { + return INVALID_WINDOW_ID; + } +} + +void DisplayServerWindows::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + wd.parent_safe_rect = p_rect; +} + +Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), Rect2i()); + const WindowData &wd = windows[p_window]; + return wd.parent_safe_rect; +} + +void DisplayServerWindows::popup_open(WindowID p_window) { + _THREAD_SAFE_METHOD_ + + const WindowData &wd = windows[p_window]; + if (wd.is_popup) { + // Find current popup parent, or root popup if new window is not transient. + List<WindowID>::Element *C = nullptr; + List<WindowID>::Element *E = popup_list.back(); + while (E) { + if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) { + C = E; + E = E->prev(); + } else { + break; + } + } + if (C) { + _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + } + + time_since_popup = OS::get_singleton()->get_ticks_msec(); + popup_list.push_back(p_window); + } +} + +void DisplayServerWindows::popup_close(WindowID p_window) { + _THREAD_SAFE_METHOD_ + + List<WindowID>::Element *E = popup_list.find(p_window); + while (E) { + List<WindowID>::Element *F = E->next(); + WindowID win_id = E->get(); + popup_list.erase(E); + + _send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + E = F; + } +} + +LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) { + _THREAD_SAFE_METHOD_ + + uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup; + if (delta > 250) { + switch (wParam) { + case WM_NCLBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: { + MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam; + Point2i pos = Point2i(ms->pt.x, ms->pt.y); + List<WindowID>::Element *C = nullptr; + List<WindowID>::Element *E = popup_list.back(); + // Find top popup to close. + while (E) { + // Popup window area. + Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get())); + // Area of the parent window, which responsible for opening sub-menu. + Rect2i safe_rect = window_get_popup_safe_rect(E->get()); + if (win_rect.has_point(pos)) { + break; + } else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) { + break; + } else { + C = E; + E = E->prev(); + } + } + if (C) { + _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + } + } break; + } + } + return ::CallNextHookEx(mouse_monitor, code, wParam, lParam); +} + // Our default window procedure to handle processing of window-related system messages/events. // Also known as DefProc or DefWindowProc. // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures @@ -1896,7 +2161,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } else { return DefWindowProcW(hWnd, uMsg, wParam, lParam); } - }; + } WindowID window_id = INVALID_WINDOW_ID; bool window_created = false; @@ -1918,6 +2183,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA // Process window messages. switch (uMsg) { + case WM_MOUSEACTIVATE: { + if (windows[window_id].no_focus) { + return MA_NOACTIVATEANDEAT; // Do not activate, and discard mouse messages. + } else if (windows[window_id].is_popup) { + return MA_NOACTIVATE; // Do not activate, but process mouse messages. + } + } break; case WM_SETFOCUS: { windows[window_id].window_has_focus = true; last_focused_window = window_id; @@ -1968,6 +2240,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA // Run a timer to prevent event catching warning if the focused window is closing. windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); } + if (wParam != WA_INACTIVE) { + track_mouse_leave_event(hWnd); + } return 0; // Return to the message loop. } break; case WM_GETMINMAXINFO: { @@ -1998,8 +2273,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case SC_MONITORPOWER: // Monitor trying to enter powersave? return 0; // Prevent from happening. case SC_KEYMENU: - if ((lParam >> 16) <= 0) + if ((lParam >> 16) <= 0) { return 0; + } } } break; case WM_CLOSE: // Did we receive a close message? @@ -2031,8 +2307,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return 0; } - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) + if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) { OutputDebugString(TEXT("GetRawInputData does not return correct size !\n")); + } RAWINPUT *raw = (RAWINPUT *)lpb; @@ -2058,8 +2335,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_position(c); mm->set_global_position(c); - Input::get_singleton()->set_mouse_position(c); - mm->set_speed(Vector2(0, 0)); + mm->set_velocity(Vector2(0, 0)); if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) { mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY)); @@ -2128,8 +2404,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA ScreenToClient(windows[window_id].hWnd, &coords); // 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) + if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) { break; + } Ref<InputEventMouseMotion> mm; mm.instantiate(); @@ -2163,8 +2440,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA SetCursorPos(pos.x, pos.y); } - Input::get_singleton()->set_mouse_position(mm->get_position()); - mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); + mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity()); if (old_invalid) { old_x = mm->get_position().x; @@ -2175,8 +2451,9 @@ 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) + if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { Input::get_singleton()->parse_input_event(mm); + } } return 0; } @@ -2252,12 +2529,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA outside = false; // Once-off notification, must call again. - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hWnd; - tme.dwHoverTime = HOVER_DEFAULT; - TrackMouseEvent(&tme); + track_mouse_leave_event(hWnd); } // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. @@ -2310,8 +2582,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA SetCursorPos(pos.x, pos.y); } - Input::get_singleton()->set_mouse_position(mm->get_position()); - mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); + mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity()); if (old_invalid) { old_x = mm->get_position().x; @@ -2322,7 +2593,7 @@ 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) { + if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { Input::get_singleton()->parse_input_event(mm); } @@ -2358,12 +2629,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA outside = false; // Once-off notification, must call again. - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hWnd; - tme.dwHoverTime = HOVER_DEFAULT; - TrackMouseEvent(&tme); + track_mouse_leave_event(hWnd); } // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. @@ -2416,8 +2682,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA SetCursorPos(pos.x, pos.y); } - Input::get_singleton()->set_mouse_position(mm->get_position()); - mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); + mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity()); if (old_invalid) { old_x = mm->get_position().x; @@ -2428,8 +2693,9 @@ 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) + if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { Input::get_singleton()->parse_input_event(mm); + } } break; case WM_LBUTTONDOWN: @@ -2461,41 +2727,41 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA switch (uMsg) { case WM_LBUTTONDOWN: { mb->set_pressed(true); - mb->set_button_index(MOUSE_BUTTON_LEFT); + mb->set_button_index(MouseButton::LEFT); } break; case WM_LBUTTONUP: { mb->set_pressed(false); - mb->set_button_index(MOUSE_BUTTON_LEFT); + mb->set_button_index(MouseButton::LEFT); } break; case WM_MBUTTONDOWN: { mb->set_pressed(true); - mb->set_button_index(MOUSE_BUTTON_MIDDLE); + mb->set_button_index(MouseButton::MIDDLE); } break; case WM_MBUTTONUP: { mb->set_pressed(false); - mb->set_button_index(MOUSE_BUTTON_MIDDLE); + mb->set_button_index(MouseButton::MIDDLE); } break; case WM_RBUTTONDOWN: { mb->set_pressed(true); - mb->set_button_index(MOUSE_BUTTON_RIGHT); + mb->set_button_index(MouseButton::RIGHT); } break; case WM_RBUTTONUP: { mb->set_pressed(false); - mb->set_button_index(MOUSE_BUTTON_RIGHT); + mb->set_button_index(MouseButton::RIGHT); } break; case WM_LBUTTONDBLCLK: { mb->set_pressed(true); - mb->set_button_index(MOUSE_BUTTON_LEFT); + mb->set_button_index(MouseButton::LEFT); mb->set_double_click(true); } break; case WM_RBUTTONDBLCLK: { mb->set_pressed(true); - mb->set_button_index(MOUSE_BUTTON_RIGHT); + mb->set_button_index(MouseButton::RIGHT); mb->set_double_click(true); } break; case WM_MBUTTONDBLCLK: { mb->set_pressed(true); - mb->set_button_index(MOUSE_BUTTON_MIDDLE); + mb->set_button_index(MouseButton::MIDDLE); mb->set_double_click(true); } break; case WM_MOUSEWHEEL: { @@ -2506,9 +2772,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (motion > 0) { - mb->set_button_index(MOUSE_BUTTON_WHEEL_UP); + mb->set_button_index(MouseButton::WHEEL_UP); } else { - mb->set_button_index(MOUSE_BUTTON_WHEEL_DOWN); + mb->set_button_index(MouseButton::WHEEL_DOWN); } mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } break; @@ -2520,34 +2786,34 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (motion < 0) { - mb->set_button_index(MOUSE_BUTTON_WHEEL_LEFT); + mb->set_button_index(MouseButton::WHEEL_LEFT); } else { - mb->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT); + mb->set_button_index(MouseButton::WHEEL_RIGHT); } mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } break; case WM_XBUTTONDOWN: { mb->set_pressed(true); if (HIWORD(wParam) == XBUTTON1) { - mb->set_button_index(MOUSE_BUTTON_XBUTTON1); + mb->set_button_index(MouseButton::MB_XBUTTON1); } else { - mb->set_button_index(MOUSE_BUTTON_XBUTTON2); + mb->set_button_index(MouseButton::MB_XBUTTON2); } } break; case WM_XBUTTONUP: { mb->set_pressed(false); if (HIWORD(wParam) == XBUTTON1) { - mb->set_button_index(MOUSE_BUTTON_XBUTTON1); + mb->set_button_index(MouseButton::MB_XBUTTON1); } else { - mb->set_button_index(MOUSE_BUTTON_XBUTTON2); + mb->set_button_index(MouseButton::MB_XBUTTON2); } } break; case WM_XBUTTONDBLCLK: { mb->set_pressed(true); if (HIWORD(wParam) == XBUTTON1) { - mb->set_button_index(MOUSE_BUTTON_XBUTTON1); + mb->set_button_index(MouseButton::MB_XBUTTON1); } else { - mb->set_button_index(MOUSE_BUTTON_XBUTTON2); + mb->set_button_index(MouseButton::MB_XBUTTON2); } mb->set_double_click(true); } break; @@ -2561,9 +2827,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mb->set_alt_pressed(alt_mem); // mb->is_alt_pressed()=(wParam&MK_MENU)!=0; if (mb->is_pressed()) { - last_button_state |= MouseButton(1 << (mb->get_button_index() - 1)); + last_button_state |= mouse_button_to_mask(mb->get_button_index()); } else { - last_button_state &= (MouseButton) ~(1 << (mb->get_button_index() - 1)); + last_button_state &= ~mouse_button_to_mask(mb->get_button_index()); } mb->set_button_mask(last_button_state); @@ -2575,8 +2841,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) { if (mb->is_pressed()) { - if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED) + if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED) { SetCapture(hWnd); + } } else { if (--pressrc <= 0) { if (mouse_mode != MOUSE_MODE_CAPTURED) { @@ -2586,7 +2853,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } } else { - // For reasons unknown to mankind, wheel comes in screen coordinates. + // For reasons unknown to humanity, wheel comes in screen coordinates. POINT coords; coords.x = mb->get_position().x; coords.y = mb->get_position().y; @@ -2599,109 +2866,89 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mb->set_global_position(mb->get_position()); Input::get_singleton()->parse_input_event(mb); - if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) { + if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) { // Send release for mouse wheel. Ref<InputEventMouseButton> mbd = mb->duplicate(); mbd->set_window_id(window_id); - last_button_state &= (MouseButton) ~(1 << (mbd->get_button_index() - 1)); + last_button_state &= ~mouse_button_to_mask(mbd->get_button_index()); mbd->set_button_mask(last_button_state); mbd->set_pressed(false); Input::get_singleton()->parse_input_event(mbd); } } break; - case WM_MOVE: { - if (!IsIconic(windows[window_id].hWnd)) { - int x = int16_t(LOWORD(lParam)); - int y = int16_t(HIWORD(lParam)); - windows[window_id].last_pos = Point2(x, y); - - if (!windows[window_id].rect_changed_callback.is_null()) { - Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height); - Variant *sizep = &size; - Variant ret; - Callable::CallError ce; - windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce); + + case WM_WINDOWPOSCHANGED: { + Rect2i window_client_rect; + Rect2i window_rect; + { + RECT rect; + GetClientRect(hWnd, &rect); + ClientToScreen(hWnd, (POINT *)&rect.left); + ClientToScreen(hWnd, (POINT *)&rect.right); + window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + + RECT wrect; + GetWindowRect(hWnd, &wrect); + window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left, wrect.bottom - wrect.top); + } + + WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam; + WindowData &window = windows[window_id]; + + bool rect_changed = false; + if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) { + int screen_id = window_get_current_screen(window_id); + Size2i screen_size = screen_get_size(screen_id); + Point2i screen_position = screen_get_position(screen_id); + + window.maximized = false; + window.minimized = false; + window.fullscreen = false; + + if (IsIconic(hWnd)) { + window.minimized = true; + } else if (IsZoomed(hWnd)) { + window.maximized = true; + } else if (window_rect.position == screen_position && window_rect.size == screen_size) { + window.fullscreen = true; } - } - } break; - case WM_SIZE: { - // Ignore window size change when a SIZE_MINIMIZED event is triggered. - if (wParam != SIZE_MINIMIZED) { - // The new width and height of the client area. - int window_w = LOWORD(lParam); - int window_h = HIWORD(lParam); - - // Set new value to the size if it isn't preserved. - if (window_w > 0 && window_h > 0 && !windows[window_id].preserve_window_size) { - windows[window_id].width = window_w; - windows[window_id].height = window_h; + if (!window.minimized) { + window.width = window_client_rect.size.width; + window.height = window_client_rect.size.height; + + rect_changed = true; + } #if defined(VULKAN_ENABLED) - if ((rendering_driver == "vulkan") && window_created) { - context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].height); - } + if (context_vulkan && window_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); + } #endif + } - } else { // If the size is preserved. - windows[window_id].preserve_window_size = false; + if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) { + window.last_pos = window_client_rect.position; + rect_changed = true; + } - // Restore the old size. - window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id); + if (rect_changed) { + if (!window.rect_changed_callback.is_null()) { + Variant size = Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height); + const Variant *args[] = { &size }; + Variant ret; + Callable::CallError ce; + window.rect_changed_callback.call(args, 1, ret, ce); } - } else { // When the window has been minimized, preserve its size. - windows[window_id].preserve_window_size = true; } - // Call windows rect change callback. - if (!windows[window_id].rect_changed_callback.is_null()) { - Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height); - Variant *size_ptr = &size; - Variant ret; - Callable::CallError ce; - windows[window_id].rect_changed_callback.call((const Variant **)&size_ptr, 1, ret, ce); - } - - // The window has been maximized. - if (wParam == SIZE_MAXIMIZED) { - windows[window_id].maximized = true; - windows[window_id].minimized = false; - } - // The window has been minimized. - else if (wParam == SIZE_MINIMIZED) { - windows[window_id].maximized = false; - windows[window_id].minimized = true; - windows[window_id].preserve_window_size = false; - } - // The window has been resized, but neither the SIZE_MINIMIZED nor SIZE_MAXIMIZED value applies. - else if (wParam == SIZE_RESTORED) { - windows[window_id].maximized = false; - windows[window_id].minimized = false; - } -#if 0 - if (is_layered_allowed() && layered_window) { - DeleteObject(hBitmap); - - RECT r; - GetWindowRect(hWnd, &r); - dib_size = Size2i(r.right - r.left, r.bottom - r.top); - - BITMAPINFO bmi; - ZeroMemory(&bmi, sizeof(BITMAPINFO)); - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = dib_size.x; - bmi.bmiHeader.biHeight = dib_size.y; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4; - hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, nullptr, 0x0); - SelectObject(hDC_dib, hBitmap); - - ZeroMemory(dib_data, dib_size.x * dib_size.y * 4); - } -#endif + // Return here to prevent WM_MOVE and WM_SIZE from being sent + // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks + return 0; + } break; + case WM_ENTERSIZEMOVE: { Input::get_singleton()->release_pressed_events(); windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); @@ -2725,14 +2972,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_SYSKEYUP: case WM_KEYUP: case WM_KEYDOWN: { - if (wParam == VK_SHIFT) + if (wParam == VK_SHIFT) { shift_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); - if (wParam == VK_CONTROL) + } + if (wParam == VK_CONTROL) { control_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); + } if (wParam == VK_MENU) { alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); - if (lParam & (1 << 24)) + if (lParam & (1 << 24)) { gr_mem = alt_mem; + } } if (mouse_mode == MOUSE_MODE_CAPTURED) { @@ -2741,10 +2991,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST); } } - /* - if (wParam==VK_WIN) TODO wtf is this? - meta_mem=uMsg==WM_KEYDOWN; - */ [[fallthrough]]; } case WM_CHAR: { @@ -2759,10 +3005,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA ke.uMsg = uMsg; ke.window_id = window_id; - if (ke.uMsg == WM_SYSKEYDOWN) + if (ke.uMsg == WM_SYSKEYDOWN) { ke.uMsg = WM_KEYDOWN; - if (ke.uMsg == WM_SYSKEYUP) + } + if (ke.uMsg == WM_SYSKEYUP) { ke.uMsg = WM_KEYUP; + } ke.wParam = wParam; ke.lParam = lParam; @@ -2790,7 +3038,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA _drag_event(window_id, touch_pos.x, touch_pos.y, ti.dwID); } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) { _touch_event(window_id, ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID); - }; + } } bHandled = TRUE; } else { @@ -2803,12 +3051,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if (bHandled) { CloseTouchInputHandle((HTOUCHINPUT)lParam); return 0; - }; + } } break; case WM_DEVICECHANGE: { joypad->probe_joypads(); } break; + case WM_DESTROY: { + Input::get_singleton()->flush_buffered_events(); + } break; case WM_SETCURSOR: { if (LOWORD(lParam) == HTCLIENT) { if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) { @@ -2854,8 +3105,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA default: { if (user_proc) { return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); - }; - }; + } + } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -2863,10 +3114,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton()); - if (ds_win) + if (ds_win) { return ds_win->WndProc(hWnd, uMsg, wParam, lParam); - else + } else { return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } } void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam) { @@ -2876,6 +3128,9 @@ void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM alt_mem = false; control_mem = false; shift_mem = false; + + // Restore mouse mode. + _set_mouse_mode_impl(mouse_mode); } else { // WM_INACTIVE. Input::get_singleton()->release_pressed_events(); _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT); @@ -2930,8 +3185,9 @@ void DisplayServerWindows::_process_key_events() { k->set_ctrl_pressed(false); } - if (k->get_unicode() < 32) + if (k->get_unicode() < 32) { k->set_unicode(0); + } Input::get_singleton()->parse_input_event(k); } else { @@ -2953,7 +3209,7 @@ void DisplayServerWindows::_process_key_events() { if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) { // Special case for Numpad Enter key. - k->set_keycode(KEY_KP_ENTER); + k->set_keycode(Key::KP_ENTER); } else { k->set_keycode((Key)KeyMappingWindows::get_keysym(ke.wParam)); } @@ -2986,8 +3242,9 @@ void DisplayServerWindows::_process_key_events() { k->set_ctrl_pressed(false); } - if (k->get_unicode() < 32) + if (k->get_unicode() < 32) { k->set_unicode(0); + } k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30)))); @@ -3043,7 +3300,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, DWORD dwExStyle; DWORD dwStyle; - _get_window_style(window_id_counter == MAIN_WINDOW_ID, p_mode == WINDOW_MODE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT), dwStyle, dwExStyle); + _get_window_style(window_id_counter == MAIN_WINDOW_ID, (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_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT), dwStyle, dwExStyle); RECT WindowRect; @@ -3052,7 +3309,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, WindowRect.top = p_rect.position.y; WindowRect.bottom = p_rect.position.y + p_rect.size.y; - if (p_mode == WINDOW_MODE_FULLSCREEN) { + if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { int nearest_area = 0; Rect2i screen_rect; for (int i = 0; i < get_screen_count(); i++) { @@ -3095,12 +3352,12 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, windows.erase(id); return INVALID_WINDOW_ID; } - if (p_mode != WINDOW_MODE_FULLSCREEN) { + if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { wd.pre_fs_valid = true; } #ifdef VULKAN_ENABLED - if (rendering_driver == "vulkan") { + if (context_vulkan) { if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) { memdelete(context_vulkan); context_vulkan = nullptr; @@ -3111,21 +3368,13 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, #endif #ifdef GLES3_ENABLED - if (rendering_driver == "opengl3") { + if (gl_manager) { Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top); ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Failed to create an OpenGL window."); } #endif RegisterTouchWindow(wd.hWnd, 0); - - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = wd.hWnd; - tme.dwHoverTime = HOVER_DEFAULT; - TrackMouseEvent(&tme); - DragAcceptFiles(wd.hWnd, true); if ((tablet_get_current_driver() == "wintab") && wintab_available) { @@ -3238,8 +3487,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win shift_mem = false; control_mem = false; meta_mem = false; - console_visible = IsWindowVisible(GetConsoleWindow()); - hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance(); + hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance(); pressrc = 0; old_invalid = true; @@ -3300,7 +3548,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win wc.cbWndExtra = 0; wc.hInstance = hInstance ? hInstance : GetModuleHandle(nullptr); wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO); - wc.hCursor = nullptr; //LoadCursor(nullptr, IDC_ARROW); + wc.hCursor = nullptr; wc.hbrBackground = nullptr; wc.lpszMenuName = nullptr; wc.lpszClassName = L"Engine"; @@ -3351,11 +3599,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win return; } - // gl_manager->set_use_vsync(current_videomode.use_vsync); RasterizerGLES3::make_current(); } #endif + mouse_monitor = SetWindowsHookEx(WH_MOUSE, ::MouseProc, nullptr, GetCurrentThreadId()); + Point2i window_position( (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); @@ -3381,14 +3630,17 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } #endif - //set_ime_active(false); - - if (!OS::get_singleton()->is_in_low_processor_usage_mode()) { + if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) { + // Increase priority for projects that are not in low-processor mode (typically games) + // to reduce the risk of frame stuttering. + // This is not done for the editor to prevent importers or resource bakers + // from making the system unresponsive. SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); DWORD index = 0; HANDLE handle = AvSetMmThreadCharacteristics("Games", &index); - if (handle) + if (handle) { AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL); + } // This is needed to make sure that background work does not starve the main thread. // This is only setting the priority of this thread, not the whole process. @@ -3403,7 +3655,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win r_error = OK; - ((OS_Windows *)OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd); + static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd); Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); } @@ -3440,17 +3692,21 @@ DisplayServerWindows::~DisplayServerWindows() { cursors_cache.clear(); + if (mouse_monitor) { + UnhookWindowsHookEx(mouse_monitor); + } + if (user_proc) { SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc); - }; + } #ifdef GLES3_ENABLED - // destroy windows .. NYI? + // destroy windows .. NYI? #endif if (windows.has(MAIN_WINDOW_ID)) { #ifdef VULKAN_ENABLED - if (rendering_driver == "vulkan") { + if (context_vulkan) { context_vulkan->window_destroy(MAIN_WINDOW_ID); } #endif @@ -3462,14 +3718,15 @@ DisplayServerWindows::~DisplayServerWindows() { } #if defined(VULKAN_ENABLED) - if (rendering_driver == "vulkan") { - if (rendering_device_vulkan) { - rendering_device_vulkan->finalize(); - memdelete(rendering_device_vulkan); - } + if (rendering_device_vulkan) { + rendering_device_vulkan->finalize(); + memdelete(rendering_device_vulkan); + rendering_device_vulkan = nullptr; + } - if (context_vulkan) - memdelete(context_vulkan); + if (context_vulkan) { + memdelete(context_vulkan); + context_vulkan = nullptr; } #endif diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 8e2c346d5b..fcf4b5a728 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -305,12 +305,12 @@ class DisplayServerWindows : public DisplayServer { Point2i center; #if defined(GLES3_ENABLED) - GLManager_Windows *gl_manager; + GLManager_Windows *gl_manager = nullptr; #endif #if defined(VULKAN_ENABLED) - VulkanContextWindows *context_vulkan; - RenderingDeviceVulkan *rendering_device_vulkan; + VulkanContextWindows *context_vulkan = nullptr; + RenderingDeviceVulkan *rendering_device_vulkan = nullptr; #endif Map<int, Vector2> touch_state; @@ -326,12 +326,12 @@ class DisplayServerWindows : public DisplayServer { Vector<Vector2> mpath; - bool preserve_window_size = false; bool pre_fs_valid = false; RECT pre_fs_rect; bool maximized = false; bool minimized = false; bool fullscreen = false; + bool multiwindow_fs = false; bool borderless = false; bool resizable = true; bool window_focused = false; @@ -339,6 +339,7 @@ class DisplayServerWindows : public DisplayServer { bool always_on_top = false; bool no_focus = false; bool window_has_focus = false; + bool exclusive = false; // Used to transfer data between events using timer. WPARAM saved_wparam; @@ -386,9 +387,15 @@ class DisplayServerWindows : public DisplayServer { WindowID transient_parent = INVALID_WINDOW_ID; Set<WindowID> transient_children; + + bool is_popup = false; + Rect2i parent_safe_rect; }; - JoypadWindows *joypad; + JoypadWindows *joypad = nullptr; + HHOOK mouse_monitor = nullptr; + List<WindowID> popup_list; + uint64_t time_since_popup = 0; WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect); WindowID window_id_counter = MAIN_WINDOW_ID; @@ -401,7 +408,7 @@ class DisplayServerWindows : public DisplayServer { WNDPROC user_proc = nullptr; void _send_window_event(const WindowData &wd, WindowEvent p_event); - void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); + void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); MouseMode mouse_mode; int restore_mouse_trails = 0; @@ -410,11 +417,10 @@ class DisplayServerWindows : public DisplayServer { bool shift_mem = false; bool control_mem = false; bool meta_mem = false; - MouseButton last_button_state = MOUSE_BUTTON_NONE; + MouseButton last_button_state = MouseButton::NONE; bool use_raw_input = false; bool drop_events = false; bool in_dispatch_input_event = false; - bool console_visible = false; WNDCLASSEXW wc; @@ -440,6 +446,10 @@ class DisplayServerWindows : public DisplayServer { public: LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam); + + void popup_open(WindowID p_window); + void popup_close(WindowID p_window); virtual bool has_feature(Feature p_feature) const override; virtual String get_name() const override; @@ -447,7 +457,7 @@ public: virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; - virtual void mouse_warp_to_position(const Point2i &p_to) override; + virtual void warp_mouse(const Point2i &p_position) override; virtual Point2i mouse_get_position() const override; virtual MouseButton mouse_get_button_state() const override; @@ -459,6 +469,7 @@ public: virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; 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; @@ -473,11 +484,17 @@ public: virtual void show_window(WindowID p_window) override; virtual void delete_sub_window(WindowID p_window) override; + virtual WindowID window_get_active_popup() const override; + virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) override; + virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const override; + + virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override; + virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override; virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override; virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override; - virtual void gl_window_make_current(DisplayServer::WindowID p_window_id); + virtual void gl_window_make_current(DisplayServer::WindowID p_window_id) override; virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; @@ -497,6 +514,7 @@ public: virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_transient(WindowID p_window, WindowID p_parent) override; + virtual void window_set_exclusive(WindowID p_window, bool p_exclusive) override; virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override; virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override; @@ -529,9 +547,6 @@ public: virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override; virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override; - virtual void console_set_visible(bool p_enabled) override; - virtual bool is_console_visible() const override; - virtual void cursor_set_shape(CursorShape p_shape) override; virtual CursorShape cursor_get_shape() const override; virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override; diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 4ff42f3f62..0fa2913218 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -32,8 +32,6 @@ #include "export_plugin.h" -static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size); - void register_windows_exporter() { EDITOR_DEF("export/windows/rcedit", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); @@ -57,84 +55,7 @@ void register_windows_exporter() { logo->create_from_image(img); platform->set_logo(logo); platform->set_name("Windows Desktop"); - platform->set_extension("exe"); - platform->set_release_32("windows_32_release.exe"); - platform->set_debug_32("windows_32_debug.exe"); - platform->set_release_64("windows_64_release.exe"); - platform->set_debug_64("windows_64_debug.exe"); platform->set_os_name("Windows"); - platform->set_fixup_embedded_pck_func(&fixup_embedded_pck); EditorExport::get_singleton()->add_export_platform(platform); } - -static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) { - // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data - - FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE); - if (!f) { - return ERR_CANT_OPEN; - } - - // Jump to the PE header and check the magic number - { - f->seek(0x3c); - uint32_t pe_pos = f->get_32(); - - f->seek(pe_pos); - uint32_t magic = f->get_32(); - if (magic != 0x00004550) { - f->close(); - return ERR_FILE_CORRUPT; - } - } - - // Process header - - int num_sections; - { - int64_t header_pos = f->get_position(); - - f->seek(header_pos + 2); - num_sections = f->get_16(); - f->seek(header_pos + 16); - uint16_t opt_header_size = f->get_16(); - - // Skip rest of header + optional header to go to the section headers - f->seek(f->get_position() + 2 + opt_header_size); - } - - // Search for the "pck" section - - int64_t section_table_pos = f->get_position(); - - bool found = false; - for (int i = 0; i < num_sections; ++i) { - int64_t section_header_pos = section_table_pos + i * 40; - f->seek(section_header_pos); - - uint8_t section_name[9]; - f->get_buffer(section_name, 8); - section_name[8] = '\0'; - - if (strcmp((char *)section_name, "pck") == 0) { - // "pck" section found, let's patch! - - // Set virtual size to a little to avoid it taking memory (zero would give issues) - f->seek(section_header_pos + 8); - f->store_32(8); - - f->seek(section_header_pos + 16); - f->store_32(p_embedded_size); - f->seek(section_header_pos + 20); - f->store_32(p_embedded_start); - - found = true; - break; - } - } - - f->close(); - - return found ? OK : ERR_FILE_CORRUPT; -} diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h index 110e1439e2..09399f2bee 100644 --- a/platform/windows/export/export.h +++ b/platform/windows/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 165e86c066..917a0af90b 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -30,6 +30,9 @@ #include "export_plugin.h" +#include "core/config/project_settings.h" +#include "editor/editor_node.h" + Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { if (p_preset->get("codesign/enable")) { return _code_sign(p_preset, p_path); @@ -38,6 +41,18 @@ 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); + ERR_FAIL_COND_V(f.is_null(), 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::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags); @@ -51,16 +66,49 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> err = _code_sign(p_preset, p_path); } + 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"; + err = _export_debug_script(p_preset, app_name, p_path.get_file(), scr_path); + } + } + return err; } +String EditorExportPlatformWindows::get_template_file_name(const String &p_target, const String &p_arch) const { + return "windows_" + p_arch + "_" + p_target + ".exe"; +} + +List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("exe"); + return list; +} + +bool EditorExportPlatformWindows::get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + // This option is not supported by "osslsigncode", used on non-Windows host. + if (!OS::get_singleton()->has_feature("windows") && p_option == "codesign/identity_type") { + return false; + } + return true; +} + void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { EditorExportPlatformPC::get_export_options(r_options); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); -#ifdef WINDOWS_ENABLED r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0)); -#endif r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); @@ -70,8 +118,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); @@ -82,7 +130,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); - if (rcedit_path == String()) { + if (rcedit_path.is_empty()) { + WARN_PRINT("The rcedit tool is not configured in the Editor Settings (Export > Windows > Rcedit). No custom icon or app information data will be embedded in the exported executable."); return; } @@ -95,12 +144,12 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> // On non-Windows we need WINE to run rcedit String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); - if (wine_path != String() && !FileAccess::exists(wine_path)) { + if (!wine_path.is_empty() && !FileAccess::exists(wine_path)) { ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included."); return; } - if (wine_path == String()) { + if (wine_path.is_empty()) { wine_path = "wine"; // try to run wine from PATH } #endif @@ -117,39 +166,39 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> List<String> args; args.push_back(p_path); - if (icon_path != String()) { + if (!icon_path.is_empty()) { args.push_back("--set-icon"); args.push_back(icon_path); } - if (file_verion != String()) { + if (!file_verion.is_empty()) { args.push_back("--set-file-version"); args.push_back(file_verion); } - if (product_version != String()) { + if (!product_version.is_empty()) { args.push_back("--set-product-version"); args.push_back(product_version); } - if (company_name != String()) { + if (!company_name.is_empty()) { args.push_back("--set-version-string"); args.push_back("CompanyName"); args.push_back(company_name); } - if (product_name != String()) { + if (!product_name.is_empty()) { args.push_back("--set-version-string"); args.push_back("ProductName"); args.push_back(product_name); } - if (file_description != String()) { + if (!file_description.is_empty()) { args.push_back("--set-version-string"); args.push_back("FileDescription"); args.push_back(file_description); } - if (copyright != String()) { + if (!copyright.is_empty()) { args.push_back("--set-version-string"); args.push_back("LegalCopyright"); args.push_back(copyright); } - if (trademarks != String()) { + if (!trademarks.is_empty()) { args.push_back("--set-version-string"); args.push_back("LegalTrademarks"); args.push_back(trademarks); @@ -169,20 +218,20 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p #ifdef WINDOWS_ENABLED String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); - if (signtool_path != String() && !FileAccess::exists(signtool_path)) { + if (!signtool_path.is_empty() && !FileAccess::exists(signtool_path)) { ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); return ERR_FILE_NOT_FOUND; } - if (signtool_path == String()) { + if (signtool_path.is_empty()) { signtool_path = "signtool"; // try to run signtool from PATH } #else String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); - if (signtool_path != String() && !FileAccess::exists(signtool_path)) { + if (!signtool_path.is_empty() && !FileAccess::exists(signtool_path)) { ERR_PRINT("Could not find osslsigncode executable at " + signtool_path + ", aborting."); return ERR_FILE_NOT_FOUND; } - if (signtool_path == String()) { + if (signtool_path.is_empty()) { signtool_path = "osslsigncode"; // try to run signtool from PATH } #endif @@ -310,7 +359,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p } #ifndef WINDOWS_ENABLED - DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); + Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); err = tmp_dir->remove(p_path); ERR_FAIL_COND_V(err != OK, err); @@ -321,3 +370,114 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p return OK; } + +bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err = ""; + bool valid = EditorExportPlatformPC::can_export(p_preset, err, r_missing_templates); + + String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); + if (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"; + } + + String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon")); + if (!icon_path.is_empty() && !FileAccess::exists(icon_path)) { + err += TTR("Invalid icon path:") + " " + icon_path + "\n"; + } + + // Only non-negative integers can exist in the version string. + + String file_version = p_preset->get("application/file_version"); + if (!file_version.is_empty()) { + PackedStringArray version_array = file_version.split(".", false); + if (version_array.size() != 4 || !version_array[0].is_valid_int() || + !version_array[1].is_valid_int() || !version_array[2].is_valid_int() || + !version_array[3].is_valid_int() || file_version.find("-") > -1) { + err += TTR("Invalid file version:") + " " + file_version + "\n"; + } + } + + String product_version = p_preset->get("application/product_version"); + if (!product_version.is_empty()) { + PackedStringArray version_array = product_version.split(".", false); + if (version_array.size() != 4 || !version_array[0].is_valid_int() || + !version_array[1].is_valid_int() || !version_array[2].is_valid_int() || + !version_array[3].is_valid_int() || product_version.find("-") > -1) { + err += TTR("Invalid product version:") + " " + product_version + "\n"; + } + } + + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const { + // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data + + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ_WRITE); + if (f.is_null()) { + return ERR_CANT_OPEN; + } + + // Jump to the PE header and check the magic number + { + f->seek(0x3c); + uint32_t pe_pos = f->get_32(); + + f->seek(pe_pos); + uint32_t magic = f->get_32(); + if (magic != 0x00004550) { + return ERR_FILE_CORRUPT; + } + } + + // Process header + + int num_sections; + { + int64_t header_pos = f->get_position(); + + f->seek(header_pos + 2); + num_sections = f->get_16(); + f->seek(header_pos + 16); + uint16_t opt_header_size = f->get_16(); + + // Skip rest of header + optional header to go to the section headers + f->seek(f->get_position() + 2 + opt_header_size); + } + + // Search for the "pck" section + + int64_t section_table_pos = f->get_position(); + + bool found = false; + for (int i = 0; i < num_sections; ++i) { + int64_t section_header_pos = section_table_pos + i * 40; + f->seek(section_header_pos); + + uint8_t section_name[9]; + f->get_buffer(section_name, 8); + section_name[8] = '\0'; + + if (strcmp((char *)section_name, "pck") == 0) { + // "pck" section found, let's patch! + + // Set virtual size to a little to avoid it taking memory (zero would give issues) + f->seek(section_header_pos + 8); + f->store_32(8); + + f->seek(section_header_pos + 16); + f->store_32(p_embedded_size); + f->seek(section_header_pos + 20); + f->store_32(p_embedded_start); + + found = true; + break; + } + } + + return found ? OK : ERR_FILE_CORRUPT; +} diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index 11d3826410..39d1cf4c77 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -34,18 +34,23 @@ #include "core/io/file_access.h" #include "core/os/os.h" #include "editor/editor_export.h" -#include "editor/editor_node.h" #include "editor/editor_settings.h" #include "platform/windows/logo.gen.h" class EditorExportPlatformWindows : public EditorExportPlatformPC { void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path); 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); - virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); - virtual void get_export_options(List<ExportOption> *r_options); + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) override; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; + virtual void get_export_options(List<ExportOption> *r_options) override; + virtual bool get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override; + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + virtual String get_template_file_name(const String &p_target, const String &p_arch) const override; + virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const override; }; #endif diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp index 98205d6282..a97fa99d7f 100644 --- a/platform/windows/gl_manager_windows.cpp +++ b/platform/windows/gl_manager_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -56,14 +56,9 @@ typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int int GLManager_Windows::_find_or_create_display(GLWindow &win) { // find display NYI, only 1 supported so far - if (_displays.size()) + if (_displays.size()) { return 0; - - // for (unsigned int n = 0; n < _displays.size(); n++) { - // const GLDisplay &d = _displays[n]; - // if (d.x11_display == p_x11_display) - // return n; - // } + } // create GLDisplay d_temp = {}; @@ -77,7 +72,7 @@ int GLManager_Windows::_find_or_create_display(GLWindow &win) { if (err != OK) { // not good // delete the _display? - _displays.remove(new_display_id); + _displays.remove_at(new_display_id); return -1; } @@ -193,13 +188,10 @@ Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND if (win.gldisplay_id == -1) { // release DC? - _windows.remove(_windows.size() - 1); + _windows.remove_at(_windows.size() - 1); return FAILED; } - // the display could be invalid .. check NYI - GLDisplay &gl_display = _displays[win.gldisplay_id]; - // make current window_make_current(_windows.size() - 1); @@ -233,23 +225,27 @@ void GLManager_Windows::window_destroy(DisplayServer::WindowID p_window_id) { } void GLManager_Windows::release_current() { - if (!_current_window) + if (!_current_window) { return; + } wglMakeCurrent(_current_window->hDC, nullptr); } void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) { - if (p_window_id == -1) + if (p_window_id == -1) { return; + } GLWindow &win = _windows[p_window_id]; - if (!win.in_use) + if (!win.in_use) { return; + } // noop - if (&win == _current_window) + if (&win == _current_window) { return; + } const GLDisplay &disp = get_display(win.gldisplay_id); wglMakeCurrent(win.hDC, disp.hRC); @@ -258,8 +254,9 @@ void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) } void GLManager_Windows::make_current() { - if (!_current_window) + if (!_current_window) { return; + } if (!_current_window->in_use) { WARN_PRINT("current window not in use!"); return; @@ -272,8 +269,9 @@ void GLManager_Windows::swap_buffers() { // NO NEED TO CALL SWAP BUFFERS for each window... // see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml - if (!_current_window) + if (!_current_window) { return; + } if (!_current_window->in_use) { WARN_PRINT("current window not in use!"); return; @@ -307,12 +305,15 @@ void GLManager_Windows::set_use_vsync(bool p_use) { if (!setup) { setup = true; String extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display)); - if (extensions.find("GLX_EXT_swap_control") != -1) + if (extensions.find("GLX_EXT_swap_control") != -1) { glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT"); - if (extensions.find("GLX_MESA_swap_control") != -1) + } + if (extensions.find("GLX_MESA_swap_control") != -1) { glXSwapIntervalMESA = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalMESA"); - if (extensions.find("GLX_SGI_swap_control") != -1) + } + if (extensions.find("GLX_SGI_swap_control") != -1) { glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI"); + } } int val = p_use ? 1 : 0; if (glXSwapIntervalMESA) { @@ -322,8 +323,9 @@ void GLManager_Windows::set_use_vsync(bool p_use) { } else if (glXSwapIntervalEXT) { GLXDrawable drawable = glXGetCurrentDrawable(); glXSwapIntervalEXT(x11_display, drawable, val); - } else + } else { return; + } use_vsync = p_use; */ } diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows.h index 9733a57420..dc411983e8 100644 --- a/platform/windows/gl_manager_windows.h +++ b/platform/windows/gl_manager_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -52,19 +52,18 @@ public: private: // any data specific to the window struct GLWindow { - GLWindow() { in_use = false; } - bool in_use; + bool in_use = false; // the external ID .. should match the GL window number .. unused I think - DisplayServer::WindowID window_id; - int width; - int height; + DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID; + int width = 0; + int height = 0; // windows specific HDC hDC; HWND hwnd; - int gldisplay_id; + int gldisplay_id = 0; }; struct GLDisplay { @@ -75,7 +74,7 @@ private: LocalVector<GLWindow> _windows; LocalVector<GLDisplay> _displays; - GLWindow *_current_window; + GLWindow *_current_window = nullptr; PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; diff --git a/platform/windows/godot.ico b/platform/windows/godot.ico Binary files differindex dd611e07da..25830ffdc6 100644 --- a/platform/windows/godot.ico +++ b/platform/windows/godot.ico diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp index 22e2e5f7e5..ad4e3ae77c 100644 --- a/platform/windows/godot_windows.cpp +++ b/platform/windows/godot_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -39,7 +39,7 @@ #ifndef TOOLS_ENABLED #if defined _MSC_VER #pragma section("pck", read) -__declspec(allocate("pck")) static char dummy[8] = { 0 }; +__declspec(allocate("pck")) static const char dummy[8] = { 0 }; #elif defined __GNUC__ static const char dummy[8] __attribute__((section("pck"), used)) = { 0 }; #endif @@ -140,6 +140,11 @@ int widechar_main(int argc, wchar_t **argv) { setlocale(LC_CTYPE, ""); +#ifndef TOOLS_ENABLED + // Workaround to prevent LTCG (MSVC LTO) from removing "pck" section + const char *dummy_guard = dummy; +#endif + char **argv_utf8 = new char *[argc]; for (int i = 0; i < argc; ++i) { @@ -158,8 +163,9 @@ int widechar_main(int argc, wchar_t **argv) { return 255; } - if (Main::start()) + if (Main::start()) { os.run(); + } Main::cleanup(); for (int i = 0; i < argc; ++i) { @@ -168,7 +174,7 @@ int widechar_main(int argc, wchar_t **argv) { delete[] argv_utf8; return os.get_exit_code(); -}; +} int _main() { LPWSTR *wc_argv; diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index 94da63e49d..d039fd13a7 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -60,8 +60,9 @@ JoypadWindows::JoypadWindows(HWND *hwnd) { load_xinput(); - for (int i = 0; i < JOYPADS_MAX; i++) + for (int i = 0; i < JOYPADS_MAX; i++) { attached_joypads[i] = false; + } HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr); if (result == DI_OK) { @@ -97,11 +98,13 @@ bool JoypadWindows::have_device(const GUID &p_guid) { // adapted from SDL2, works a lot better than the MSDN version bool JoypadWindows::is_xinput_device(const GUID *p_guid) { - static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; + static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x28DE, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; - if (p_guid == &IID_ValveStreamingGamepad || p_guid == &IID_X360WiredGamepad || p_guid == &IID_X360WirelessGamepad) + if (memcmp(p_guid, &IID_ValveStreamingGamepad, sizeof(*p_guid)) == 0 || + memcmp(p_guid, &IID_X360WiredGamepad, sizeof(*p_guid)) == 0 || + memcmp(p_guid, &IID_X360WirelessGamepad, sizeof(*p_guid)) == 0) return true; PRAWINPUTDEVICELIST dev_list = nullptr; @@ -142,8 +145,9 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { HRESULT hr; int num = input->get_unused_joy_id(); - if (have_device(instance->guidInstance) || num == -1) + if (have_device(instance->guidInstance) || num == -1) { return false; + } d_joypads[num] = dinput_gamepad(); dinput_gamepad *joy = &d_joypads[num]; @@ -194,27 +198,28 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_ DIPROPRANGE prop_range; DIPROPDWORD dilong; LONG ofs; - if (ob->guidType == GUID_XAxis) + if (ob->guidType == GUID_XAxis) { ofs = DIJOFS_X; - else if (ob->guidType == GUID_YAxis) + } else if (ob->guidType == GUID_YAxis) { ofs = DIJOFS_Y; - else if (ob->guidType == GUID_ZAxis) + } else if (ob->guidType == GUID_ZAxis) { ofs = DIJOFS_Z; - else if (ob->guidType == GUID_RxAxis) + } else if (ob->guidType == GUID_RxAxis) { ofs = DIJOFS_RX; - else if (ob->guidType == GUID_RyAxis) + } else if (ob->guidType == GUID_RyAxis) { ofs = DIJOFS_RY; - else if (ob->guidType == GUID_RzAxis) + } else if (ob->guidType == GUID_RzAxis) { ofs = DIJOFS_RZ; - else if (ob->guidType == GUID_Slider) { + } else if (ob->guidType == GUID_Slider) { if (slider_count < 2) { ofs = DIJOFS_SLIDER(slider_count); slider_count++; } else { return; } - } else + } else { return; + } prop_range.diph.dwSize = sizeof(DIPROPRANGE); prop_range.diph.dwHeaderSize = sizeof(DIPROPHEADER); prop_range.diph.dwObj = ob->dwType; @@ -225,8 +230,9 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_ dinput_gamepad &joy = d_joypads[p_joy_id]; res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_RANGE, &prop_range.diph); - if (FAILED(res)) + if (FAILED(res)) { return; + } dilong.diph.dwSize = sizeof(dilong); dilong.diph.dwHeaderSize = sizeof(dilong.diph); @@ -235,15 +241,16 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_ dilong.dwData = 0; res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_DEADZONE, &dilong.diph); - if (FAILED(res)) + if (FAILED(res)) { return; + } joy.joy_axis.push_back(ofs); } } BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context) { - JoypadWindows *self = (JoypadWindows *)p_context; + JoypadWindows *self = static_cast<JoypadWindows *>(p_context); if (self->is_xinput_device(&p_instance->guidProduct)) { return DIENUM_CONTINUE; } @@ -251,9 +258,9 @@ BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, vo return DIENUM_CONTINUE; } -BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context) { - JoypadWindows *self = (JoypadWindows *)context; - self->setup_joypad_object(instance, self->id_to_change); +BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *p_instance, void *p_context) { + JoypadWindows *self = static_cast<JoypadWindows *>(p_context); + self->setup_joypad_object(p_instance, self->id_to_change); return DIENUM_CONTINUE; } @@ -266,8 +273,9 @@ void JoypadWindows::close_joypad(int id) { return; } - if (!d_joypads[id].attached) + if (!d_joypads[id].attached) { return; + } d_joypads[id].di_joy->Unacquire(); d_joypads[id].di_joy->Release(); @@ -334,12 +342,12 @@ void JoypadWindows::process_joypads() { button_mask = button_mask * 2; } - input->joy_axis(joy.id, JOY_AXIS_LEFT_X, axis_correct(joy.state.Gamepad.sThumbLX, true)); - input->joy_axis(joy.id, JOY_AXIS_LEFT_Y, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true)); - input->joy_axis(joy.id, JOY_AXIS_RIGHT_X, axis_correct(joy.state.Gamepad.sThumbRX, true)); - input->joy_axis(joy.id, JOY_AXIS_RIGHT_Y, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true)); - input->joy_axis(joy.id, JOY_AXIS_TRIGGER_LEFT, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true)); - input->joy_axis(joy.id, JOY_AXIS_TRIGGER_RIGHT, axis_correct(joy.state.Gamepad.bRightTrigger, true, true)); + input->joy_axis(joy.id, JoyAxis::LEFT_X, axis_correct(joy.state.Gamepad.sThumbLX, true)); + input->joy_axis(joy.id, JoyAxis::LEFT_Y, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true)); + input->joy_axis(joy.id, JoyAxis::RIGHT_X, axis_correct(joy.state.Gamepad.sThumbRX, true)); + input->joy_axis(joy.id, JoyAxis::RIGHT_Y, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true)); + input->joy_axis(joy.id, JoyAxis::TRIGGER_LEFT, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true)); + input->joy_axis(joy.id, JoyAxis::TRIGGER_RIGHT, axis_correct(joy.state.Gamepad.bRightTrigger, true, true)); joy.last_packet = joy.state.dwPacketNumber; } uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id); @@ -353,16 +361,18 @@ void JoypadWindows::process_joypads() { } } else if (joy.vibrating && joy.ff_end_timestamp != 0) { uint64_t current_time = OS::get_singleton()->get_ticks_usec(); - if (current_time >= joy.ff_end_timestamp) + if (current_time >= joy.ff_end_timestamp) { joypad_vibration_stop_xinput(i, current_time); + } } } for (int i = 0; i < JOYPADS_MAX; i++) { dinput_gamepad *joy = &d_joypads[i]; - if (!joy->attached) + if (!joy->attached) { continue; + } DIJOYSTATE2 js; hr = joy->di_joy->Poll(); @@ -394,7 +404,7 @@ void JoypadWindows::process_joypads() { // on mingw, these constants are not constants int count = 8; - LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) }; + const LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) }; int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz, js.rglSlider[0], js.rglSlider[1] }; for (int j = 0; j < joy->joy_axis.size(); j++) { @@ -402,9 +412,9 @@ void JoypadWindows::process_joypads() { if (joy->joy_axis[j] == axes[k]) { input->joy_axis(joy->id, (JoyAxis)j, axis_correct(values[k])); break; - }; - }; - }; + } + } + } } return; } @@ -417,62 +427,56 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) { // BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);" // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks if (LOWORD(p_dpad) == 0xFFFF) { - dpad_val = (HatMask)HatMask::HAT_MASK_CENTER; + dpad_val = (HatMask)HatMask::CENTER; } if (p_dpad == 0) { - dpad_val = (HatMask)HatMask::HAT_MASK_UP; + dpad_val = (HatMask)HatMask::UP; } else if (p_dpad == 4500) { - dpad_val = (HatMask)(HatMask::HAT_MASK_UP | HatMask::HAT_MASK_RIGHT); + dpad_val = (HatMask)(HatMask::UP | HatMask::RIGHT); } else if (p_dpad == 9000) { - dpad_val = (HatMask)HatMask::HAT_MASK_RIGHT; + dpad_val = (HatMask)HatMask::RIGHT; } else if (p_dpad == 13500) { - dpad_val = (HatMask)(HatMask::HAT_MASK_RIGHT | HatMask::HAT_MASK_DOWN); + dpad_val = (HatMask)(HatMask::RIGHT | HatMask::DOWN); } else if (p_dpad == 18000) { - dpad_val = (HatMask)HatMask::HAT_MASK_DOWN; + dpad_val = (HatMask)HatMask::DOWN; } else if (p_dpad == 22500) { - dpad_val = (HatMask)(HatMask::HAT_MASK_DOWN | HatMask::HAT_MASK_LEFT); + dpad_val = (HatMask)(HatMask::DOWN | HatMask::LEFT); } else if (p_dpad == 27000) { - dpad_val = (HatMask)HatMask::HAT_MASK_LEFT; + dpad_val = (HatMask)HatMask::LEFT; } else if (p_dpad == 31500) { - dpad_val = (HatMask)(HatMask::HAT_MASK_LEFT | HatMask::HAT_MASK_UP); + dpad_val = (HatMask)(HatMask::LEFT | HatMask::UP); } input->joy_hat(p_device, dpad_val); -}; +} -Input::JoyAxisValue JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const { - Input::JoyAxisValue jx; +float JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const { if (Math::abs(p_val) < MIN_JOY_AXIS) { - jx.min = p_trigger ? 0 : -1; - jx.value = 0.0f; - return jx; - } - if (p_xinput) { - if (p_trigger) { - jx.min = 0; - jx.value = (float)p_val / MAX_TRIGGER; - return jx; - } - jx.min = -1; - if (p_val < 0) { - jx.value = (float)p_val / MAX_JOY_AXIS; - } else { - jx.value = (float)p_val / (MAX_JOY_AXIS - 1); - } - if (p_negate) { - jx.value = -jx.value; - } - return jx; + return p_trigger ? -1.0f : 0.0f; + } + if (!p_xinput) { + return (float)p_val / MAX_JOY_AXIS; + } + if (p_trigger) { + // Convert to a value between -1.0f and 1.0f. + return 2.0f * p_val / MAX_TRIGGER - 1.0f; + } + float value; + if (p_val < 0) { + value = (float)p_val / MAX_JOY_AXIS; + } else { + value = (float)p_val / (MAX_JOY_AXIS - 1); + } + if (p_negate) { + value = -value; } - jx.min = -1; - jx.value = (float)p_val / MAX_JOY_AXIS; - return jx; + return value; } void JoypadWindows::joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) { diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h index 757fb54fb3..d239471a5c 100644 --- a/platform/windows/joypad_windows.h +++ b/platform/windows/joypad_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -86,8 +86,9 @@ private: attached = false; confirmed = false; - for (int i = 0; i < MAX_JOY_BUTTONS; i++) + for (int i = 0; i < MAX_JOY_BUTTONS; i++) { last_buttons[i] = false; + } } }; @@ -104,10 +105,10 @@ private: typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState); typedef DWORD(WINAPI *XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration); - HWND *hWnd; + HWND *hWnd = nullptr; HANDLE xinput_dll; LPDIRECTINPUT8 dinput; - Input *input; + Input *input = nullptr; int id_to_change; int slider_count; @@ -132,7 +133,7 @@ private: void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp); - Input::JoyAxisValue axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const; + float axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const; XInputGetState_t xinput_get_state; XInputSetState_t xinput_set_state; }; diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp index b9cf8151fc..e32dc0d1a6 100644 --- a/platform/windows/key_mapping_windows.cpp +++ b/platform/windows/key_mapping_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -32,321 +32,398 @@ #include <stdio.h> +// This provides translation from Windows virtual key codes to Godot and back. +// See WinUser.h and the below for documentation: +// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes + struct _WinTranslatePair { - unsigned int keysym; + Key keysym; unsigned int keycode; }; static _WinTranslatePair _vk_to_keycode[] = { - { KEY_BACKSPACE, VK_BACK }, // (0x08) // backspace - { KEY_TAB, VK_TAB }, //(0x09) + // VK_LBUTTON (0x01) + // VK_RBUTTON (0x02) + // VK_CANCEL (0x03) + // VK_MBUTTON (0x04) + // VK_XBUTTON1 (0x05) + // VK_XBUTTON2 (0x06) + // We have no mappings for the above, as we only map keyboard buttons here. + + // 0x07 is undefined. + + { Key::BACKSPACE, VK_BACK }, // (0x08) + { Key::TAB, VK_TAB }, // (0x09) + + // 0x0A-0B are reserved. + + { Key::CLEAR, VK_CLEAR }, // (0x0C) + { Key::ENTER, VK_RETURN }, // (0x0D) + + // 0x0E-0F are undefined. + + { Key::SHIFT, VK_SHIFT }, // (0x10) + { Key::CTRL, VK_CONTROL }, // (0x11) + { Key::ALT, VK_MENU }, // (0x12) + { Key::PAUSE, VK_PAUSE }, // (0x13) + { Key::CAPSLOCK, VK_CAPITAL }, // (0x14) + + // 0x15-1A are IME keys. We have no mapping. + + { Key::ESCAPE, VK_ESCAPE }, // (0x1B) + + // 0x1C-1F are IME keys. We have no mapping. + + { Key::SPACE, VK_SPACE }, // (0x20) + { Key::PAGEUP, VK_PRIOR }, // (0x21) + { Key::PAGEDOWN, VK_NEXT }, // (0x22) + { Key::END, VK_END }, // (0x23) + { Key::HOME, VK_HOME }, // (0x24) + { Key::LEFT, VK_LEFT }, // (0x25) + { Key::UP, VK_UP }, // (0x26) + { Key::RIGHT, VK_RIGHT }, // (0x27) + { Key::DOWN, VK_DOWN }, // (0x28) + + // VK_SELECT (0x29) + // Old select key, e.g. on Digital Equipment Corporation keyboards. + // Old and uncommon, we have no mapping. + + { Key::PRINT, VK_PRINT }, // (0x2A) + // Old IBM key, modern keyboards use VK_SNAPSHOT. Map to VK_SNAPSHOT. + + // VK_EXECUTE (0x2B) + // Old and uncommon, we have no mapping. + + { Key::PRINT, VK_SNAPSHOT }, // (0x2C) + { Key::INSERT, VK_INSERT }, // (0x2D) + { Key::KEY_DELETE, VK_DELETE }, // (0x2E) + + { Key::HELP, VK_HELP }, // (0x2F) + // Old and uncommon, but we have a mapping. + + { Key::KEY_0, (0x30) }, // 0 key. + { Key::KEY_1, (0x31) }, // 1 key. + { Key::KEY_2, (0x32) }, // 2 key. + { Key::KEY_3, (0x33) }, // 3 key. + { Key::KEY_4, (0x34) }, // 4 key. + { Key::KEY_5, (0x35) }, // 5 key. + { Key::KEY_6, (0x36) }, // 6 key. + { Key::KEY_7, (0x37) }, // 7 key. + { Key::KEY_8, (0x38) }, // 8 key. + { Key::KEY_9, (0x39) }, // 9 key. + // 0x3A-40 are undefined. + { Key::A, (0x41) }, // A key. + { Key::B, (0x42) }, // B key. + { Key::C, (0x43) }, // C key. + { Key::D, (0x44) }, // D key. + { Key::E, (0x45) }, // E key. + { Key::F, (0x46) }, // F key. + { Key::G, (0x47) }, // G key. + { Key::H, (0x48) }, // H key. + { Key::I, (0x49) }, // I key + { Key::J, (0x4A) }, // J key. + { Key::K, (0x4B) }, // K key. + { Key::L, (0x4C) }, // L key. + { Key::M, (0x4D) }, // M key. + { Key::N, (0x4E) }, // N key. + { Key::O, (0x4F) }, // O key. + { Key::P, (0x50) }, // P key. + { Key::Q, (0x51) }, // Q key. + { Key::R, (0x52) }, // R key. + { Key::S, (0x53) }, // S key. + { Key::T, (0x54) }, // T key. + { Key::U, (0x55) }, // U key. + { Key::V, (0x56) }, // V key. + { Key::W, (0x57) }, // W key. + { Key::X, (0x58) }, // X key. + { Key::Y, (0x59) }, // Y key. + { Key::Z, (0x5A) }, // Z key. + + { (Key)KeyModifierMask::META, VK_LWIN }, // (0x5B) + { (Key)KeyModifierMask::META, VK_RWIN }, // (0x5C) + { Key::MENU, VK_APPS }, // (0x5D) + // 0x5E is reserved. + { Key::STANDBY, VK_SLEEP }, // (0x5F) + { Key::KP_0, VK_NUMPAD0 }, // (0x60) + { Key::KP_1, VK_NUMPAD1 }, // (0x61) + { Key::KP_2, VK_NUMPAD2 }, // (0x62) + { Key::KP_3, VK_NUMPAD3 }, // (0x63) + { Key::KP_4, VK_NUMPAD4 }, // (0x64) + { Key::KP_5, VK_NUMPAD5 }, // (0x65) + { Key::KP_6, VK_NUMPAD6 }, // (0x66) + { Key::KP_7, VK_NUMPAD7 }, // (0x67) + { Key::KP_8, VK_NUMPAD8 }, // (0x68) + { Key::KP_9, VK_NUMPAD9 }, // (0x69) + { Key::KP_MULTIPLY, VK_MULTIPLY }, // (0x6A) + { Key::KP_ADD, VK_ADD }, // (0x6B) + { Key::KP_PERIOD, VK_SEPARATOR }, // (0x6C) + // VK_SEPERATOR (key 0x6C) is not found on US keyboards. + // It is used on some Brazilian and Far East keyboards. + // We don't have a direct mapping, map to period. + { Key::KP_SUBTRACT, VK_SUBTRACT }, // (0x6D) + { Key::KP_PERIOD, VK_DECIMAL }, // (0x6E) + { Key::KP_DIVIDE, VK_DIVIDE }, // (0x6F) + { Key::F1, VK_F1 }, // (0x70) + { Key::F2, VK_F2 }, // (0x71) + { Key::F3, VK_F3 }, // (0x72) + { Key::F4, VK_F4 }, // (0x73) + { Key::F5, VK_F5 }, // (0x74) + { Key::F6, VK_F6 }, // (0x75) + { Key::F7, VK_F7 }, // (0x76) + { Key::F8, VK_F8 }, // (0x77) + { Key::F9, VK_F9 }, // (0x78) + { Key::F10, VK_F10 }, // (0x79) + { Key::F11, VK_F11 }, // (0x7A) + { Key::F12, VK_F12 }, // (0x7B) + { Key::F13, VK_F13 }, // (0x7C) + { Key::F14, VK_F14 }, // (0x7D) + { Key::F15, VK_F15 }, // (0x7E) + { Key::F16, VK_F16 }, // (0x7F) + // We have no mappings for F17-F24. (0x80-87) + // 0x88-8F are reserved for UI navigation. + { Key::NUMLOCK, VK_NUMLOCK }, // (0x90) + { Key::SCROLLLOCK, VK_SCROLL }, // (0x91) + + { Key::EQUAL, VK_OEM_NEC_EQUAL }, // (0x92) + // OEM NEC PC-9800 numpad '=' key. + + // 0x93-96 are OEM specific (e.g. used by Fujitsu/OASYS), we have no mappings. + // 0x97-9F are unassigned. + + { Key::SHIFT, VK_LSHIFT }, // (0xA0) + { Key::SHIFT, VK_RSHIFT }, // (0xA1) + { Key::CTRL, VK_LCONTROL }, // (0xA2) + { Key::CTRL, VK_RCONTROL }, // (0xA3) + { Key::MENU, VK_LMENU }, // (0xA4) + { Key::MENU, VK_RMENU }, // (0xA5) + + { Key::BACK, VK_BROWSER_BACK }, // (0xA6) + { Key::FORWARD, VK_BROWSER_FORWARD }, // (0xA7) + { Key::REFRESH, VK_BROWSER_REFRESH }, // (0xA8) + { Key::STOP, VK_BROWSER_STOP }, // (0xA9) + { Key::SEARCH, VK_BROWSER_SEARCH }, // (0xAA) + { Key::FAVORITES, VK_BROWSER_FAVORITES }, // (0xAB) + { Key::HOMEPAGE, VK_BROWSER_HOME }, // (0xAC) + { Key::VOLUMEMUTE, VK_VOLUME_MUTE }, // (0xAD) + { Key::VOLUMEDOWN, VK_VOLUME_DOWN }, // (0xAE) + { Key::VOLUMEUP, VK_VOLUME_UP }, // (0xAF) + { Key::MEDIANEXT, VK_MEDIA_NEXT_TRACK }, // (0xB0) + { Key::MEDIAPREVIOUS, VK_MEDIA_PREV_TRACK }, // (0xB1) + { Key::MEDIASTOP, VK_MEDIA_STOP }, // (0xB2) + + { Key::MEDIAPLAY, VK_MEDIA_PLAY_PAUSE }, // (0xB3) + // Media button play/pause toggle. + // Map to media play (there is no other 'play' mapping on Windows). + + { Key::LAUNCHMAIL, VK_LAUNCH_MAIL }, // (0xB4) + { Key::LAUNCHMEDIA, VK_LAUNCH_MEDIA_SELECT }, // (0xB5) + { Key::LAUNCH0, VK_LAUNCH_APP1 }, // (0xB6) + { Key::LAUNCH1, VK_LAUNCH_APP2 }, // (0xB7) + + // 0xB8-B9 are reserved. + + { Key::SEMICOLON, VK_OEM_1 }, // (0xBA) + // Misc. character, can vary by keyboard/region. + // Windows 2000/XP: For US standard keyboards, the ';:' key. + + { Key::EQUAL, VK_OEM_PLUS }, // (0xBB) + // Windows 2000/XP: For any country/region, the '+' key. + { Key::COMMA, VK_OEM_COMMA }, // (0xBC) + // Windows 2000/XP: For any country/region, the ',' key. + { Key::MINUS, VK_OEM_MINUS }, // (0xBD) + // Windows 2000/XP: For any country/region, the '-' key. + { Key::PERIOD, VK_OEM_PERIOD }, // (0xBE) + // Windows 2000/XP: For any country/region, the '.' key. + + { Key::SLASH, VK_OEM_2 }, // (0xBF) + // Windows 2000/XP: For US standard keyboards, the '/?' key. + + { Key::QUOTELEFT, VK_OEM_3 }, // (0xC0) + // Windows 2000/XP: For US standard keyboards, the '`~' key. + + // 0xC1-D7 are reserved. 0xD8-DA are unassigned. + // TODO: 0xC3-DA may be used for old gamepads? Maybe we want to support this? See WinUser.h. + + { Key::BRACKETLEFT, VK_OEM_4 }, // (0xDB) + // Misc. character, can vary by keyboard/region. + // Windows 2000/XP: For US standard keyboards, the '[{' key. + + { Key::BACKSLASH, VK_OEM_5 }, // (0xDC) + // Misc. character, can vary by keyboard/region. + // Windows 2000/XP: For US standard keyboards, the '\|' key. + + { Key::BRACKETRIGHT, VK_OEM_6 }, // (0xDD) + // Misc. character, can vary by keyboard/region. + // Windows 2000/XP: For US standard keyboards, the ']}' key. + + { Key::APOSTROPHE, VK_OEM_7 }, // (0xDE) + // Misc. character, can vary by keyboard/region. + // Windows 2000/XP: For US standard keyboards, single quote/double quote. + + // VK_OEM_8 (0xDF) + // Misc. character, can vary by keyboard/region. We have no mapping. + + // 0xE0 is reserved. 0xE1 is OEM specific, we have no mapping. + + // VK_OEM_102 (0xE2) + // Either angle bracket or backslash key on the RT 102-key keyboard. + // Old and uncommon, we have no mapping. + + { Key::HELP, VK_ICO_HELP }, // (0xE3) + // OEM (ICO) help key. Map to help. - //VK_CLEAR (0x0C) + // 0xE4 is OEM (e.g. ICO) specific, we have no mapping. - { KEY_ENTER, VK_RETURN }, //(0x0D) + // VK_PROCESSKEY (0xE5) + // For IME, we have no mapping. - { KEY_SHIFT, VK_SHIFT }, //(0x10) - - { KEY_CTRL, VK_CONTROL }, //(0x11) - - { KEY_ALT, VK_MENU }, //(0x12) - - { KEY_PAUSE, VK_PAUSE }, //(0x13) - - { KEY_CAPSLOCK, VK_CAPITAL }, //(0x14) - - { KEY_ESCAPE, VK_ESCAPE }, //(0x1B) - - { KEY_SPACE, VK_SPACE }, //(0x20) - - { KEY_PAGEUP, VK_PRIOR }, //(0x21) - - { KEY_PAGEDOWN, VK_NEXT }, //(0x22) - - { KEY_END, VK_END }, //(0x23) - - { KEY_HOME, VK_HOME }, //(0x24) - - { KEY_LEFT, VK_LEFT }, //(0x25) - - { KEY_UP, VK_UP }, //(0x26) - - { KEY_RIGHT, VK_RIGHT }, //(0x27) - - { KEY_DOWN, VK_DOWN }, // (0x28) - - //VK_SELECT (0x29) - - { KEY_PRINT, VK_PRINT }, // (0x2A) - - //VK_EXECUTE (0x2B) - - { KEY_PRINT, VK_SNAPSHOT }, // (0x2C) - - { KEY_INSERT, VK_INSERT }, // (0x2D) - - { KEY_DELETE, VK_DELETE }, // (0x2E) - - { KEY_HELP, VK_HELP }, // (0x2F) - - { KEY_0, (0x30) }, ////0 key - { KEY_1, (0x31) }, ////1 key - { KEY_2, (0x32) }, ////2 key - { KEY_3, (0x33) }, ////3 key - { KEY_4, (0x34) }, ////4 key - { KEY_5, (0x35) }, ////5 key - { KEY_6, (0x36) }, ////6 key - { KEY_7, (0x37) }, ////7 key - { KEY_8, (0x38) }, ////8 key - { KEY_9, (0x39) }, ////9 key - { KEY_A, (0x41) }, ////A key - { KEY_B, (0x42) }, ////B key - { KEY_C, (0x43) }, ////C key - { KEY_D, (0x44) }, ////D key - { KEY_E, (0x45) }, ////E key - { KEY_F, (0x46) }, ////F key - { KEY_G, (0x47) }, ////G key - { KEY_H, (0x48) }, ////H key - { KEY_I, (0x49) }, ////I key - { KEY_J, (0x4A) }, ////J key - { KEY_K, (0x4B) }, ////K key - { KEY_L, (0x4C) }, ////L key - { KEY_M, (0x4D) }, ////M key - { KEY_N, (0x4E) }, ////N key - { KEY_O, (0x4F) }, ////O key - { KEY_P, (0x50) }, ////P key - { KEY_Q, (0x51) }, ////Q key - { KEY_R, (0x52) }, ////R key - { KEY_S, (0x53) }, ////S key - { KEY_T, (0x54) }, ////T key - { KEY_U, (0x55) }, ////U key - { KEY_V, (0x56) }, ////V key - { KEY_W, (0x57) }, ////W key - { KEY_X, (0x58) }, ////X key - { KEY_Y, (0x59) }, ////Y key - { KEY_Z, (0x5A) }, ////Z key + { Key::CLEAR, VK_ICO_CLEAR }, // (0xE6) + // OEM (ICO) clear key. Map to clear. - { KEY_MASK_META, VK_LWIN }, //(0x5B) - { KEY_MASK_META, VK_RWIN }, //(0x5C) - { KEY_MENU, VK_APPS }, //(0x5D) - { KEY_STANDBY, VK_SLEEP }, //(0x5F) - { KEY_KP_0, VK_NUMPAD0 }, //(0x60) - { KEY_KP_1, VK_NUMPAD1 }, //(0x61) - { KEY_KP_2, VK_NUMPAD2 }, //(0x62) - { KEY_KP_3, VK_NUMPAD3 }, //(0x63) - { KEY_KP_4, VK_NUMPAD4 }, //(0x64) - { KEY_KP_5, VK_NUMPAD5 }, //(0x65) - { KEY_KP_6, VK_NUMPAD6 }, //(0x66) - { KEY_KP_7, VK_NUMPAD7 }, //(0x67) - { KEY_KP_8, VK_NUMPAD8 }, //(0x68) - { KEY_KP_9, VK_NUMPAD9 }, //(0x69) - { KEY_KP_MULTIPLY, VK_MULTIPLY }, // (0x6A) - { KEY_KP_ADD, VK_ADD }, // (0x6B) - //VK_SEPARATOR (0x6C) - { KEY_KP_SUBTRACT, VK_SUBTRACT }, // (0x6D) - { KEY_KP_PERIOD, VK_DECIMAL }, // (0x6E) - { KEY_KP_DIVIDE, VK_DIVIDE }, // (0x6F) - { KEY_F1, VK_F1 }, // (0x70) - { KEY_F2, VK_F2 }, // (0x71) - { KEY_F3, VK_F3 }, // (0x72) - { KEY_F4, VK_F4 }, // (0x73) - { KEY_F5, VK_F5 }, // (0x74) - { KEY_F6, VK_F6 }, // (0x75) - { KEY_F7, VK_F7 }, // (0x76) - { KEY_F8, VK_F8 }, // (0x77) - { KEY_F9, VK_F9 }, // (0x78) - { KEY_F10, VK_F10 }, // (0x79) - { KEY_F11, VK_F11 }, // (0x7A) - { KEY_F12, VK_F12 }, // (0x7B) - { KEY_F13, VK_F13 }, // (0x7C) - { KEY_F14, VK_F14 }, // (0x7D) - { KEY_F15, VK_F15 }, // (0x7E) - { KEY_F16, VK_F16 }, // (0x7F) - { KEY_NUMLOCK, VK_NUMLOCK }, // (0x90) - { KEY_SCROLLLOCK, VK_SCROLL }, // (0x91) - { KEY_SHIFT, VK_LSHIFT }, // (0xA0) - { KEY_SHIFT, VK_RSHIFT }, // (0xA1) - { KEY_CTRL, VK_LCONTROL }, // (0xA2) - { KEY_CTRL, VK_RCONTROL }, // (0xA3) - { KEY_MENU, VK_LMENU }, // (0xA4) - { KEY_MENU, VK_RMENU }, // (0xA5) - - { KEY_BACK, VK_BROWSER_BACK }, // (0xA6) - - { KEY_FORWARD, VK_BROWSER_FORWARD }, // (0xA7) - - { KEY_REFRESH, VK_BROWSER_REFRESH }, // (0xA8) - - { KEY_STOP, VK_BROWSER_STOP }, // (0xA9) - - { KEY_SEARCH, VK_BROWSER_SEARCH }, // (0xAA) - - { KEY_FAVORITES, VK_BROWSER_FAVORITES }, // (0xAB) - - { KEY_HOMEPAGE, VK_BROWSER_HOME }, // (0xAC) - - { KEY_VOLUMEMUTE, VK_VOLUME_MUTE }, // (0xAD) - - { KEY_VOLUMEDOWN, VK_VOLUME_DOWN }, // (0xAE) - - { KEY_VOLUMEUP, VK_VOLUME_UP }, // (0xAF) - - { KEY_MEDIANEXT, VK_MEDIA_NEXT_TRACK }, // (0xB0) - - { KEY_MEDIAPREVIOUS, VK_MEDIA_PREV_TRACK }, // (0xB1) - - { KEY_MEDIASTOP, VK_MEDIA_STOP }, // (0xB2) - - //VK_MEDIA_PLAY_PAUSE (0xB3) - - { KEY_LAUNCHMAIL, VK_LAUNCH_MAIL }, // (0xB4) - - { KEY_LAUNCHMEDIA, VK_LAUNCH_MEDIA_SELECT }, // (0xB5) - - { KEY_LAUNCH0, VK_LAUNCH_APP1 }, // (0xB6) - - { KEY_LAUNCH1, VK_LAUNCH_APP2 }, // (0xB7) - - { KEY_SEMICOLON, VK_OEM_1 }, // (0xBA) - - { KEY_EQUAL, VK_OEM_PLUS }, // (0xBB) // Windows 2000/XP: For any country/region, the '+' key - { KEY_COMMA, VK_OEM_COMMA }, // (0xBC) // Windows 2000/XP: For any country/region, the ',' key - { KEY_MINUS, VK_OEM_MINUS }, // (0xBD) // Windows 2000/XP: For any country/region, the '-' key - { KEY_PERIOD, VK_OEM_PERIOD }, // (0xBE) // Windows 2000/XP: For any country/region, the '.' key - { KEY_SLASH, VK_OEM_2 }, // (0xBF) //Windows 2000/XP: For the US standard keyboard, the '/?' key - - { KEY_QUOTELEFT, VK_OEM_3 }, // (0xC0) - { KEY_BRACELEFT, VK_OEM_4 }, // (0xDB) - { KEY_BACKSLASH, VK_OEM_5 }, // (0xDC) - { KEY_BRACERIGHT, VK_OEM_6 }, // (0xDD) - { KEY_APOSTROPHE, VK_OEM_7 }, // (0xDE) - /* -{VK_OEM_8 (0xDF) -{VK_OEM_102 (0xE2) // Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard -*/ - //{ KEY_PLAY, VK_PLAY},// (0xFA) - - { KEY_UNKNOWN, 0 } -}; + // VK_PACKET (0xE7) + // Used to pass Unicode characters as if they were keystrokes. + // See Win32 API docs. We have no mapping. + + // 0xE8 is unassigned, 0xE9-F5 are OEM (Nokia/Ericsson) specific, we have no mappings. + + { Key::ESCAPE, VK_ATTN }, // (0xF6) + // Old IBM 'ATTN' key used on midrange computers, e.g. AS/400, map to Escape. + + { Key::TAB, VK_CRSEL }, // (0xF7) + // Old IBM 3270 'CrSel' (cursor select) key, used to select data fields, map to Tab. + + // VK_EXSEL (0xF7) + // Old IBM 3270 extended selection key. No mapping. + + // VK_EREOF (0xF8) + // Old IBM 3270 erase to end of field key. No mapping. -/* -VK_ZOOM (0xFB) -VK_NONAME (0xFC) -VK_PA1 (0xFD) -VK_OEM_CLEAR (0xFE) -*/ + { Key::MEDIAPLAY, VK_PLAY }, // (0xFA) + // Old IBM 3270 'Play' key. Map to media play. + + // VK_ZOOM (0xFB) + // Old IBM 3290 'Zoom' key. No mapping. + + // VK_NONAME (0xFC) + // Reserved. No mapping. + + // VK_PA1 (0xFD) + // Old IBM 3270 PA1 key. No mapping. + + { Key::CLEAR, VK_OEM_CLEAR }, // (0xFE) + // OEM specific clear key. Unclear how it differs from normal clear. Map to clear. + + { Key::UNKNOWN, 0 } +}; static _WinTranslatePair _scancode_to_keycode[] = { - { KEY_ESCAPE, 0x01 }, - { KEY_1, 0x02 }, - { KEY_2, 0x03 }, - { KEY_3, 0x04 }, - { KEY_4, 0x05 }, - { KEY_5, 0x06 }, - { KEY_6, 0x07 }, - { KEY_7, 0x08 }, - { KEY_8, 0x09 }, - { KEY_9, 0x0A }, - { KEY_0, 0x0B }, - { KEY_MINUS, 0x0C }, - { KEY_EQUAL, 0x0D }, - { KEY_BACKSPACE, 0x0E }, - { KEY_TAB, 0x0F }, - { KEY_Q, 0x10 }, - { KEY_W, 0x11 }, - { KEY_E, 0x12 }, - { KEY_R, 0x13 }, - { KEY_T, 0x14 }, - { KEY_Y, 0x15 }, - { KEY_U, 0x16 }, - { KEY_I, 0x17 }, - { KEY_O, 0x18 }, - { KEY_P, 0x19 }, - { KEY_BRACELEFT, 0x1A }, - { KEY_BRACERIGHT, 0x1B }, - { KEY_ENTER, 0x1C }, - { KEY_CTRL, 0x1D }, - { KEY_A, 0x1E }, - { KEY_S, 0x1F }, - { KEY_D, 0x20 }, - { KEY_F, 0x21 }, - { KEY_G, 0x22 }, - { KEY_H, 0x23 }, - { KEY_J, 0x24 }, - { KEY_K, 0x25 }, - { KEY_L, 0x26 }, - { KEY_SEMICOLON, 0x27 }, - { KEY_APOSTROPHE, 0x28 }, - { KEY_QUOTELEFT, 0x29 }, - { KEY_SHIFT, 0x2A }, - { KEY_BACKSLASH, 0x2B }, - { KEY_Z, 0x2C }, - { KEY_X, 0x2D }, - { KEY_C, 0x2E }, - { KEY_V, 0x2F }, - { KEY_B, 0x30 }, - { KEY_N, 0x31 }, - { KEY_M, 0x32 }, - { KEY_COMMA, 0x33 }, - { KEY_PERIOD, 0x34 }, - { KEY_SLASH, 0x35 }, - { KEY_SHIFT, 0x36 }, - { KEY_PRINT, 0x37 }, - { KEY_ALT, 0x38 }, - { KEY_SPACE, 0x39 }, - { KEY_CAPSLOCK, 0x3A }, - { KEY_F1, 0x3B }, - { KEY_F2, 0x3C }, - { KEY_F3, 0x3D }, - { KEY_F4, 0x3E }, - { KEY_F5, 0x3F }, - { KEY_F6, 0x40 }, - { KEY_F7, 0x41 }, - { KEY_F8, 0x42 }, - { KEY_F9, 0x43 }, - { KEY_F10, 0x44 }, - { KEY_NUMLOCK, 0x45 }, - { KEY_SCROLLLOCK, 0x46 }, - { KEY_HOME, 0x47 }, - { KEY_UP, 0x48 }, - { KEY_PAGEUP, 0x49 }, - { KEY_KP_SUBTRACT, 0x4A }, - { KEY_LEFT, 0x4B }, - { KEY_KP_5, 0x4C }, - { KEY_RIGHT, 0x4D }, - { KEY_KP_ADD, 0x4E }, - { KEY_END, 0x4F }, - { KEY_DOWN, 0x50 }, - { KEY_PAGEDOWN, 0x51 }, - { KEY_INSERT, 0x52 }, - { KEY_DELETE, 0x53 }, - //{ KEY_???, 0x56 }, //NON US BACKSLASH - { KEY_F11, 0x57 }, - { KEY_F12, 0x58 }, - { KEY_META, 0x5B }, - { KEY_META, 0x5C }, - { KEY_MENU, 0x5D }, - { KEY_F13, 0x64 }, - { KEY_F14, 0x65 }, - { KEY_F15, 0x66 }, - { KEY_F16, 0x67 }, - { KEY_UNKNOWN, 0 } + { Key::ESCAPE, 0x01 }, + { Key::KEY_1, 0x02 }, + { Key::KEY_2, 0x03 }, + { Key::KEY_3, 0x04 }, + { Key::KEY_4, 0x05 }, + { Key::KEY_5, 0x06 }, + { Key::KEY_6, 0x07 }, + { Key::KEY_7, 0x08 }, + { Key::KEY_8, 0x09 }, + { Key::KEY_9, 0x0A }, + { Key::KEY_0, 0x0B }, + { Key::MINUS, 0x0C }, + { Key::EQUAL, 0x0D }, + { Key::BACKSPACE, 0x0E }, + { Key::TAB, 0x0F }, + { Key::Q, 0x10 }, + { Key::W, 0x11 }, + { Key::E, 0x12 }, + { Key::R, 0x13 }, + { Key::T, 0x14 }, + { Key::Y, 0x15 }, + { Key::U, 0x16 }, + { Key::I, 0x17 }, + { Key::O, 0x18 }, + { Key::P, 0x19 }, + { Key::BRACELEFT, 0x1A }, + { Key::BRACERIGHT, 0x1B }, + { Key::ENTER, 0x1C }, + { Key::CTRL, 0x1D }, + { Key::A, 0x1E }, + { Key::S, 0x1F }, + { Key::D, 0x20 }, + { Key::F, 0x21 }, + { Key::G, 0x22 }, + { Key::H, 0x23 }, + { Key::J, 0x24 }, + { Key::K, 0x25 }, + { Key::L, 0x26 }, + { Key::SEMICOLON, 0x27 }, + { Key::APOSTROPHE, 0x28 }, + { Key::QUOTELEFT, 0x29 }, + { Key::SHIFT, 0x2A }, + { Key::BACKSLASH, 0x2B }, + { Key::Z, 0x2C }, + { Key::X, 0x2D }, + { Key::C, 0x2E }, + { Key::V, 0x2F }, + { Key::B, 0x30 }, + { Key::N, 0x31 }, + { Key::M, 0x32 }, + { Key::COMMA, 0x33 }, + { Key::PERIOD, 0x34 }, + { Key::SLASH, 0x35 }, + { Key::SHIFT, 0x36 }, + { Key::PRINT, 0x37 }, + { Key::ALT, 0x38 }, + { Key::SPACE, 0x39 }, + { Key::CAPSLOCK, 0x3A }, + { Key::F1, 0x3B }, + { Key::F2, 0x3C }, + { Key::F3, 0x3D }, + { Key::F4, 0x3E }, + { Key::F5, 0x3F }, + { Key::F6, 0x40 }, + { Key::F7, 0x41 }, + { Key::F8, 0x42 }, + { Key::F9, 0x43 }, + { Key::F10, 0x44 }, + { Key::NUMLOCK, 0x45 }, + { Key::SCROLLLOCK, 0x46 }, + { Key::HOME, 0x47 }, + { Key::UP, 0x48 }, + { Key::PAGEUP, 0x49 }, + { Key::KP_SUBTRACT, 0x4A }, + { Key::LEFT, 0x4B }, + { Key::KP_5, 0x4C }, + { Key::RIGHT, 0x4D }, + { Key::KP_ADD, 0x4E }, + { Key::END, 0x4F }, + { Key::DOWN, 0x50 }, + { Key::PAGEDOWN, 0x51 }, + { Key::INSERT, 0x52 }, + { Key::KEY_DELETE, 0x53 }, + { Key::F11, 0x57 }, + { Key::F12, 0x58 }, + { Key::META, 0x5B }, + { Key::META, 0x5C }, + { Key::MENU, 0x5D }, + { Key::F13, 0x64 }, + { Key::F14, 0x65 }, + { Key::F15, 0x66 }, + { Key::F16, 0x67 }, + { Key::UNKNOWN, 0 } }; -unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) { - for (int i = 0; _vk_to_keycode[i].keysym != KEY_UNKNOWN; i++) { +Key KeyMappingWindows::get_keysym(unsigned int p_code) { + for (int i = 0; _vk_to_keycode[i].keysym != Key::UNKNOWN; i++) { if (_vk_to_keycode[i].keycode == p_code) { - //printf("outcode: %x\n",_vk_to_keycode[i].keysym); - return _vk_to_keycode[i].keysym; } } - return KEY_UNKNOWN; + return Key::UNKNOWN; } unsigned int KeyMappingWindows::get_scancode(Key p_keycode) { - for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) { + for (int i = 0; _scancode_to_keycode[i].keysym != Key::UNKNOWN; i++) { if (_scancode_to_keycode[i].keysym == p_keycode) { return _scancode_to_keycode[i].keycode; } @@ -355,9 +432,9 @@ unsigned int KeyMappingWindows::get_scancode(Key p_keycode) { return 0; } -unsigned int KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended) { - unsigned int keycode = KEY_UNKNOWN; - for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) { +Key KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended) { + Key keycode = Key::UNKNOWN; + for (int i = 0; _scancode_to_keycode[i].keysym != Key::UNKNOWN; i++) { if (_scancode_to_keycode[i].keycode == p_code) { keycode = _scancode_to_keycode[i].keysym; break; @@ -366,55 +443,55 @@ unsigned int KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended if (p_extended) { switch (keycode) { - case KEY_ENTER: { - keycode = KEY_KP_ENTER; + case Key::ENTER: { + keycode = Key::KP_ENTER; } break; - case KEY_SLASH: { - keycode = KEY_KP_DIVIDE; + case Key::SLASH: { + keycode = Key::KP_DIVIDE; } break; - case KEY_CAPSLOCK: { - keycode = KEY_KP_ADD; + case Key::CAPSLOCK: { + keycode = Key::KP_ADD; } break; default: break; } } else { switch (keycode) { - case KEY_NUMLOCK: { - keycode = KEY_PAUSE; + case Key::NUMLOCK: { + keycode = Key::PAUSE; } break; - case KEY_HOME: { - keycode = KEY_KP_7; + case Key::HOME: { + keycode = Key::KP_7; } break; - case KEY_UP: { - keycode = KEY_KP_8; + case Key::UP: { + keycode = Key::KP_8; } break; - case KEY_PAGEUP: { - keycode = KEY_KP_9; + case Key::PAGEUP: { + keycode = Key::KP_9; } break; - case KEY_LEFT: { - keycode = KEY_KP_4; + case Key::LEFT: { + keycode = Key::KP_4; } break; - case KEY_RIGHT: { - keycode = KEY_KP_6; + case Key::RIGHT: { + keycode = Key::KP_6; } break; - case KEY_END: { - keycode = KEY_KP_1; + case Key::END: { + keycode = Key::KP_1; } break; - case KEY_DOWN: { - keycode = KEY_KP_2; + case Key::DOWN: { + keycode = Key::KP_2; } break; - case KEY_PAGEDOWN: { - keycode = KEY_KP_3; + case Key::PAGEDOWN: { + keycode = Key::KP_3; } break; - case KEY_INSERT: { - keycode = KEY_KP_0; + case Key::INSERT: { + keycode = Key::KP_0; } break; - case KEY_DELETE: { - keycode = KEY_KP_PERIOD; + case Key::KEY_DELETE: { + keycode = Key::KP_PERIOD; } break; - case KEY_PRINT: { - keycode = KEY_KP_MULTIPLY; + case Key::PRINT: { + keycode = Key::KP_MULTIPLY; } break; default: break; diff --git a/platform/windows/key_mapping_windows.h b/platform/windows/key_mapping_windows.h index d056e88f06..393432fa39 100644 --- a/platform/windows/key_mapping_windows.h +++ b/platform/windows/key_mapping_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -41,9 +41,9 @@ class KeyMappingWindows { KeyMappingWindows() {} public: - static unsigned int get_keysym(unsigned int p_code); + static Key get_keysym(unsigned int p_code); static unsigned int get_scancode(Key p_keycode); - static unsigned int get_scansym(unsigned int p_code, bool p_extended); + static Key get_scansym(unsigned int p_code, bool p_extended); static bool is_extended_key(unsigned int p_code); }; diff --git a/platform/windows/lang_table.h b/platform/windows/lang_table.h index 51583cc11e..5b022853e8 100644 --- a/platform/windows/lang_table.h +++ b/platform/windows/lang_table.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 2c21390d73..b4669e452a 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -46,14 +46,13 @@ #include "windows_terminal_logger.h" #include <avrt.h> +#include <bcrypt.h> #include <direct.h> #include <knownfolders.h> #include <process.h> #include <regstr.h> #include <shlobj.h> -static const WORD MAX_CONSOLE_LINES = 1500; - extern "C" { __declspec(dllexport) DWORD NvOptimusEnablement = 1; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; @@ -85,71 +84,32 @@ static String format_error_message(DWORD id) { return msg; } -void RedirectIOToConsole() { - int hConHandle; - - intptr_t lStdHandle; - - CONSOLE_SCREEN_BUFFER_INFO coninfo; - - FILE *fp; - - // allocate a console for this app - - AllocConsole(); - - // set the screen buffer to be big enough to let us scroll text - - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); - - coninfo.dwSize.Y = MAX_CONSOLE_LINES; - - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); - - // redirect unbuffered STDOUT to the console - - lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE); - - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - - fp = _fdopen(hConHandle, "w"); - - *stdout = *fp; - - setvbuf(stdout, nullptr, _IONBF, 0); - - // redirect unbuffered STDIN to the console - - lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE); - - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - - fp = _fdopen(hConHandle, "r"); - - *stdin = *fp; - - setvbuf(stdin, nullptr, _IONBF, 0); - - // redirect unbuffered STDERR to the console - - lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE); - - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - - fp = _fdopen(hConHandle, "w"); - - *stderr = *fp; - - setvbuf(stderr, nullptr, _IONBF, 0); +void RedirectStream(const char *p_file_name, const char *p_mode, FILE *p_cpp_stream, const DWORD p_std_handle) { + const HANDLE h_existing = GetStdHandle(p_std_handle); + if (h_existing != INVALID_HANDLE_VALUE) { // Redirect only if attached console have a valid handle. + const HANDLE h_cpp = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(p_cpp_stream))); + if (h_cpp == INVALID_HANDLE_VALUE) { // Redirect only if it's not already redirected to the pipe or file. + FILE *fp = p_cpp_stream; + freopen_s(&fp, p_file_name, p_mode, p_cpp_stream); // Redirect stream. + setvbuf(p_cpp_stream, nullptr, _IONBF, 0); // Disable stream buffering. + } + } +} - // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog +void RedirectIOToConsole() { + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + RedirectStream("CONIN$", "r", stdin, STD_INPUT_HANDLE); + RedirectStream("CONOUT$", "w", stdout, STD_OUTPUT_HANDLE); + RedirectStream("CONOUT$", "w", stderr, STD_ERROR_HANDLE); - // point to console as well + printf("\n"); // Make sure our output is starting from the new line. + } } BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) { - if (!EngineDebugger::is_active()) + if (!EngineDebugger::is_active()) { return FALSE; + } switch (dwCtrlType) { case CTRL_C_EVENT: @@ -172,7 +132,9 @@ void OS_Windows::initialize_debugging() { void OS_Windows::initialize() { crash_handler.initialize(); - //RedirectIOToConsole(); +#ifndef WINDOWS_SUBSYSTEM_CONSOLE + RedirectIOToConsole(); +#endif FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA); @@ -184,12 +146,8 @@ void OS_Windows::initialize() { NetSocketPosix::make_default(); // We need to know how often the clock is updated - if (!QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second)) - ticks_per_second = 1000; - // If timeAtGameStart is 0 then we get the time since - // the start of the computer when we call GetGameTime() - ticks_start = 0; - ticks_start = get_ticks_usec(); + QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second); + QueryPerformanceCounter((LARGE_INTEGER *)&ticks_start); // set minimum resolution for periodic timers, otherwise Sleep(n) may wait at least as // long as the windows scheduler resolution (~16-30ms) even for calls like Sleep(1) @@ -209,8 +167,9 @@ void OS_Windows::initialize() { } void OS_Windows::delete_main_loop() { - if (main_loop) + if (main_loop) { memdelete(main_loop); + } main_loop = nullptr; } @@ -223,8 +182,9 @@ void OS_Windows::finalize() { driver_midi.close(); #endif - if (main_loop) + if (main_loop) { memdelete(main_loop); + } main_loop = nullptr; } @@ -236,6 +196,12 @@ void OS_Windows::finalize_core() { NetSocketPosix::cleanup(); } +Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) { + NTSTATUS status = BCryptGenRandom(nullptr, r_buffer, p_bytes, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + ERR_FAIL_COND_V(status, FAILED); + return OK; +} + Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { String path = p_path.replace("/", "\\"); @@ -298,12 +264,19 @@ OS::Date OS_Windows::get_date(bool p_utc) const { GetLocalTime(&systemtime); } + //Get DST information from Windows, but only if p_utc is false. + TIME_ZONE_INFORMATION info; + bool daylight = false; + if (!p_utc && GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) { + daylight = true; + } + Date date; date.day = systemtime.wDay; date.month = Month(systemtime.wMonth); date.weekday = Weekday(systemtime.wDayOfWeek); date.year = systemtime.wYear; - date.dst = false; + date.dst = daylight; return date; } @@ -325,19 +298,23 @@ OS::Time OS_Windows::get_time(bool p_utc) const { OS::TimeZoneInfo OS_Windows::get_time_zone_info() const { TIME_ZONE_INFORMATION info; bool daylight = false; - if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) + if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) { daylight = true; + } + // Daylight Bias needs to be added to the bias if DST is in effect, or else it will not properly update. TimeZoneInfo ret; if (daylight) { ret.name = info.DaylightName; + ret.bias = info.Bias + info.DaylightBias; } else { ret.name = info.StandardName; + ret.bias = info.Bias + info.StandardBias; } // Bias value returned by GetTimeZoneInformation is inverted of what we expect // For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180 - ret.bias = -info.Bias; + ret.bias = -ret.bias; return ret; } @@ -359,18 +336,21 @@ double OS_Windows::get_unix_time() const { } void OS_Windows::delay_usec(uint32_t p_usec) const { - if (p_usec < 1000) + if (p_usec < 1000) { Sleep(1); - else + } else { Sleep(p_usec / 1000); + } } uint64_t OS_Windows::get_ticks_usec() const { uint64_t ticks; // This is the number of clock ticks since start - if (!QueryPerformanceCounter((LARGE_INTEGER *)&ticks)) - ticks = (UINT64)timeGetTime(); + QueryPerformanceCounter((LARGE_INTEGER *)&ticks); + // Subtract the ticks at game start to get + // the ticks since the game started + ticks -= ticks_start; // Divide by frequency to get the time in seconds // original calculation shown below is subject to overflow @@ -390,9 +370,6 @@ uint64_t OS_Windows::get_ticks_usec() const { // seconds time += seconds * 1000000L; - // Subtract the time at game start to get - // the time since the game started - time -= ticks_start; return time; } @@ -406,63 +383,87 @@ String OS_Windows::_quote_command_line_argument(const String &p_text) const { return p_text; } -Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { +Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { String path = p_path.replace("/", "\\"); String command = _quote_command_line_argument(path); for (const String &E : p_arguments) { command += " " + _quote_command_line_argument(E); } + ProcessInfo pi; + ZeroMemory(&pi.si, sizeof(pi.si)); + pi.si.cb = sizeof(pi.si); + ZeroMemory(&pi.pi, sizeof(pi.pi)); + LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si; + + bool inherit_handles = false; + HANDLE pipe[2] = { nullptr, nullptr }; if (r_pipe) { + // Create pipe for StdOut and StdErr. + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = true; + sa.lpSecurityDescriptor = nullptr; + + ERR_FAIL_COND_V(!CreatePipe(&pipe[0], &pipe[1], &sa, 0), ERR_CANT_FORK); + ERR_FAIL_COND_V(!SetHandleInformation(pipe[0], HANDLE_FLAG_INHERIT, 0), ERR_CANT_FORK); // Read handle is for host process only and should not be inherited. + + pi.si.dwFlags |= STARTF_USESTDHANDLES; + pi.si.hStdOutput = pipe[1]; if (read_stderr) { - command += " 2>&1"; // Include stderr + pi.si.hStdError = pipe[1]; } - // Add extra quotes around the full command, to prevent it from stripping quotes in the command, - // because _wpopen calls command as "cmd.exe /c command", instead of executing it directly - command = _quote_command_line_argument(command); - - FILE *f = _wpopen((LPCWSTR)(command.utf16().get_data()), L"r"); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot create pipe from command: " + command); - char buf[65535]; - while (fgets(buf, 65535, f)) { + inherit_handles = true; + } + DWORD creation_flags = NORMAL_PRIORITY_CLASS; + if (p_open_console) { + creation_flags |= CREATE_NEW_CONSOLE; + } else { + creation_flags |= CREATE_NO_WINDOW; + } + + int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, inherit_handles, creation_flags, nullptr, nullptr, si_w, &pi.pi); + if (!ret && r_pipe) { + CloseHandle(pipe[0]); // Cleanup pipe handles. + CloseHandle(pipe[1]); + } + ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command); + + if (r_pipe) { + CloseHandle(pipe[1]); // Close pipe write handle (only child process is writing). + char buf[4096]; + DWORD read = 0; + for (;;) { // Read StdOut and StdErr from pipe. + bool success = ReadFile(pipe[0], buf, 4096, &read, NULL); + if (!success || read == 0) { + break; + } if (p_pipe_mutex) { p_pipe_mutex->lock(); } - (*r_pipe) += String::utf8(buf); + (*r_pipe) += String::utf8(buf, read); if (p_pipe_mutex) { p_pipe_mutex->unlock(); } } - int rv = _pclose(f); - - if (r_exitcode) { - *r_exitcode = rv; - } - return OK; + CloseHandle(pipe[0]); // Close pipe read handle. + } else { + WaitForSingleObject(pi.pi.hProcess, INFINITE); } - ProcessInfo pi; - ZeroMemory(&pi.si, sizeof(pi.si)); - pi.si.cb = sizeof(pi.si); - ZeroMemory(&pi.pi, sizeof(pi.pi)); - LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si; - - int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi); - ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command); - - WaitForSingleObject(pi.pi.hProcess, INFINITE); if (r_exitcode) { DWORD ret2; GetExitCodeProcess(pi.pi.hProcess, &ret2); *r_exitcode = ret2; } + CloseHandle(pi.pi.hProcess); CloseHandle(pi.pi.hThread); return OK; -}; +} -Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) { +Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) { String path = p_path.replace("/", "\\"); String command = _quote_command_line_argument(path); for (const String &E : p_arguments) { @@ -475,7 +476,14 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg ZeroMemory(&pi.pi, sizeof(pi.pi)); LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si; - int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi); + DWORD creation_flags = NORMAL_PRIORITY_CLASS; + if (p_open_console) { + creation_flags |= CREATE_NEW_CONSOLE; + } else { + creation_flags |= CREATE_NO_WINDOW; + } + + int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, creation_flags, nullptr, nullptr, si_w, &pi.pi); ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command); ProcessID pid = pi.pi.dwProcessId; @@ -485,7 +493,7 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg process_map->insert(pid, pi); return OK; -}; +} Error OS_Windows::kill(const ProcessID &p_pid) { ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED); @@ -499,15 +507,16 @@ Error OS_Windows::kill(const ProcessID &p_pid) { CloseHandle(pi.hThread); return ret != 0 ? OK : FAILED; -}; +} int OS_Windows::get_process_id() const { return _getpid(); } Error OS_Windows::set_cwd(const String &p_cwd) { - if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) + if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) { return ERR_CANT_OPEN; + } return OK; } @@ -530,7 +539,7 @@ bool OS_Windows::has_environment(const String &p_var) const { free(env); return has_env; #endif -}; +} String OS_Windows::get_environment(const String &p_var) const { WCHAR wval[0x7fff]; // MSDN says 32767 char is the maximum @@ -549,7 +558,7 @@ String OS_Windows::get_stdin_string(bool p_block) { if (p_block) { char buff[1024]; return fgets(buff, 1024, stdin); - }; + } return String(); } @@ -587,17 +596,20 @@ String OS_Windows::get_locale() const { int sublang = SUBLANGID(langid); while (wl->locale) { - if (wl->main_lang == lang && wl->sublang == SUBLANG_NEUTRAL) + if (wl->main_lang == lang && wl->sublang == SUBLANG_NEUTRAL) { neutral = wl->locale; + } - if (lang == wl->main_lang && sublang == wl->sublang) + if (lang == wl->main_lang && sublang == wl->sublang) { return String(wl->locale).replace("-", "_"); + } wl++; } - if (neutral != "") + if (!neutral.is_empty()) { return String(neutral).replace("-", "_"); + } return "en"; } @@ -624,25 +636,48 @@ BOOL is_wow64() { int OS_Windows::get_processor_count() const { SYSTEM_INFO sysinfo; - if (is_wow64()) + if (is_wow64()) { GetNativeSystemInfo(&sysinfo); - else + } else { GetSystemInfo(&sysinfo); + } return sysinfo.dwNumberOfProcessors; } +String OS_Windows::get_processor_name() const { + const String id = "Hardware\\Description\\System\\CentralProcessor\\0"; + + HKEY hkey; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(id.utf16().get_data()), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { + ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string.")); + } + + WCHAR buffer[256]; + DWORD buffer_len = 256; + DWORD vtype = REG_SZ; + if (RegQueryValueExW(hkey, L"ProcessorNameString", NULL, &vtype, (LPBYTE)buffer, &buffer_len) == ERROR_SUCCESS) { + RegCloseKey(hkey); + return String::utf16((const char16_t *)buffer, buffer_len).strip_edges(); + } else { + RegCloseKey(hkey); + ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string.")); + } +} + void OS_Windows::run() { - if (!main_loop) + if (!main_loop) { return; + } main_loop->initialize(); while (!force_quit) { DisplayServer::get_singleton()->process_events(); // get rid of pending events - if (Main::iteration()) + if (Main::iteration()) { break; - }; + } + } main_loop->finalize(); } @@ -747,11 +782,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")); - if (appname != "") { + if (!appname.is_empty()) { bool use_custom_dir = ProjectSettings::get_singleton()->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); - if (custom_dir == "") { + if (custom_dir.is_empty()) { custom_dir = appname; } return get_data_path().plus_file(custom_dir).replace("\\", "/"); @@ -760,7 +795,7 @@ String OS_Windows::get_user_data_dir() const { } } - return ProjectSettings::get_singleton()->get_resource_path(); + return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file("[unnamed project]"); } String OS_Windows::get_unique_id() const { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 1342d95575..adeecf37c5 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -40,8 +40,6 @@ #include "drivers/winmidi/midi_driver_winmidi.h" #include "key_mapping_windows.h" #include "servers/audio_server.h" -#include "servers/rendering/renderer_compositor.h" -#include "servers/rendering_server.h" #ifdef XAUDIO2_ENABLED #include "drivers/xaudio2/audio_driver_xaudio2.h" #endif @@ -62,14 +60,14 @@ class JoypadWindows; class OS_Windows : public OS { #ifdef STDOUT_FILE - FILE *stdo; + FILE *stdo = nullptr; #endif uint64_t ticks_start; uint64_t ticks_per_second; HINSTANCE hInstance; - MainLoop *main_loop; + MainLoop *main_loop = nullptr; #ifdef WASAPI_ENABLED AudioDriverWASAPI driver_wasapi; @@ -108,6 +106,8 @@ protected: public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; + virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override; virtual Error close_dynamic_library(void *p_library_handle) override; virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override; @@ -128,8 +128,8 @@ public: virtual void delay_usec(uint32_t p_usec) const override; virtual uint64_t get_ticks_usec() const override; - virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override; - virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override; + virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override; + virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override; virtual Error kill(const ProcessID &p_pid) override; virtual int get_process_id() const override; @@ -142,6 +142,7 @@ public: virtual String get_locale() const override; virtual int get_processor_count() const override; + virtual String get_processor_name() const override; virtual String get_config_path() const override; virtual String get_data_path() const override; diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h index dace0f86af..8e80f8cacb 100644 --- a/platform/windows/platform_config.h +++ b/platform/windows/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp index db5e6466be..07c41395fb 100644 --- a/platform/windows/vulkan_context_win.cpp +++ b/platform/windows/vulkan_context_win.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h index 61e66b8ae0..e68f0125ca 100644 --- a/platform/windows/vulkan_context_win.h +++ b/platform/windows/vulkan_context_win.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index e54a61fdfd..df21977698 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -44,25 +44,29 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er const unsigned int BUFFER_SIZE = 16384; char buf[BUFFER_SIZE + 1]; // +1 for the terminating character int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list); - if (len <= 0) + if (len <= 0) { return; - if ((unsigned int)len >= BUFFER_SIZE) + } + if ((unsigned int)len >= BUFFER_SIZE) { len = BUFFER_SIZE; // Output is too big, will be truncated + } buf[len] = 0; int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, nullptr, 0); - if (wlen < 0) + if (wlen < 0) { return; + } wchar_t *wbuf = (wchar_t *)memalloc((len + 1) * sizeof(wchar_t)); ERR_FAIL_NULL_MSG(wbuf, "Out of memory."); MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen); wbuf[wlen] = 0; - if (p_err) + if (p_err) { fwprintf(stderr, L"%ls", wbuf); - else + } else { wprintf(L"%ls", wbuf); + } memfree(wbuf); @@ -71,7 +75,7 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er #endif } -void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { +void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) { if (!should_log(true)) { return; } diff --git a/platform/windows/windows_terminal_logger.h b/platform/windows/windows_terminal_logger.h index aacfe5869e..1045f12201 100644 --- a/platform/windows/windows_terminal_logger.h +++ b/platform/windows/windows_terminal_logger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -37,8 +37,8 @@ class WindowsTerminalLogger : public StdLogger { public: - virtual void logv(const char *p_format, va_list p_list, bool p_err); - virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); + virtual void logv(const char *p_format, va_list p_list, bool p_err) override; + virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR) override; virtual ~WindowsTerminalLogger(); }; |