diff options
Diffstat (limited to 'platform/windows')
| -rw-r--r-- | platform/windows/crash_handler_windows.cpp | 30 | ||||
| -rw-r--r-- | platform/windows/detect.py | 20 | ||||
| -rw-r--r-- | platform/windows/display_server_windows.cpp | 620 | ||||
| -rw-r--r-- | platform/windows/display_server_windows.h | 29 | ||||
| -rw-r--r-- | platform/windows/export/export_plugin.cpp | 90 | ||||
| -rw-r--r-- | platform/windows/export/export_plugin.h | 10 | ||||
| -rw-r--r-- | platform/windows/gl_manager_windows.cpp | 39 | ||||
| -rw-r--r-- | platform/windows/godot.ico | bin | 110755 -> 359559 bytes | |||
| -rw-r--r-- | platform/windows/godot_windows.cpp | 12 | ||||
| -rw-r--r-- | platform/windows/joypad_windows.cpp | 90 | ||||
| -rw-r--r-- | platform/windows/joypad_windows.h | 5 | ||||
| -rw-r--r-- | platform/windows/key_mapping_windows.cpp | 317 | ||||
| -rw-r--r-- | platform/windows/os_windows.cpp | 299 | ||||
| -rw-r--r-- | platform/windows/os_windows.h | 10 | ||||
| -rw-r--r-- | platform/windows/windows_terminal_logger.cpp | 14 |
15 files changed, 1012 insertions, 573 deletions
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp index 71e9d9acbd..3b2c6fe9f6 100644 --- a/platform/windows/crash_handler_windows.cpp +++ b/platform/windows/crash_handler_windows.cpp @@ -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/detect.py b/platform/windows/detect.py index e9ecc99ef5..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"]) @@ -326,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"]) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index ca2b68371c..d243d4c05d 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -77,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: @@ -98,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); @@ -229,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); @@ -238,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); @@ -248,9 +250,9 @@ String DisplayServerWindows::clipboard_get() const { if (ptr != nullptr) { ret.parse_utf8((const char *)ptr); GlobalUnlock(mem); - }; - }; - }; + } + } + } CloseClipboard(); @@ -324,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) { @@ -361,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_ @@ -394,8 +422,9 @@ 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; } } @@ -444,6 +473,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 @@ -504,13 +540,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; } @@ -518,13 +566,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. } @@ -536,6 +585,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()) { @@ -571,6 +622,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_ @@ -675,8 +744,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 { @@ -747,6 +837,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) { + WindowData &wd_parent = windows[wd.transient_parent]; + if (wd.exclusive) { + 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_ @@ -769,7 +876,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"); @@ -778,7 +887,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); + } } } @@ -905,7 +1016,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 @@ -914,10 +1025,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) { @@ -929,14 +1044,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) { @@ -948,12 +1066,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; @@ -968,10 +1086,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) { @@ -1010,7 +1129,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); } @@ -1028,7 +1155,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); @@ -1039,6 +1166,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 { @@ -1048,7 +1184,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) { @@ -1082,6 +1222,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"); @@ -1095,6 +1236,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; } @@ -1121,6 +1267,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; } @@ -1149,7 +1298,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 { @@ -1196,8 +1347,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; @@ -1207,23 +1359,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; - } - if (!((OS_Windows *)OS::get_singleton())->_is_win11_terminal()) { - // GetConsoleWindow is not supported by the Windows Terminal. - 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_ @@ -1782,16 +1917,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; } @@ -1876,33 +2009,144 @@ 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) { + WindowData &wd = windows[p_window]; + if (wd.is_popup) { + // Close all popups, up to current popup parent, or every popup if new window is not transient. + List<WindowID>::Element *E = popup_list.back(); + while (E) { + if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) { + _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + List<WindowID>::Element *F = E->prev(); + popup_list.erase(E); + E = F; + } else { + break; + } + } + + time_since_popup = OS::get_singleton()->get_ticks_msec(); + popup_list.push_back(p_window); + } +} + +void DisplayServerWindows::popup_close(WindowID p_window) { + List<WindowID>::Element *E = popup_list.find(p_window); + while (E) { + _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + List<WindowID>::Element *F = E->next(); + popup_list.erase(E); + 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 *E = popup_list.back(); + 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 { + _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + List<WindowID>::Element *F = E->prev(); + popup_list.erase(E); + E = F; + } + } + + } 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 @@ -1913,7 +2157,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; @@ -1935,6 +2179,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; @@ -2018,8 +2269,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? @@ -2051,8 +2303,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; @@ -2078,8 +2331,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)); @@ -2148,8 +2400,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(); @@ -2183,8 +2436,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; @@ -2195,8 +2447,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; } @@ -2325,8 +2578,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; @@ -2337,7 +2589,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); } @@ -2426,8 +2678,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; @@ -2438,8 +2689,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: @@ -2585,8 +2837,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) { @@ -2620,98 +2873,78 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } 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 (context_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); @@ -2735,14 +2968,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) { @@ -2751,10 +2987,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: { @@ -2769,10 +3001,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; @@ -2800,7 +3034,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 { @@ -2813,12 +3047,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)) { @@ -2864,8 +3101,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); @@ -2873,10 +3110,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) { @@ -2886,6 +3124,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); @@ -2940,8 +3181,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 { @@ -2996,8 +3238,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)))); @@ -3053,7 +3296,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; @@ -3062,7 +3305,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++) { @@ -3105,7 +3348,7 @@ 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; } @@ -3240,7 +3483,6 @@ 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(); pressrc = 0; @@ -3302,7 +3544,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"; @@ -3353,11 +3595,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); @@ -3383,14 +3626,13 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } #endif - //set_ime_active(false); - if (!OS::get_singleton()->is_in_low_processor_usage_mode()) { 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. @@ -3442,12 +3684,16 @@ 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)) { diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 409335b41c..a56a2b83ac 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -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; + 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; @@ -414,7 +421,6 @@ class DisplayServerWindows : public DisplayServer { 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; @@ -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_plugin.cpp b/platform/windows/export/export_plugin.cpp index 02b2d026b5..7b9cb59896 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -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) { + FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V(!f, 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,39 @@ 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; } +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 +108,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"), "")); @@ -83,6 +121,7 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); 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; } @@ -321,3 +360,46 @@ 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; +} diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index 4ec9342cdf..b40e872461 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -34,18 +34,20 @@ #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 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; }; #endif diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp index 74b5f48502..a97fa99d7f 100644 --- a/platform/windows/gl_manager_windows.cpp +++ b/platform/windows/gl_manager_windows.cpp @@ -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 = {}; @@ -230,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); @@ -255,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; @@ -269,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; @@ -304,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) { @@ -319,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/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 7819ab9a32..ad4e3ae77c 100644 --- a/platform/windows/godot_windows.cpp +++ b/platform/windows/godot_windows.cpp @@ -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 8b6081d606..494e0b9105 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -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) { @@ -144,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]; @@ -196,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; @@ -227,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); @@ -237,8 +241,9 @@ 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); } @@ -268,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(); @@ -355,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(); @@ -404,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; } @@ -446,35 +454,29 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) { 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 4faefe932f..4f15bcf080 100644 --- a/platform/windows/joypad_windows.h +++ b/platform/windows/joypad_windows.h @@ -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; + } } }; @@ -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 938a777de6..e32dc0d1a6 100644 --- a/platform/windows/key_mapping_windows.cpp +++ b/platform/windows/key_mapping_windows.cpp @@ -32,117 +32,134 @@ #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 { Key keysym; unsigned int keycode; }; static _WinTranslatePair _vk_to_keycode[] = { - { Key::BACKSPACE, VK_BACK }, // (0x08) // backspace - { Key::TAB, VK_TAB }, //(0x09) - - //VK_CLEAR (0x0C) - - { Key::ENTER, VK_RETURN }, //(0x0D) - - { Key::SHIFT, VK_SHIFT }, //(0x10) - - { Key::CTRL, VK_CONTROL }, //(0x11) - - { Key::ALT, VK_MENU }, //(0x12) - - { Key::PAUSE, VK_PAUSE }, //(0x13) + // 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. - { Key::CAPSLOCK, VK_CAPITAL }, //(0x14) + // 0x07 is undefined. - { Key::ESCAPE, VK_ESCAPE }, //(0x1B) + { Key::BACKSPACE, VK_BACK }, // (0x08) + { Key::TAB, VK_TAB }, // (0x09) - { Key::SPACE, VK_SPACE }, //(0x20) + // 0x0A-0B are reserved. - { Key::PAGEUP, VK_PRIOR }, //(0x21) + { Key::CLEAR, VK_CLEAR }, // (0x0C) + { Key::ENTER, VK_RETURN }, // (0x0D) - { Key::PAGEDOWN, VK_NEXT }, //(0x22) + // 0x0E-0F are undefined. - { Key::END, VK_END }, //(0x23) + { 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::HOME, VK_HOME }, //(0x24) + // 0x15-1A are IME keys. We have no mapping. - { Key::LEFT, VK_LEFT }, //(0x25) + { Key::ESCAPE, VK_ESCAPE }, // (0x1B) - { Key::UP, VK_UP }, //(0x26) - - { Key::RIGHT, VK_RIGHT }, //(0x27) + // 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) + // 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) + // 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) - - { 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 - { 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) - { 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) + // 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) - //VK_SEPARATOR (0x6C) + { 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) @@ -162,8 +179,17 @@ static _WinTranslatePair _vk_to_keycode[] = { { 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) @@ -172,70 +198,124 @@ static _WinTranslatePair _vk_to_keycode[] = { { 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::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 the US standard keyboard, 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) - { Key::BRACELEFT, VK_OEM_4 }, // (0xDB) + // 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) - { Key::BRACERIGHT, VK_OEM_6 }, // (0xDD) + // 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) - /* -{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) + // 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. + + // 0xE4 is OEM (e.g. ICO) specific, we have no mapping. + + // VK_PROCESSKEY (0xE5) + // For IME, we have no mapping. + + { Key::CLEAR, VK_ICO_CLEAR }, // (0xE6) + // OEM (ICO) clear key. Map to clear. + + // 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. + + { 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 } }; -/* -VK_ZOOM (0xFB) -VK_NONAME (0xFC) -VK_PA1 (0xFD) -VK_OEM_CLEAR (0xFE) -*/ - static _WinTranslatePair _scancode_to_keycode[] = { { Key::ESCAPE, 0x01 }, { Key::KEY_1, 0x02 }, @@ -320,7 +400,6 @@ static _WinTranslatePair _scancode_to_keycode[] = { { Key::PAGEDOWN, 0x51 }, { Key::INSERT, 0x52 }, { Key::KEY_DELETE, 0x53 }, - //{ Key::???, 0x56 }, //NON US BACKSLASH { Key::F11, 0x57 }, { Key::F12, 0x58 }, { Key::META, 0x5B }, @@ -336,8 +415,6 @@ static _WinTranslatePair _scancode_to_keycode[] = { 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; } } diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 8477746001..b4669e452a 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -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,78 +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; - - DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS; -#ifndef DEBUG_ENABLED - dwCreationFlags |= CREATE_NO_WINDOW; -#endif - - int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, dwCreationFlags, 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; -}; - -bool OS_Windows::_is_win11_terminal() const { - HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD dwMode = 0; - if (GetConsoleMode(hStdOut, &dwMode)) { - return ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == ENABLE_VIRTUAL_TERMINAL_PROCESSING); - } else { - return false; - } } -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) { @@ -490,16 +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; - DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS; -#ifndef DEBUG_ENABLED - dwCreationFlags |= CREATE_NO_WINDOW; -#endif - if (p_path == get_executable_path() && GetConsoleWindow() != nullptr && _is_win11_terminal()) { - // Open a new terminal as a workaround for Windows Terminal bug. - dwCreationFlags |= CREATE_NEW_CONSOLE; + 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, dwCreationFlags, nullptr, nullptr, si_w, &pi.pi); + 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; @@ -509,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); @@ -523,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; } @@ -554,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 @@ -573,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(); } @@ -611,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.is_empty()) + if (!neutral.is_empty()) { return String(neutral).replace("-", "_"); + } return "en"; } @@ -648,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(); } diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 4e61f3be7e..5bfd24327e 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -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 @@ -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; @@ -157,7 +158,6 @@ public: void run(); - bool _is_win11_terminal() const; virtual bool _check_internal_feature_support(const String &p_feature) override; virtual void disable_crash_handler() override; diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index 0d5f0e617c..df21977698 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -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); |