diff options
Diffstat (limited to 'platform/windows/display_server_windows.cpp')
-rw-r--r-- | platform/windows/display_server_windows.cpp | 1571 |
1 files changed, 1021 insertions, 550 deletions
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e29faf4f3c..998b0882b3 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,7 +38,15 @@ #include <avrt.h> -#ifdef DEBUG_ENABLED +#if defined(GLES3_ENABLED) +#include "drivers/gles3/rasterizer_gles3.h" +#endif + +#if defined(__GNUC__) +// Workaround GCC warning from -Wcast-function-type. +#define GetProcAddress (void *)GetProcAddress +#endif + static String format_error_message(DWORD id) { LPWSTR messageBuffer = nullptr; size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, @@ -50,7 +58,15 @@ static String format_error_message(DWORD id) { return msg; } -#endif // DEBUG_ENABLED + +static void track_mouse_leave_event(HWND hWnd) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + tme.dwHoverTime = HOVER_DEFAULT; + TrackMouseEvent(&tme); +} bool DisplayServerWindows::has_feature(Feature p_feature) const { switch (p_feature) { @@ -61,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: @@ -69,6 +84,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const { case FEATURE_NATIVE_ICON: case FEATURE_SWAP_BUFFERS: case FEATURE_KEEP_SCREEN_ON: + case FEATURE_TEXT_TO_SPEECH: return true; default: return false; @@ -79,13 +95,13 @@ String DisplayServerWindows::get_name() const { return "Windows"; } -void DisplayServerWindows::alert(const String &p_alert, const String &p_title) { - MessageBoxW(nullptr, (LPCWSTR)(p_alert.utf16().get_data()), (LPCWSTR)(p_title.utf16().get_data()), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL); -} - void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { - if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED) { - WindowData &wd = windows[MAIN_WINDOW_ID]; + if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) { + // Mouse is grabbed (captured or confined). + + WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + + WindowData &wd = windows[window_id]; RECT clipRect; GetClientRect(wd.hWnd, &clipRect); @@ -100,11 +116,12 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { SetCapture(wd.hWnd); } } else { + // Mouse is free to move around (not captured or confined). ReleaseCapture(); ClipCursor(nullptr); } - if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) { + if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) { if (hCursor == nullptr) { hCursor = SetCursor(nullptr); } else { @@ -117,11 +134,48 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { } } +bool DisplayServerWindows::tts_is_speaking() const { + ERR_FAIL_COND_V(!tts, false); + return tts->is_speaking(); +} + +bool DisplayServerWindows::tts_is_paused() const { + ERR_FAIL_COND_V(!tts, false); + return tts->is_paused(); +} + +Array DisplayServerWindows::tts_get_voices() const { + ERR_FAIL_COND_V(!tts, Array()); + return tts->get_voices(); +} + +void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { + ERR_FAIL_COND(!tts); + tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt); +} + +void DisplayServerWindows::tts_pause() { + ERR_FAIL_COND(!tts); + tts->pause(); +} + +void DisplayServerWindows::tts_resume() { + ERR_FAIL_COND(!tts); + tts->resume(); +} + +void DisplayServerWindows::tts_stop() { + ERR_FAIL_COND(!tts); + tts->stop(); +} + void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) { _THREAD_SAFE_METHOD_ - if (mouse_mode == p_mode) + if (mouse_mode == p_mode) { + // Already in the same mode; do nothing. return; + } mouse_mode = p_mode; @@ -132,20 +186,20 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const { return mouse_mode; } -void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { +void DisplayServerWindows::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { - return; //no window focused? + return; // No focused window? } if (mouse_mode == MOUSE_MODE_CAPTURED) { - old_x = p_to.x; - old_y = p_to.y; + old_x = p_position.x; + old_y = p_position.y; } else { POINT p; - p.x = p_to.x; - p.y = p_to.y; + p.x = p_position.x; + p.y = p_position.y; ClientToScreen(windows[last_focused_window].hWnd, &p); SetCursorPos(p.x, p.y); @@ -156,10 +210,9 @@ Point2i DisplayServerWindows::mouse_get_position() const { POINT p; GetCursorPos(&p); return Point2i(p.x, p.y); - //return Point2(old_x, old_y); } -int DisplayServerWindows::mouse_get_button_state() const { +MouseButton DisplayServerWindows::mouse_get_button_state() const { return last_button_state; } @@ -167,12 +220,12 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { - return; //no window focused? + return; // No focused window? } - // Convert LF line endings to CRLF in clipboard content - // Otherwise, line endings won't be visible when pasted in other software - String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // avoid \r\r\n + // Convert LF line endings to CRLF in clipboard content. + // Otherwise, line endings won't be visible when pasted in other software. + String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // Avoid \r\r\n. if (!OpenClipboard(windows[last_focused_window].hWnd)) { ERR_FAIL_MSG("Unable to open clipboard."); @@ -189,7 +242,7 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { SetClipboardData(CF_UNICODETEXT, mem); - // set the CF_TEXT version (not needed?) + // Set the CF_TEXT version (not needed?). CharString utf8 = text.utf8(); mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1); ERR_FAIL_COND_MSG(mem == nullptr, "Unable to allocate memory for clipboard contents."); @@ -208,13 +261,13 @@ String DisplayServerWindows::clipboard_get() const { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { - return String(); //no window focused? + return String(); // No focused window? } 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); @@ -223,8 +276,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); @@ -233,9 +286,9 @@ String DisplayServerWindows::clipboard_get() const { if (ptr != nullptr) { ret.parse_utf8((const char *)ptr); GlobalUnlock(mem); - }; - }; - }; + } + } + } CloseClipboard(); @@ -309,6 +362,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) { @@ -332,7 +391,7 @@ static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonito EnumRectData *data = (EnumRectData *)dwData; if (data->count == data->screen) { MONITORINFO minfo; - zeromem(&minfo, sizeof(MONITORINFO)); + memset(&minfo, 0, sizeof(MONITORINFO)); minfo.cbSize = sizeof(MONITORINFO); GetMonitorInfoA(hMonitor, &minfo); @@ -346,6 +405,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_ @@ -379,16 +458,16 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : nullptr; if ((Shcore == nullptr) || (getDPIForMonitor == nullptr)) { - if (Shcore) + if (Shcore) { FreeLibrary(Shcore); + } Shcore = (HMODULE)INVALID_HANDLE_VALUE; } } UINT x = 0, y = 0; - HRESULT hr = E_FAIL; if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) { - hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y); + HRESULT hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y); if (SUCCEEDED(hr) && (x > 0) && (y > 0)) { dpiX = (int)x; dpiY = (int)y; @@ -429,6 +508,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 @@ -455,8 +541,8 @@ Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const { _THREAD_SAFE_METHOD_ Vector<DisplayServer::WindowID> ret; - for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - ret.push_back(E->key()); + for (const KeyValue<WindowID, WindowData> &E : windows) { + ret.push_back(E.key); } return ret; } @@ -466,19 +552,19 @@ DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(cons p.x = p_position.x; p.y = p_position.y; HWND hwnd = WindowFromPoint(p); - for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - if (E->get().hWnd == hwnd) { - return E->key(); + for (const KeyValue<WindowID, WindowData> &E : windows) { + if (E.value.hWnd == hwnd) { + return E.key; } } return INVALID_WINDOW_ID; } -DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { _THREAD_SAFE_METHOD_ - WindowID window_id = _create_window(p_mode, p_flags, p_rect); + WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect); ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window."); WindowData &wd = windows[window_id]; @@ -489,27 +575,45 @@ 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; } 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) { - SetForegroundWindow(wd.hWnd); // Slightly Higher Priority - SetFocus(wd.hWnd); // Sets Keyboard Focus To + if (wd.no_focus || wd.is_popup) { + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow + ShowWindow(wd.hWnd, SW_SHOWNA); + } else { + ShowWindow(wd.hWnd, SW_SHOW); + SetForegroundWindow(wd.hWnd); // Slightly higher priority. + SetFocus(wd.hWnd); // Set keyboard focus. } } @@ -519,10 +623,12 @@ 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()) { - window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID); + window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID); } if (wd.transient_parent != INVALID_WINDOW_ID) { @@ -530,10 +636,15 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { } #ifdef VULKAN_ENABLED - if (rendering_driver == "vulkan") { + if (context_vulkan) { context_vulkan->window_destroy(p_window); } #endif +#ifdef GLES3_ENABLED + if (gl_manager) { + gl_manager->window_destroy(p_window); + } +#endif if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window].wtctx) { wintab_WTClose(windows[p_window].wtctx); @@ -543,6 +654,30 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { windows.erase(p_window); } +void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) { +#if defined(GLES3_ENABLED) + gl_manager->window_make_current(p_window_id); +#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_ @@ -608,6 +743,8 @@ void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p } void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) { + ERR_FAIL_COND(!windows.has(p_window)); + if (windows[p_window].mpath.size() == 0) { SetWindowRgn(windows[p_window].hWnd, nullptr, TRUE); } else { @@ -645,8 +782,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 { @@ -666,16 +824,11 @@ Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { ClientToScreen(wd.hWnd, &point); return Point2i(point.x, point.y); - -#if 0 - //do not use this method, as it includes windows decorations - RECT r; - GetWindowRect(wd.hWnd, &r); - return Point2(r.left, r.top); -#endif } void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) { + ERR_FAIL_COND(!windows.has(p_window)); + POINT mouse_pos; if (GetCursorPos(&mouse_pos) && ScreenToClient(windows[p_window].hWnd, &mouse_pos)) { if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= windows[p_window].width && mouse_pos.y <= windows[p_window].height) { @@ -693,14 +846,9 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - if (wd.fullscreen) + if (wd.fullscreen) { return; -#if 0 - //wrong needs to account properly for decorations - RECT r; - GetWindowRect(wd.hWnd, &r); - MoveWindow(wd.hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE); -#else + } RECT rc; rc.left = p_position.x; @@ -713,9 +861,9 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window AdjustWindowRectEx(&rc, style, false, exStyle); MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); -#endif - // Don't let the mouse leave the window when moved - if (mouse_mode == MOUSE_MODE_CONFINED) { + + // Don't let the mouse leave the window when moved. + if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { RECT rect; GetClientRect(wd.hWnd, &rect); ClientToScreen(wd.hWnd, (POINT *)&rect.left); @@ -727,20 +875,36 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window _update_real_mouse_position(p_window); } +void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclusive) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + if (wd.exclusive != p_exclusive) { + wd.exclusive = p_exclusive; + if (wd.transient_parent != INVALID_WINDOW_ID) { + if (wd.exclusive) { + WindowData &wd_parent = windows[wd.transient_parent]; + SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd); + } else { + SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr); + } + } + } +} + void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(p_window == p_parent); - ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd_window = windows[p_window]; ERR_FAIL_COND(wd_window.transient_parent == p_parent); - ERR_FAIL_COND_MSG(wd_window.always_on_top, "Windows with the 'on top' can't become transient."); if (p_parent == INVALID_WINDOW_ID) { - //remove transient + // Remove transient. ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID); ERR_FAIL_COND(!windows.has(wd_window.transient_parent)); @@ -750,7 +914,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"); @@ -759,7 +925,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); + } } } @@ -818,10 +986,15 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo wd.height = h; #if defined(VULKAN_ENABLED) - if (rendering_driver == "vulkan") { + if (context_vulkan) { context_vulkan->window_resize(p_window, w, h); } #endif +#if defined(GLES3_ENABLED) + if (gl_manager) { + gl_manager->window_resize(p_window, w, h); + } +#endif if (wd.fullscreen) { return; @@ -840,8 +1013,8 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE); - // Don't let the mouse leave the window when resizing to a smaller resolution - if (mouse_mode == MOUSE_MODE_CONFINED) { + // 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); @@ -856,12 +1029,13 @@ Size2i DisplayServerWindows::window_get_size(WindowID p_window) const { ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); const WindowData &wd = windows[p_window]; + // GetClientRect() returns a zero rect for a minimized window, so we need to get the size in another way. if (wd.minimized) { return Size2(wd.width, wd.height); } RECT r; - if (GetClientRect(wd.hWnd, &r)) { // Only area inside of window border + if (GetClientRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration. return Size2(r.right - r.left, r.bottom - r.top); } return Size2(); @@ -874,24 +1048,29 @@ Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const { const WindowData &wd = windows[p_window]; RECT r; - if (GetWindowRect(wd.hWnd, &r)) { // Includes area of the window border + if (GetWindowRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration. return Size2(r.right - r.left, r.bottom - r.top); } 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 + r_style = 0; 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; - //if (p_borderless) { - // r_style_ex |= WS_EX_TOOLWINDOW; - //} + 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) { @@ -903,14 +1082,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) { @@ -922,12 +1104,16 @@ 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)); + if (icon.is_valid()) { + set_icon(icon); + } + + 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; @@ -942,10 +1128,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) { @@ -961,6 +1148,11 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) _update_window_style(p_window, false); MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); + + if (restore_mouse_trails > 1) { + SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0); + restore_mouse_trails = 0; + } } else if (p_mode == WINDOW_MODE_WINDOWED) { ShowWindow(wd.hWnd, SW_RESTORE); wd.maximized = false; @@ -979,7 +1171,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); } @@ -997,9 +1197,25 @@ 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); + + // If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode. + // Save number of trails so we can restore when exiting, then turn off mouse trails + SystemParametersInfoA(SPI_GETMOUSETRAILS, 0, &restore_mouse_trails, 0); + if (restore_mouse_trails > 1) { + 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); } } @@ -1010,7 +1226,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) { @@ -1027,7 +1247,7 @@ bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const { // FIXME: Implement this, or confirm that it should always be true. - return true; //no idea + return true; } void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) { @@ -1044,6 +1264,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"); @@ -1057,6 +1278,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; } @@ -1083,6 +1309,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; } @@ -1094,7 +1323,7 @@ void DisplayServerWindows::window_request_attention(WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; + const WindowData &wd = windows[p_window]; FLASHWINFO info; info.cbSize = sizeof(FLASHWINFO); @@ -1111,7 +1340,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 { @@ -1119,14 +1350,14 @@ bool DisplayServerWindows::window_can_draw(WindowID p_window) const { ERR_FAIL_COND_V(!windows.has(p_window), false); const WindowData &wd = windows[p_window]; - return wd.minimized; + return !wd.minimized; } bool DisplayServerWindows::can_any_window_draw() const { _THREAD_SAFE_METHOD_ - for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - if (!E->get().minimized) { + for (const KeyValue<WindowID, WindowData> &E : windows) { + if (!E.value.minimized) { return true; } } @@ -1158,8 +1389,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; @@ -1169,26 +1401,14 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI ImmReleaseContext(wd.hWnd, himc); } -void DisplayServerWindows::console_set_visible(bool p_enabled) { - _THREAD_SAFE_METHOD_ - - if (console_visible == p_enabled) - return; - ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE); - console_visible = p_enabled; -} - -bool DisplayServerWindows::is_console_visible() const { - return console_visible; -} - void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { _THREAD_SAFE_METHOD_ ERR_FAIL_INDEX(p_shape, CURSOR_MAX); - if (cursor_shape == p_shape) + if (cursor_shape == p_shape) { return; + } if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { cursor_shape = p_shape; @@ -1198,11 +1418,11 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { static const LPCTSTR win_cursors[CURSOR_MAX] = { IDC_ARROW, IDC_IBEAM, - IDC_HAND, //finger + IDC_HAND, // Finger. IDC_CROSS, IDC_WAIT, IDC_APPSTARTING, - IDC_ARROW, + IDC_SIZEALL, IDC_ARROW, IDC_NO, IDC_SIZENS, @@ -1229,59 +1449,59 @@ DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const { } void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) { - // Get the system display DC + // Get the system display DC. HDC hDC = GetDC(nullptr); - // Create helper DC + // Create helper DC. HDC hMainDC = CreateCompatibleDC(hDC); HDC hAndMaskDC = CreateCompatibleDC(hDC); HDC hXorMaskDC = CreateCompatibleDC(hDC); - // Get the dimensions of the source bitmap + // Get the dimensions of the source bitmap. BITMAP bm; GetObject(hSourceBitmap, sizeof(BITMAP), &bm); - // Create the mask bitmaps - hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color - hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color + // Create the mask bitmaps. + hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color. + hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color. - // Release the system display DC + // Release the system display DC. ReleaseDC(nullptr, hDC); - // Select the bitmaps to helper DC + // Select the bitmaps to helper DC. HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap); HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap); HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap); // Assign the monochrome AND mask bitmap pixels so that the pixels of the source bitmap - // with 'clrTransparent' will be white pixels of the monochrome bitmap + // with 'clrTransparent' will be white pixels of the monochrome bitmap. SetBkColor(hMainDC, clrTransparent); BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY); // Assign the color XOR mask bitmap pixels so that the pixels of the source bitmap // with 'clrTransparent' will be black and rest the pixels same as corresponding - // pixels of the source bitmap + // pixels of the source bitmap. SetBkColor(hXorMaskDC, RGB(0, 0, 0)); SetTextColor(hXorMaskDC, RGB(255, 255, 255)); BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY); BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND); - // Deselect bitmaps from the helper DC + // Deselect bitmaps from the helper DC. SelectObject(hMainDC, hOldMainBitmap); SelectObject(hAndMaskDC, hOldAndMaskBitmap); SelectObject(hXorMaskDC, hOldXorMaskBitmap); - // Delete the helper DC + // Delete the helper DC. DeleteDC(hXorMaskDC); DeleteDC(hAndMaskDC); DeleteDC(hMainDC); } -void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { _THREAD_SAFE_METHOD_ if (p_cursor.is_valid()) { - Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape); + RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape); if (cursor_c) { if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) { @@ -1299,7 +1519,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh Rect2 atlas_rect; if (texture.is_valid()) { - image = texture->get_data(); + image = texture->get_image(); } if (!image.is_valid() && atlas_texture.is_valid()) { @@ -1322,13 +1542,13 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - image = texture->get_data(); + image = texture->get_image(); ERR_FAIL_COND(!image.is_valid()); UINT image_size = texture_size.width * texture_size.height; - // Create the BITMAP with alpha channel + // Create the BITMAP with alpha channel. COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size); for (UINT index = 0; index < image_size; index++) { @@ -1343,11 +1563,11 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32(); } - // Using 4 channels, so 4 * 8 bits + // Using 4 channels, so 4 * 8 bits. HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer); COLORREF clrTransparent = -1; - // Create the AND and XOR masks for the bitmap + // Create the AND and XOR masks for the bitmap. HBITMAP hAndMask = nullptr; HBITMAP hXorMask = nullptr; @@ -1359,7 +1579,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh return; } - // Finally, create the icon + // Finally, create the icon. ICONINFO iconinfo; iconinfo.fIcon = FALSE; iconinfo.xHotspot = p_hotspot.x; @@ -1367,8 +1587,9 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh iconinfo.hbmMask = hAndMask; iconinfo.hbmColor = hXorMask; - if (cursors[p_shape]) + if (cursors[p_shape]) { DestroyIcon(cursors[p_shape]); + } cursors[p_shape] = CreateIconIndirect(&iconinfo); @@ -1383,18 +1604,13 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh } } - if (hAndMask != nullptr) { - DeleteObject(hAndMask); - } - - if (hXorMask != nullptr) { - DeleteObject(hXorMask); - } + DeleteObject(hAndMask); + DeleteObject(hXorMask); memfree(buffer); DeleteObject(bitmap); } else { - // Reset to default system cursor + // Reset to default system cursor. if (cursors[p_shape]) { DestroyIcon(cursors[p_shape]); cursors[p_shape] = nullptr; @@ -1419,13 +1635,13 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) { } int DisplayServerWindows::keyboard_get_layout_count() const { - return GetKeyboardLayoutList(0, NULL); + return GetKeyboardLayoutList(0, nullptr); } int DisplayServerWindows::keyboard_get_current_layout() const { HKL cur_layout = GetKeyboardLayout(0); - int layout_count = GetKeyboardLayoutList(0, NULL); + int layout_count = GetKeyboardLayoutList(0, nullptr); HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); GetKeyboardLayoutList(layout_count, layouts); @@ -1440,7 +1656,7 @@ int DisplayServerWindows::keyboard_get_current_layout() const { } void DisplayServerWindows::keyboard_set_current_layout(int p_index) { - int layout_count = GetKeyboardLayoutList(0, NULL); + int layout_count = GetKeyboardLayoutList(0, nullptr); ERR_FAIL_INDEX(p_index, layout_count); @@ -1451,7 +1667,7 @@ void DisplayServerWindows::keyboard_set_current_layout(int p_index) { } String DisplayServerWindows::keyboard_get_layout_language(int p_index) const { - int layout_count = GetKeyboardLayoutList(0, NULL); + int layout_count = GetKeyboardLayoutList(0, nullptr); ERR_FAIL_INDEX_V(p_index, layout_count, ""); @@ -1467,6 +1683,42 @@ String DisplayServerWindows::keyboard_get_layout_language(int p_index) const { return String::utf16((const char16_t *)buf).substr(0, 2); } +Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) const { + Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK; + Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK); + + if (keycode_no_mod == Key::PRINT || + keycode_no_mod == Key::KP_ADD || + keycode_no_mod == Key::KP_5 || + (keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) { + return p_keycode; + } + + unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod); + if (scancode == 0) { + return p_keycode; + } + + HKL current_layout = GetKeyboardLayout(0); + UINT vk = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK, current_layout); + if (vk == 0) { + return p_keycode; + } + + UINT char_code = MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, current_layout) & 0x7FFF; + // Unlike a similar Linux/BSD check which matches full Latin-1 range, + // we limit these to ASCII to fix some layouts, including Arabic ones + if (char_code >= 32 && char_code <= 127) { + // Godot uses 'braces' instead of 'brackets' + if (char_code == (unsigned int)Key::BRACKETLEFT || char_code == (unsigned int)Key::BRACKETRIGHT) { + char_code += 32; + } + return (Key)(char_code | (unsigned int)modifiers); + } + + return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers); +} + String _get_full_layout_name_from_registry(HKL p_layout) { String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0"); String ret; @@ -1481,7 +1733,7 @@ String _get_full_layout_name_from_registry(HKL p_layout) { DWORD buffer = 1024; DWORD vtype = REG_SZ; - if (RegQueryValueExW(hkey, L"Layout Text", NULL, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) { + if (RegQueryValueExW(hkey, L"Layout Text", nullptr, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) { ret = String::utf16((const char16_t *)layout_text); } RegCloseKey(hkey); @@ -1489,7 +1741,7 @@ String _get_full_layout_name_from_registry(HKL p_layout) { } String DisplayServerWindows::keyboard_get_layout_name(int p_index) const { - int layout_count = GetKeyboardLayoutList(0, NULL); + int layout_count = GetKeyboardLayoutList(0, nullptr); ERR_FAIL_INDEX_V(p_index, layout_count, ""); @@ -1497,7 +1749,7 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const { GetKeyboardLayoutList(layout_count, layouts); String ret = _get_full_layout_name_from_registry(layouts[p_index]); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine). - if (ret == String()) { + if (ret.is_empty()) { WCHAR buf[LOCALE_NAME_MAX_LENGTH]; memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR)); LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); @@ -1529,7 +1781,7 @@ void DisplayServerWindows::process_events() { if (!drop_events) { _process_key_events(); - Input::get_singleton()->flush_accumulated_events(); + Input::get_singleton()->flush_buffered_events(); } } @@ -1548,13 +1800,18 @@ void DisplayServerWindows::make_rendering_thread() { } void DisplayServerWindows::swap_buffers() { +#if defined(GLES3_ENABLED) + if (gl_manager) { + gl_manager->swap_buffers(); + } +#endif } void DisplayServerWindows::set_native_icon(const String &p_filename) { _THREAD_SAFE_METHOD_ - FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); - ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'."); + Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'."); ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR)); int pos = 0; @@ -1576,9 +1833,9 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { icon_dir = (ICONDIR *)memrealloc(icon_dir, 3 * sizeof(WORD) + icon_dir->idCount * sizeof(ICONDIRENTRY)); f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY)); - int small_icon_index = -1; // Select 16x16 with largest color count + int small_icon_index = -1; // Select 16x16 with largest color count. int small_icon_cc = 0; - int big_icon_index = -1; // Select largest + int big_icon_index = -1; // Select largest. int big_icon_width = 16; int big_icon_cc = 0; @@ -1608,7 +1865,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { small_icon_cc = big_icon_cc; } - // Read the big icon + // Read the big icon. DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes; Vector<uint8_t> data_big; data_big.resize(bytecount_big); @@ -1618,7 +1875,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000); ERR_FAIL_COND_MSG(!icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + "."); - // Read the small icon + // Read the small icon. DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes; Vector<uint8_t> data_small; data_small.resize(bytecount_small); @@ -1628,7 +1885,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000); ERR_FAIL_COND_MSG(!icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + "."); - // Online tradition says to be sure last error is cleared and set the small icon first + // Online tradition says to be sure last error is cleared and set the small icon first. int err = 0; SetLastError(err); @@ -1640,7 +1897,6 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { err = GetLastError(); ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + "."); - memdelete(f); memdelete(icon_dir); } @@ -1648,13 +1904,16 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!p_icon.is_valid()); - Ref<Image> icon = p_icon->duplicate(); - if (icon->get_format() != Image::FORMAT_RGBA8) - icon->convert(Image::FORMAT_RGBA8); + if (icon != p_icon) { + icon = p_icon->duplicate(); + if (icon->get_format() != Image::FORMAT_RGBA8) { + icon->convert(Image::FORMAT_RGBA8); + } + } int w = icon->get_width(); int h = icon->get_height(); - /* Create temporary bitmap buffer */ + // Create temporary bitmap buffer. int icon_len = 40 + h * w * 4; Vector<BYTE> v; v.resize(icon_len); @@ -1688,18 +1947,30 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); - /* Set the icon for the window */ + // Set the icon for the window. SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon); - /* Set the icon in the task manager (should we do this?) */ + // Set the icon in the task manager (should we do this?). SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon); } -void DisplayServerWindows::vsync_set_use_via_compositor(bool p_enable) { +void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { + _THREAD_SAFE_METHOD_ +#if defined(VULKAN_ENABLED) + if (context_vulkan) { + context_vulkan->set_vsync_mode(p_window, p_vsync_mode); + } +#endif } -bool DisplayServerWindows::vsync_is_using_via_compositor() const { - return false; +DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const { + _THREAD_SAFE_METHOD_ +#if defined(VULKAN_ENABLED) + if (context_vulkan) { + return context_vulkan->get_vsync_mode(p_window); + } +#endif + return DisplayServer::VSYNC_ENABLED; } void DisplayServerWindows::set_context(Context p_context) { @@ -1710,13 +1981,13 @@ void DisplayServerWindows::set_context(Context p_context) { // Keeping the name suggested by Microsoft, but this macro really answers: // Is this mouse event emulated from touch or pen input? #define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE) -// This one tells whether the event comes from touchscreen (and not from pen) +// This one tells whether the event comes from touchscreen (and not from pen). #define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80)) void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx) { - // Defensive - if (touch_state.has(idx) == p_pressed) + if (touch_state.has(idx) == p_pressed) { return; + } if (p_pressed) { touch_state.insert(idx, Vector2(p_x, p_y)); @@ -1725,32 +1996,33 @@ void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float } Ref<InputEventScreenTouch> event; - event.instance(); + event.instantiate(); event->set_index(idx); event->set_window_id(p_window); event->set_pressed(p_pressed); event->set_position(Vector2(p_x, p_y)); - Input::get_singleton()->accumulate_input_event(event); + Input::get_singleton()->parse_input_event(event); } void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) { - Map<int, Vector2>::Element *curr = touch_state.find(idx); - // Defensive - if (!curr) + RBMap<int, Vector2>::Element *curr = touch_state.find(idx); + if (!curr) { return; + } - if (curr->get() == Vector2(p_x, p_y)) + if (curr->get() == Vector2(p_x, p_y)) { return; + } Ref<InputEventScreenDrag> event; - event.instance(); + event.instantiate(); event->set_window_id(p_window); event->set_index(idx); event->set_position(Vector2(p_x, p_y)); event->set_relative(Vector2(p_x, p_y) - curr->get()); - Input::get_singleton()->accumulate_input_event(event); + Input::get_singleton()->parse_input_event(event); curr->get() = Vector2(p_x, p_y); } @@ -1766,7 +2038,7 @@ void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent } void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) { - ((DisplayServerWindows *)(get_singleton()))->_dispatch_input_event(p_event); + static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event); } void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) { @@ -1781,33 +2053,194 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) Variant ret; Callable::CallError ce; - 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 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()) { + { + List<WindowID>::Element *E = popup_list.back(); + 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; } - callable.call((const Variant **)&evp, 1, ret, ce); + } + + 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())) { + Callable callable = windows[event_from_window->get_window_id()].input_event_callback; + if (callable.is_valid()) { + callable.call((const Variant **)&evp, 1, ret, ce); + } + } } else { - //send to all windows - for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - Callable callable = E->get().input_event_callback; - if (callable.is_null()) { - continue; + // Send to all windows. + for (const KeyValue<WindowID, WindowData> &E : windows) { + const Callable callable = E.value.input_event_callback; + if (callable.is_valid()) { + callable.call((const Variant **)&evp, 1, ret, ce); } - callable.call((const Variant **)&evp, 1, ret, ce); } } in_dispatch_input_event = false; } +LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) { + DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton()); + if (ds_win) { + return ds_win->MouseProc(code, wParam, lParam); + } else { + return ::CallNextHookEx(nullptr, code, wParam, lParam); + } +} + +DisplayServer::WindowID DisplayServerWindows::window_get_active_popup() const { + const List<WindowID>::Element *E = popup_list.back(); + if (E) { + return E->get(); + } else { + return INVALID_WINDOW_ID; + } +} + +void DisplayServerWindows::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + wd.parent_safe_rect = p_rect; +} + +Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), Rect2i()); + const WindowData &wd = windows[p_window]; + return wd.parent_safe_rect; +} + +void DisplayServerWindows::popup_open(WindowID p_window) { + _THREAD_SAFE_METHOD_ + + const WindowData &wd = windows[p_window]; + if (wd.is_popup) { + // Find current popup parent, or root popup if new window is not transient. + List<WindowID>::Element *C = nullptr; + List<WindowID>::Element *E = popup_list.back(); + while (E) { + if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) { + C = E; + E = E->prev(); + } else { + break; + } + } + if (C) { + _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + } + + time_since_popup = OS::get_singleton()->get_ticks_msec(); + popup_list.push_back(p_window); + } +} + +void DisplayServerWindows::popup_close(WindowID p_window) { + _THREAD_SAFE_METHOD_ + + List<WindowID>::Element *E = popup_list.find(p_window); + while (E) { + List<WindowID>::Element *F = E->next(); + WindowID win_id = E->get(); + popup_list.erase(E); + + if (win_id != p_window) { + // Only request close on related windows, not this window. We are already processing it. + _send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + } + E = F; + } +} + +LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) { + _THREAD_SAFE_METHOD_ + + uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup; + if (delta > 250) { + switch (wParam) { + case WM_NCLBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: { + MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam; + Point2i pos = Point2i(ms->pt.x, ms->pt.y); + List<WindowID>::Element *C = nullptr; + List<WindowID>::Element *E = popup_list.back(); + // Find top popup to close. + while (E) { + // Popup window area. + Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get())); + // Area of the parent window, which responsible for opening sub-menu. + Rect2i safe_rect = window_get_popup_safe_rect(E->get()); + if (win_rect.has_point(pos)) { + break; + } else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) { + break; + } else { + C = E; + E = E->prev(); + } + } + if (C) { + _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); + return 1; + } + } break; + } + } + return ::CallNextHookEx(mouse_monitor, code, wParam, lParam); +} + +// Handle a single window message received while CreateWindowEx is still on the stack and our data +// structures are not fully initialized. +LRESULT DisplayServerWindows::_handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) { + case WM_GETMINMAXINFO: { + // We receive this during CreateWindowEx and we haven't initialized the window + // struct, so let Windows figure out the maximized size. + // Silently forward to user/default. + } break; + case WM_NCCREATE: { + // We tunnel an unowned pointer to our window context (WindowData) through the + // first possible message (WM_NCCREATE) to fix up our window context collection. + CREATESTRUCTW *pCreate = (CREATESTRUCTW *)lParam; + WindowData *pWindowData = reinterpret_cast<WindowData *>(pCreate->lpCreateParams); + + // Fix this up so we can recognize the remaining messages. + pWindowData->hWnd = hWnd; + } break; + default: { + // Additional messages during window creation should happen after we fixed + // up the data structures on WM_NCCREATE, but this might change in the future, + // so report an error here and then we can implement them. + ERR_PRINT_ONCE(vformat("Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error.", uMsg)); + } break; + } + + if (user_proc) { + return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); + } + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events. +// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (drop_events) { if (user_proc) { @@ -1815,32 +2248,44 @@ 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; - for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - if (E->get().hWnd == hWnd) { - window_id = E->key(); + // Check whether window exists + // FIXME this is O(n), where n is the set of currently open windows and subwindows + // we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below + for (const KeyValue<WindowID, WindowData> &E : windows) { + if (E.value.hWnd == hWnd) { + window_id = E.key; window_created = true; break; } } + // WARNING: we get called with events before the window is registered in our collection + // specifically, even the call to CreateWindowEx already calls here while still on the stack, + // so there is no way to store the window handle in our collection before we get here if (!window_created) { - // Window creation in progress. - window_id = window_id_counter; - ERR_FAIL_COND_V(!windows.has(window_id), 0); + // don't let code below operate on incompletely initialized window objects or missing window_id + return _handle_early_window_message(hWnd, uMsg, wParam, lParam); } - switch (uMsg) // Check For Windows Messages - { + // 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; - // Restore mouse mode + // Restore mouse mode. _set_mouse_mode_impl(mouse_mode); if (!app_focused) { @@ -1849,18 +2294,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } app_focused = true; } - break; - } + } break; case WM_KILLFOCUS: { windows[window_id].window_has_focus = false; last_focused_window = window_id; - // Release capture unconditionally because it can be set due to dragging, in addition to captured mode + // Release capture unconditionally because it can be set due to dragging, in addition to captured mode. ReleaseCapture(); - // Release every touch to avoid sticky points - for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) { - _touch_event(window_id, false, E->get().x, E->get().y, E->key()); + // Release every touch to avoid sticky points. + for (const KeyValue<int, Vector2> &E : touch_state) { + _touch_event(window_id, false, E.value.x, E.value.y, E.key); } touch_state.clear(); @@ -1876,10 +2320,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } app_focused = false; } - - break; - } - case WM_ACTIVATE: { // Watch For Window Activate Message + } break; + case WM_ACTIVATE: { // Watch for window activate message. if (!windows[window_id].window_focused) { _process_activate_event(window_id, wParam, lParam); } else { @@ -1889,11 +2331,16 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA // Run a timer to prevent event catching warning if the focused window is closing. windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); } - return 0; // Return To The Message Loop - } + if (wParam != WA_INACTIVE) { + track_mouse_leave_event(hWnd); + } + return 0; // Return to the message loop. + } break; case WM_GETMINMAXINFO: { if (windows[window_id].resizable && !windows[window_id].fullscreen) { - Size2 decor = window_get_size(window_id) - window_get_real_size(window_id); // Size of window decorations + // Size of window decorations. + Size2 decor = window_get_real_size(window_id) - window_get_size(window_id); + MINMAXINFO *min_max_info = (MINMAXINFO *)lParam; if (windows[window_id].min_size != Size2()) { min_max_info->ptMinTrackSize.x = windows[window_id].min_size.x + decor.x; @@ -1904,37 +2351,32 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y; } return 0; - } else { - break; } - } - case WM_PAINT: - + } break; + case WM_PAINT: { Main::force_redraw(); - break; - - case WM_SYSCOMMAND: // Intercept System Commands + } break; + case WM_SYSCOMMAND: // Intercept system commands. { - switch (wParam) // Check System Calls + switch (wParam) // Check system calls. { - case SC_SCREENSAVE: // Screensaver Trying To Start? - case SC_MONITORPOWER: // Monitor Trying To Enter Powersave? - return 0; // Prevent From Happening + case SC_SCREENSAVE: // Screensaver trying to start? + 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; // Exit - } - - case WM_CLOSE: // Did We Receive A Close Message? + } break; + case WM_CLOSE: // Did we receive a close message? { if (windows[window_id].focus_timer_id != 0U) { KillTimer(windows[window_id].hWnd, windows[window_id].focus_timer_id); } _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST); - return 0; // Jump Back + return 0; // Jump back. } case WM_MOUSELEAVE: { old_invalid = true; @@ -1956,19 +2398,20 @@ 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; if (raw->header.dwType == RIM_TYPEMOUSE) { Ref<InputEventMouseMotion> mm; - mm.instance(); + mm.instantiate(); mm->set_window_id(window_id); - mm->set_control(control_mem); - mm->set_shift(shift_mem); - mm->set_alt(alt_mem); + mm->set_ctrl_pressed(control_mem); + mm->set_shift_pressed(shift_mem); + mm->set_alt_pressed(alt_mem); mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f); @@ -1976,15 +2419,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Point2i c(windows[window_id].width / 2, windows[window_id].height / 2); - // centering just so it works as before + // Centering just so it works as before. POINT pos = { (int)c.x, (int)c.y }; ClientToScreen(windows[window_id].hWnd, &pos); SetCursorPos(pos.x, pos.y); 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)); @@ -1999,7 +2441,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA (double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft, (double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop); - POINT coords; //client coords + POINT coords; // Client coords. coords.x = abs_pos.x; coords.y = abs_pos.y; @@ -2008,14 +2450,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y)); old_x = coords.x; old_y = coords.y; - - /*Input.mi.dx = (int)((((double)(pos.x)-nScreenLeft) * 65536) / nScreenWidth + 65536 / (nScreenWidth)); - Input.mi.dy = (int)((((double)(pos.y)-nScreenTop) * 65536) / nScreenHeight + 65536 / (nScreenHeight)); - */ } - if (windows[window_id].window_has_focus && mm->get_relative() != Vector2()) - Input::get_singleton()->accumulate_input_event(mm); + if (windows[window_id].window_has_focus && mm->get_relative() != Vector2()) { + Input::get_singleton()->parse_input_event(mm); + } } delete[] lpb; } break; @@ -2056,15 +2495,16 @@ 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.instance(); + mm.instantiate(); mm->set_window_id(window_id); - mm->set_control(GetKeyState(VK_CONTROL) < 0); - mm->set_shift(GetKeyState(VK_SHIFT) < 0); - mm->set_alt(alt_mem); + mm->set_ctrl_pressed(GetKeyState(VK_CONTROL) < 0); + mm->set_shift_pressed(GetKeyState(VK_SHIFT) < 0); + mm->set_alt_pressed(alt_mem); mm->set_pressure(windows[window_id].last_pressure); mm->set_tilt(windows[window_id].last_tilt); @@ -2091,8 +2531,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; @@ -2103,8 +2542,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) - Input::get_singleton()->accumulate_input_event(mm); + if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { + Input::get_singleton()->parse_input_event(mm); + } } return 0; } @@ -2160,7 +2600,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (Input::get_singleton()->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translation + // Universal translation enabled; ignore OS translation. LPARAM extra = GetMessageExtraInfo(); if (IsTouchEvent(extra)) { break; @@ -2168,7 +2608,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (outside) { - //mouse enter + // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); @@ -2179,21 +2619,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA cursor_set_shape(c); outside = false; - //Once-Off notification, must call again.... - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hWnd; - tme.dwHoverTime = HOVER_DEFAULT; - TrackMouseEvent(&tme); + // Once-off notification, must call again. + track_mouse_leave_event(hWnd); } // 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.instance(); + mm.instantiate(); mm->set_window_id(window_id); if (pen_info.penMask & PEN_MASK_PRESSURE) { @@ -2205,13 +2641,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90)); } - mm->set_control(GetKeyState(VK_CONTROL) < 0); - mm->set_shift(GetKeyState(VK_SHIFT) < 0); - mm->set_alt(alt_mem); + mm->set_ctrl_pressed(GetKeyState(VK_CONTROL) < 0); + mm->set_shift_pressed(GetKeyState(VK_SHIFT) < 0); + mm->set_alt_pressed(alt_mem); mm->set_button_mask(last_button_state); - POINT coords; //client coords + POINT coords; // Client coords. coords.x = GET_X_LPARAM(lParam); coords.y = GET_Y_LPARAM(lParam); @@ -2237,8 +2673,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; @@ -2249,11 +2684,11 @@ 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) { - Input::get_singleton()->accumulate_input_event(mm); + if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { + Input::get_singleton()->parse_input_event(mm); } - return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event + return 0; // Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event. } break; case WM_MOUSEMOVE: { if (windows[window_id].block_mm) { @@ -2265,7 +2700,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (Input::get_singleton()->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translation + // Universal translation enabled; ignore OS translation. LPARAM extra = GetMessageExtraInfo(); if (IsTouchEvent(extra)) { break; @@ -2273,7 +2708,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (outside) { - //mouse enter + // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); @@ -2284,25 +2719,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA cursor_set_shape(c); outside = false; - //Once-Off notification, must call again.... - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hWnd; - tme.dwHoverTime = HOVER_DEFAULT; - TrackMouseEvent(&tme); + // Once-off notification, must call again. + track_mouse_leave_event(hWnd); } // 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.instance(); + mm.instantiate(); mm->set_window_id(window_id); - mm->set_control((wParam & MK_CONTROL) != 0); - mm->set_shift((wParam & MK_SHIFT) != 0); - mm->set_alt(alt_mem); + mm->set_ctrl_pressed((wParam & MK_CONTROL) != 0); + mm->set_shift_pressed((wParam & MK_SHIFT) != 0); + mm->set_alt_pressed(alt_mem); if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { // Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not updated recently. @@ -2342,8 +2773,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; @@ -2354,14 +2784,15 @@ 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) - Input::get_singleton()->accumulate_input_event(mm); + if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { + Input::get_singleton()->parse_input_event(mm); + } } break; case WM_LBUTTONDOWN: case WM_LBUTTONUP: if (Input::get_singleton()->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translations for left button + // Universal translation enabled; ignore OS translations for left button. LPARAM extra = GetMessageExtraInfo(); if (IsTouchEvent(extra)) { break; @@ -2381,110 +2812,116 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_XBUTTONDOWN: case WM_XBUTTONUP: { Ref<InputEventMouseButton> mb; - mb.instance(); + mb.instantiate(); mb->set_window_id(window_id); switch (uMsg) { case WM_LBUTTONDOWN: { mb->set_pressed(true); - mb->set_button_index(1); + mb->set_button_index(MouseButton::LEFT); } break; case WM_LBUTTONUP: { mb->set_pressed(false); - mb->set_button_index(1); + mb->set_button_index(MouseButton::LEFT); } break; case WM_MBUTTONDOWN: { mb->set_pressed(true); - mb->set_button_index(3); + mb->set_button_index(MouseButton::MIDDLE); } break; case WM_MBUTTONUP: { mb->set_pressed(false); - mb->set_button_index(3); + mb->set_button_index(MouseButton::MIDDLE); } break; case WM_RBUTTONDOWN: { mb->set_pressed(true); - mb->set_button_index(2); + mb->set_button_index(MouseButton::RIGHT); } break; case WM_RBUTTONUP: { mb->set_pressed(false); - mb->set_button_index(2); + mb->set_button_index(MouseButton::RIGHT); } break; case WM_LBUTTONDBLCLK: { mb->set_pressed(true); - mb->set_button_index(1); - mb->set_doubleclick(true); + mb->set_button_index(MouseButton::LEFT); + mb->set_double_click(true); } break; case WM_RBUTTONDBLCLK: { mb->set_pressed(true); - mb->set_button_index(2); - mb->set_doubleclick(true); + mb->set_button_index(MouseButton::RIGHT); + mb->set_double_click(true); } break; case WM_MBUTTONDBLCLK: { mb->set_pressed(true); - mb->set_button_index(3); - mb->set_doubleclick(true); + mb->set_button_index(MouseButton::MIDDLE); + mb->set_double_click(true); } break; case WM_MOUSEWHEEL: { mb->set_pressed(true); int motion = (short)HIWORD(wParam); - if (!motion) + if (!motion) { return 0; + } - if (motion > 0) - mb->set_button_index(BUTTON_WHEEL_UP); - else - mb->set_button_index(BUTTON_WHEEL_DOWN); - + if (motion > 0) { + mb->set_button_index(MouseButton::WHEEL_UP); + } else { + mb->set_button_index(MouseButton::WHEEL_DOWN); + } + mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } break; case WM_MOUSEHWHEEL: { mb->set_pressed(true); int motion = (short)HIWORD(wParam); - if (!motion) + if (!motion) { return 0; + } if (motion < 0) { - mb->set_button_index(BUTTON_WHEEL_LEFT); - mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); + mb->set_button_index(MouseButton::WHEEL_LEFT); } else { - mb->set_button_index(BUTTON_WHEEL_RIGHT); - mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); + mb->set_button_index(MouseButton::WHEEL_RIGHT); } + mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } break; case WM_XBUTTONDOWN: { mb->set_pressed(true); - if (HIWORD(wParam) == XBUTTON1) - mb->set_button_index(BUTTON_XBUTTON1); - else - mb->set_button_index(BUTTON_XBUTTON2); + if (HIWORD(wParam) == XBUTTON1) { + mb->set_button_index(MouseButton::MB_XBUTTON1); + } else { + mb->set_button_index(MouseButton::MB_XBUTTON2); + } } break; case WM_XBUTTONUP: { mb->set_pressed(false); - if (HIWORD(wParam) == XBUTTON1) - mb->set_button_index(BUTTON_XBUTTON1); - else - mb->set_button_index(BUTTON_XBUTTON2); + if (HIWORD(wParam) == XBUTTON1) { + mb->set_button_index(MouseButton::MB_XBUTTON1); + } else { + mb->set_button_index(MouseButton::MB_XBUTTON2); + } } break; case WM_XBUTTONDBLCLK: { mb->set_pressed(true); - if (HIWORD(wParam) == XBUTTON1) - mb->set_button_index(BUTTON_XBUTTON1); - else - mb->set_button_index(BUTTON_XBUTTON2); - mb->set_doubleclick(true); + if (HIWORD(wParam) == XBUTTON1) { + mb->set_button_index(MouseButton::MB_XBUTTON1); + } else { + mb->set_button_index(MouseButton::MB_XBUTTON2); + } + mb->set_double_click(true); } break; default: { return 0; } } - mb->set_control((wParam & MK_CONTROL) != 0); - mb->set_shift((wParam & MK_SHIFT) != 0); - mb->set_alt(alt_mem); - //mb->get_alt()=(wParam&MK_MENU)!=0; - if (mb->is_pressed()) - last_button_state |= (1 << (mb->get_button_index() - 1)); - else - last_button_state &= ~(1 << (mb->get_button_index() - 1)); + mb->set_ctrl_pressed((wParam & MK_CONTROL) != 0); + mb->set_shift_pressed((wParam & MK_SHIFT) != 0); + mb->set_alt_pressed(alt_mem); + // mb->is_alt_pressed()=(wParam&MK_MENU)!=0; + if (mb->is_pressed()) { + last_button_state |= mouse_button_to_mask(mb->get_button_index()); + } else { + last_button_state &= ~mouse_button_to_mask(mb->get_button_index()); + } mb->set_button_mask(last_button_state); mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); @@ -2495,8 +2932,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) { @@ -2506,7 +2944,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } } else { - // for reasons unknown to mankind, wheel comes in screen coordinates + // For reasons unknown to humanity, wheel comes in screen coordinates. POINT coords; coords.x = mb->get_position().x; coords.y = mb->get_position().y; @@ -2518,98 +2956,88 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mb->set_global_position(mb->get_position()); - Input::get_singleton()->accumulate_input_event(mb); - if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) { - //send release for mouse wheel + Input::get_singleton()->parse_input_event(mb); + if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) { + // Send release for mouse wheel. Ref<InputEventMouseButton> mbd = mb->duplicate(); mbd->set_window_id(window_id); - last_button_state &= ~(1 << (mbd->get_button_index() - 1)); + last_button_state &= ~mouse_button_to_mask(mbd->get_button_index()); mbd->set_button_mask(last_button_state); mbd->set_pressed(false); - Input::get_singleton()->accumulate_input_event(mbd); + Input::get_singleton()->parse_input_event(mbd); } } break; - case WM_MOVE: { - if (!IsIconic(windows[window_id].hWnd)) { - int x = int16_t(LOWORD(lParam)); - int y = int16_t(HIWORD(lParam)); - windows[window_id].last_pos = Point2(x, y); - - if (!windows[window_id].rect_changed_callback.is_null()) { - Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height); - Variant *sizep = &size; - Variant ret; - Callable::CallError ce; - windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce); + case WM_WINDOWPOSCHANGED: { + Rect2i window_client_rect; + Rect2i window_rect; + { + RECT rect; + GetClientRect(hWnd, &rect); + ClientToScreen(hWnd, (POINT *)&rect.left); + ClientToScreen(hWnd, (POINT *)&rect.right); + window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + + RECT wrect; + GetWindowRect(hWnd, &wrect); + window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left, wrect.bottom - wrect.top); + } + + WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam; + WindowData &window = windows[window_id]; + + bool rect_changed = false; + if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) { + int screen_id = window_get_current_screen(window_id); + Size2i screen_size = screen_get_size(screen_id); + Point2i screen_position = screen_get_position(screen_id); + + window.maximized = false; + window.minimized = false; + window.fullscreen = false; + + if (IsIconic(hWnd)) { + window.minimized = true; + } else if (IsZoomed(hWnd)) { + window.maximized = true; + } else if (window_rect.position == screen_position && window_rect.size == screen_size) { + window.fullscreen = true; } - } - } break; - case WM_SIZE: { - // Ignore size when a SIZE_MINIMIZED event is triggered - if (wParam != SIZE_MINIMIZED) { - int window_w = LOWORD(lParam); - int window_h = HIWORD(lParam); - if (window_w > 0 && window_h > 0 && !windows[window_id].preserve_window_size) { - windows[window_id].width = window_w; - windows[window_id].height = window_h; + if (!window.minimized) { + window.width = window_client_rect.size.width; + window.height = window_client_rect.size.height; + rect_changed = true; + } #if defined(VULKAN_ENABLED) - if ((rendering_driver == "vulkan") && window_created) { - context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].height); - } + if (context_vulkan && window_created) { + // Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed. + context_vulkan->window_resize(window_id, window.width, window.height); + } #endif + } - } else { - windows[window_id].preserve_window_size = false; - window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id); - } + 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; } - 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); - } - - if (wParam == SIZE_MAXIMIZED) { - windows[window_id].maximized = true; - windows[window_id].minimized = false; - } else if (wParam == SIZE_MINIMIZED) { - windows[window_id].maximized = false; - windows[window_id].minimized = true; - } 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); + 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); + } } -#endif - //return 0; // Jump Back + + // 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: { @@ -2631,19 +3059,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA windows[window_id].focus_timer_id = 0U; } } break; - case WM_SYSKEYDOWN: 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) { @@ -2652,10 +3082,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: { @@ -2670,10 +3096,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; @@ -2683,7 +3111,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_INPUTLANGCHANGEREQUEST: { // FIXME: Do something? } break; - case WM_TOUCH: { BOOL bHandled = FALSE; UINT cInputs = LOWORD(wParam); @@ -2697,35 +3124,37 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA TOUCH_COORD_TO_PIXEL(ti.y), }; ScreenToClient(hWnd, &touch_pos); - //do something with each touch input entry + // Do something with each touch input entry. if (ti.dwFlags & TOUCHEVENTF_MOVE) { _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 { - /* handle the error here */ + // TODO: Handle the error here. } memdelete_arr(pInputs); } else { - /* handle the error here, probably out of memory */ + // TODO: Handle the error here, probably out of memory. } 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)) { - //Hide the cursor + if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) { + // Hide the cursor. if (hCursor == nullptr) { hCursor = SetCursor(nullptr); } else { @@ -2740,7 +3169,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } } - } break; case WM_DROPFILES: { HDROP hDropInfo = (HDROP)wParam; @@ -2764,14 +3192,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Callable::CallError ce; windows[window_id].drop_files_callback.call((const Variant **)&vp, 1, ret, ce); } - } break; - default: { if (user_proc) { return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); - }; - }; + } + } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -2779,10 +3205,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) { @@ -2792,7 +3219,10 @@ void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM alt_mem = false; control_mem = false; shift_mem = false; - } else { // WM_INACTIVE + + // 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); windows[p_window_id].window_focused = false; @@ -2809,7 +3239,7 @@ void DisplayServerWindows::_process_key_events() { KeyEvent &ke = key_event_buffer[i]; switch (ke.uMsg) { case WM_CHAR: { - // extended keys should only be processed as WM_KEYDOWN message. + // Extended keys should only be processed as WM_KEYDOWN message. if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) { static char32_t prev_wc = 0; char32_t unicode = ke.wParam; @@ -2830,51 +3260,52 @@ void DisplayServerWindows::_process_key_events() { prev_wc = 0; } Ref<InputEventKey> k; - k.instance(); + k.instantiate(); k->set_window_id(ke.window_id); - k->set_shift(ke.shift); - k->set_alt(ke.alt); - k->set_control(ke.control); - k->set_metakey(ke.meta); + k->set_shift_pressed(ke.shift); + k->set_alt_pressed(ke.alt); + k->set_ctrl_pressed(ke.control); + k->set_meta_pressed(ke.meta); k->set_pressed(true); - k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam)); - k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24))); + k->set_keycode((Key)KeyMappingWindows::get_keysym(ke.wParam)); + k->set_physical_keycode((Key)(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)))); k->set_unicode(unicode); if (k->get_unicode() && gr_mem) { - k->set_alt(false); - k->set_control(false); + k->set_alt_pressed(false); + k->set_ctrl_pressed(false); } - if (k->get_unicode() < 32) + if (k->get_unicode() < 32) { k->set_unicode(0); + } - Input::get_singleton()->accumulate_input_event(k); + Input::get_singleton()->parse_input_event(k); + } else { + // Do nothing. } - - //do nothing } break; case WM_KEYUP: case WM_KEYDOWN: { Ref<InputEventKey> k; - k.instance(); + k.instantiate(); k->set_window_id(ke.window_id); - k->set_shift(ke.shift); - k->set_alt(ke.alt); - k->set_control(ke.control); - k->set_metakey(ke.meta); + k->set_shift_pressed(ke.shift); + k->set_alt_pressed(ke.alt); + k->set_ctrl_pressed(ke.control); + k->set_meta_pressed(ke.meta); k->set_pressed(ke.uMsg == WM_KEYDOWN); if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) { - // Special case for Numpad Enter key - k->set_keycode(KEY_KP_ENTER); + // Special case for Numpad Enter key. + k->set_keycode(Key::KP_ENTER); } else { - k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam)); + k->set_keycode((Key)KeyMappingWindows::get_keysym(ke.wParam)); } - k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24))); + k->set_physical_keycode((Key)(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)))); if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) { char32_t unicode = key_event_buffer[i + 1].wParam; @@ -2898,16 +3329,17 @@ void DisplayServerWindows::_process_key_events() { k->set_unicode(unicode); } if (k->get_unicode() && gr_mem) { - k->set_alt(false); - k->set_control(false); + k->set_alt_pressed(false); + 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)))); - Input::get_singleton()->accumulate_input_event(k); + Input::get_singleton()->parse_input_event(k); } break; } @@ -2917,8 +3349,8 @@ void DisplayServerWindows::_process_key_events() { } void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const String &p_new_driver) { - for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - WindowData &wd = E->get(); + for (KeyValue<WindowID, WindowData> &E : windows) { + WindowData &wd = E.value; wd.block_mm = false; if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) { wintab_WTEnable(wd.wtctx, false); @@ -2955,11 +3387,11 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const } } -DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { 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; @@ -2968,7 +3400,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++) { @@ -3005,19 +3437,25 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, WindowRect.top, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, - nullptr, nullptr, hInstance, nullptr); + nullptr, + nullptr, + hInstance, + // tunnel the WindowData we need to handle creation message + // lifetime is ensured because we are still on the stack when this is + // processed in the window proc + reinterpret_cast<void *>(&wd)); if (!wd.hWnd) { MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); windows.erase(id); - return INVALID_WINDOW_ID; + ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window."); } - if (p_mode != WINDOW_MODE_FULLSCREEN) { + if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { wd.pre_fs_valid = true; } -#ifdef VULKAN_ENABLED - if (rendering_driver == "vulkan") { - if (context_vulkan->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) { +#ifdef VULKAN_ENABLED + if (context_vulkan) { + if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) { memdelete(context_vulkan); context_vulkan = nullptr; windows.erase(id); @@ -3026,15 +3464,21 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, } #endif - RegisterTouchWindow(wd.hWnd, 0); +#ifdef GLES3_ENABLED + if (gl_manager) { + Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top); - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = wd.hWnd; - tme.dwHoverTime = HOVER_DEFAULT; - TrackMouseEvent(&tme); + // shut down OpenGL, to mirror behavior of Vulkan code + if (err != OK) { + memdelete(gl_manager); + gl_manager = nullptr; + windows.erase(id); + ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window."); + } + } +#endif + RegisterTouchWindow(wd.hWnd, 0); DragAcceptFiles(wd.hWnd, true); if ((tablet_get_current_driver() == "wintab") && wintab_available) { @@ -3070,11 +3514,13 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wd.last_pressure_update = 0; wd.last_tilt = Vector2(); - // IME + // IME. wd.im_himc = ImmGetContext(wd.hWnd); ImmReleaseContext(wd.hWnd, wd.im_himc); wd.im_position = Vector2(); + + // FIXME this is wrong in cases where the window coordinates were changed due to full screen mode; use WindowRect wd.last_pos = p_rect.position; wd.width = p_rect.size.width; wd.height = p_rect.size.height; @@ -3085,7 +3531,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, return id; } -// WinTab API +// WinTab API. bool DisplayServerWindows::wintab_available = false; WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr; WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr; @@ -3093,7 +3539,7 @@ WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr; WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr; WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr; -// Windows Ink API +// Windows Ink API. bool DisplayServerWindows::winink_available = false; GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr; GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr; @@ -3138,7 +3584,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) { } } -DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { drop_events = false; key_event_pos = 0; @@ -3147,8 +3593,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win shift_mem = false; control_mem = false; meta_mem = false; - console_visible = IsWindowVisible(GetConsoleWindow()); - hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance(); + hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance(); pressrc = 0; old_invalid = true; @@ -3156,7 +3601,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win outside = true; - //Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. + rendering_driver = p_rendering_driver; + + // Init TTS + tts = memnew(TTS_Windows); + + // Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll"); if (wintab_lib) { wintab_WTOpen = (WTOpenPtr)GetProcAddress(wintab_lib, "WTOpenW"); @@ -3172,7 +3622,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win tablet_drivers.push_back("wintab"); } - //Note: Windows Ink API for pen input, available on Windows 8+ only. + // Note: Windows Ink API for pen input, available on Windows 8+ only. HMODULE user32_lib = LoadLibraryW(L"user32.dll"); if (user32_lib) { win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); @@ -3205,10 +3655,9 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win wc.lpfnWndProc = (WNDPROC)::WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; - //wc.hInstance = hInstance; 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"; @@ -3229,12 +3678,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win Rid[0].hwndTarget = 0; if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) { - //registration failed. + // Registration failed. use_raw_input = false; } - rendering_driver = "vulkan"; - #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { context_vulkan = memnew(VulkanContextWindows); @@ -3246,34 +3693,32 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } } #endif -#if defined(OPENGL_ENABLED) - if (rendering_driver_index == VIDEO_DRIVER_GLES2) { - context_gles2 = memnew(ContextGL_Windows(hWnd, false)); + // Init context and rendering device +#if defined(GLES3_ENABLED) - if (context_gles2->initialize() != OK) { - memdelete(context_gles2); - context_gles2 = nullptr; - ERR_FAIL_V(ERR_UNAVAILABLE); - } + if (rendering_driver == "opengl3") { + GLManager_Windows::ContextType opengl_api_type = GLManager_Windows::GLES_3_0_COMPATIBLE; - context_gles2->set_use_vsync(video_mode.use_vsync); - set_vsync_via_compositor(video_mode.vsync_via_compositor); + gl_manager = memnew(GLManager_Windows(opengl_api_type)); - if (RasterizerGLES2::is_viable() == OK) { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - } else { - memdelete(context_gles2); - context_gles2 = nullptr; - ERR_FAIL_V(ERR_UNAVAILABLE); + if (gl_manager->initialize() != OK) { + memdelete(gl_manager); + gl_manager = nullptr; + r_error = ERR_UNAVAILABLE; + return; } + + 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); - WindowID main_window = _create_window(p_mode, 0, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution)); ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { @@ -3294,18 +3739,20 @@ 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); + if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) { + // Increase priority for projects that are not in low-processor mode (typically games) + // to reduce the risk of frame stuttering. + // This is not done for the editor to prevent importers or resource bakers + // from making the system unresponsive. SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); DWORD index = 0; HANDLE handle = AvSetMmThreadCharacteristics("Games", &index); - if (handle) + if (handle) { AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL); + } // This is needed to make sure that background work does not starve the main thread. - // This is only setting priority of this thread, not the whole process. + // This is only setting the priority of this thread, not the whole process. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); } @@ -3317,7 +3764,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win r_error = OK; - ((OS_Windows *)OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd); + static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd); Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); } @@ -3327,18 +3774,18 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() { #ifdef VULKAN_ENABLED drivers.push_back("vulkan"); #endif -#ifdef OPENGL_ENABLED - drivers.push_back("opengl"); +#ifdef GLES3_ENABLED + drivers.push_back("opengl3"); #endif return drivers; } -DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); if (r_error != OK) { - ds->alert("Your video card driver does not support any of the supported Vulkan versions.\n" - "Please update your drivers or if you have a very old or integrated GPU upgrade it.", + OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.\n" + "Please update your drivers or if you have a very old or integrated GPU upgrade it.", "Unable to initialize Video driver"); } return ds; @@ -3354,13 +3801,22 @@ 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? + // FIXME wglDeleteContext is never called +#endif if (windows.has(MAIN_WINDOW_ID)) { #ifdef VULKAN_ENABLED - if (rendering_driver == "vulkan") { + if (context_vulkan) { context_vulkan->window_destroy(MAIN_WINDOW_ID); } #endif @@ -3372,14 +3828,29 @@ DisplayServerWindows::~DisplayServerWindows() { } #if defined(VULKAN_ENABLED) - if (rendering_driver == "vulkan") { - if (rendering_device_vulkan) { - rendering_device_vulkan->finalize(); - memdelete(rendering_device_vulkan); - } + if (rendering_device_vulkan) { + rendering_device_vulkan->finalize(); + memdelete(rendering_device_vulkan); + rendering_device_vulkan = nullptr; + } - if (context_vulkan) - memdelete(context_vulkan); + if (context_vulkan) { + memdelete(context_vulkan); + context_vulkan = nullptr; + } +#endif + + if (restore_mouse_trails > 1) { + SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0); + } +#ifdef GLES3_ENABLED + if (gl_manager) { + memdelete(gl_manager); + gl_manager = nullptr; } #endif + if (tts) { + memdelete(tts); + } + CoUninitialize(); } |