diff options
Diffstat (limited to 'platform/windows/display_server_windows.cpp')
-rw-r--r-- | platform/windows/display_server_windows.cpp | 688 |
1 files changed, 450 insertions, 238 deletions
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e4fe7f04d0..103e858d97 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -36,13 +36,8 @@ #include <avrt.h> -#ifndef WM_POINTERUPDATE -#define WM_POINTERUPDATE 0x0245 -#endif - #ifdef DEBUG_ENABLED 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, nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr); @@ -87,7 +82,6 @@ void DisplayServerWindows::alert(const String &p_alert, const String &p_title) { } 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]; @@ -97,7 +91,6 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { ClientToScreen(wd.hWnd, (POINT *)&clipRect.right); ClipCursor(&clipRect); if (p_mode == MOUSE_MODE_CAPTURED) { - center = window_get_size() / 2; POINT pos = { (int)center.x, (int)center.y }; ClientToScreen(wd.hWnd, &pos); @@ -110,30 +103,34 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { } if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) { - hCursor = SetCursor(nullptr); + if (hCursor == nullptr) { + hCursor = SetCursor(nullptr); + } else { + SetCursor(nullptr); + } } else { CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; cursor_set_shape(c); } } -void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) { +void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) { _THREAD_SAFE_METHOD_ if (mouse_mode == p_mode) return; - _set_mouse_mode_impl(p_mode); - mouse_mode = p_mode; + + _set_mouse_mode_impl(p_mode); } + DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const { return mouse_mode; } void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { - _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { @@ -141,11 +138,9 @@ void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { } if (mouse_mode == MOUSE_MODE_CAPTURED) { - old_x = p_to.x; old_y = p_to.y; } else { - POINT p; p.x = p_to.x; p.y = p_to.y; @@ -154,18 +149,19 @@ void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { SetCursorPos(p.x, p.y); } } + 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 { return last_button_state; } void DisplayServerWindows::clipboard_set(const String &p_text) { - _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { @@ -204,8 +200,8 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { CloseClipboard(); } -String DisplayServerWindows::clipboard_get() const { +String DisplayServerWindows::clipboard_get() const { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { @@ -218,26 +214,20 @@ String DisplayServerWindows::clipboard_get() const { }; if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { - HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); if (mem != nullptr) { - LPWSTR ptr = (LPWSTR)GlobalLock(mem); if (ptr != nullptr) { - ret = String((CharType *)ptr); GlobalUnlock(mem); }; }; } else if (IsClipboardFormatAvailable(CF_TEXT)) { - HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); if (mem != nullptr) { - LPTSTR ptr = (LPTSTR)GlobalLock(mem); if (ptr != nullptr) { - ret.parse_utf8((const char *)ptr); GlobalUnlock(mem); }; @@ -256,7 +246,6 @@ typedef struct { } EnumScreenData; static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - EnumScreenData *data = (EnumScreenData *)dwData; if (data->monitor == hMonitor) { data->screen = data->count; @@ -267,7 +256,6 @@ static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, L } static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - int *data = (int *)dwData; (*data)++; return TRUE; @@ -288,7 +276,6 @@ typedef struct { } EnumPosData; static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - EnumPosData *data = (EnumPosData *)dwData; if (data->count == data->screen) { data->pos.x = lprcMonitor->left; @@ -298,8 +285,8 @@ static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRE data->count++; return TRUE; } -Point2i DisplayServerWindows::screen_get_position(int p_screen) const { +Point2i DisplayServerWindows::screen_get_position(int p_screen) const { _THREAD_SAFE_METHOD_ EnumPosData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Point2() }; @@ -320,7 +307,6 @@ typedef struct { } EnumRectData; static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - EnumSizeData *data = (EnumSizeData *)dwData; if (data->count == data->screen) { data->size.x = lprcMonitor->right - lprcMonitor->left; @@ -332,7 +318,6 @@ static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPR } Size2i DisplayServerWindows::screen_get_size(int p_screen) const { - _THREAD_SAFE_METHOD_ EnumSizeData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Size2() }; @@ -341,7 +326,6 @@ Size2i DisplayServerWindows::screen_get_size(int p_screen) const { } static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - EnumRectData *data = (EnumRectData *)dwData; if (data->count == data->screen) { MONITORINFO minfo; @@ -360,7 +344,6 @@ static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonito } Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const { - _THREAD_SAFE_METHOD_ EnumRectData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Rect2i() }; @@ -382,7 +365,6 @@ enum _MonitorDpiType { }; static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Default) { - int dpiX = 96, dpiY = 96; static HMODULE Shcore = nullptr; @@ -405,7 +387,6 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) { hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y); if (SUCCEEDED(hr) && (x > 0) && (y > 0)) { - dpiX = (int)x; dpiY = (int)y; } @@ -429,7 +410,6 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau } static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - EnumDpiData *data = (EnumDpiData *)dwData; if (data->count == data->screen) { data->dpi = QueryDpiForMonitor(hMonitor); @@ -446,6 +426,7 @@ int DisplayServerWindows::screen_get_dpi(int p_screen) const { EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data); return data.dpi; } + bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const { #ifndef _MSC_VER #warning touchscreen not working @@ -455,18 +436,19 @@ bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const { void DisplayServerWindows::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) { } + DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(int p_screen) const { return SCREEN_LANDSCAPE; } void DisplayServerWindows::screen_set_keep_on(bool p_enable) { } + bool DisplayServerWindows::screen_is_kept_on() const { return false; } Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const { - _THREAD_SAFE_METHOD_ Vector<DisplayServer::WindowID> ret; @@ -477,7 +459,6 @@ Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const { } DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(const Point2i &p_position) const { - POINT p; p.x = p_position.x; p.y = p_position.y; @@ -492,10 +473,10 @@ DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(cons } DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) { - _THREAD_SAFE_METHOD_ WindowID window_id = _create_window(p_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]; @@ -522,8 +503,8 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod return window_id; } -void DisplayServerWindows::delete_sub_window(WindowID p_window) { +void DisplayServerWindows::delete_sub_window(WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -545,12 +526,15 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { } #endif + if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[p_window].wtctx) { + wintab_WTClose(windows[p_window].wtctx); + windows[p_window].wtctx = 0; + } DestroyWindow(windows[p_window].hWnd); windows.erase(p_window); } void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -558,7 +542,6 @@ void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, Window } ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window) const { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), ObjectID()); @@ -566,7 +549,6 @@ ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window } void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -574,21 +556,20 @@ void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_ca } void DisplayServerWindows::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); windows[p_window].event_callback = p_callable; } -void DisplayServerWindows::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) { +void DisplayServerWindows::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); windows[p_window].input_event_callback = p_callable; } -void DisplayServerWindows::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) { +void DisplayServerWindows::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -596,7 +577,6 @@ void DisplayServerWindows::window_set_input_text_callback(const Callable &p_call } void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -604,7 +584,6 @@ void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_call } void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -612,7 +591,6 @@ void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_wi } int DisplayServerWindows::window_get_current_screen(WindowID p_window) const { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), -1); @@ -621,8 +599,8 @@ int DisplayServerWindows::window_get_current_screen(WindowID p_window) const { EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data); return data.screen; } -void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) { +void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -633,7 +611,6 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi } Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), Point2i()); @@ -658,8 +635,8 @@ Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { return Point2(r.left, r.top); #endif } -void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) { +void DisplayServerWindows::_update_real_mouse_position(WindowID 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) { @@ -670,14 +647,15 @@ void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) { } } } -void DisplayServerWindows::window_set_position(const Point2i &p_position, WindowID p_window) { +void DisplayServerWindows::window_set_position(const Point2i &p_position, WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - if (wd.fullscreen) return; + if (wd.fullscreen) + return; #if 0 //wrong needs to account properly for decorations RECT r; @@ -711,7 +689,6 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window } void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(p_window == p_parent); @@ -748,7 +725,6 @@ void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_pa } void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -760,8 +736,8 @@ void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_w } wd.max_size = p_size; } -Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const { +Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); @@ -770,7 +746,6 @@ Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const { } void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -782,8 +757,8 @@ void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_w } wd.min_size = p_size; } -Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const { +Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); @@ -792,7 +767,6 @@ Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const { } void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -836,8 +810,8 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo ClipCursor(&crect); } } -Size2i DisplayServerWindows::window_get_size(WindowID p_window) const { +Size2i DisplayServerWindows::window_get_size(WindowID p_window) const { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); @@ -853,8 +827,8 @@ Size2i DisplayServerWindows::window_get_size(WindowID p_window) const { } return Size2(); } -Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const { +Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); @@ -868,7 +842,6 @@ Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const { } 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) { - r_style = 0; r_style_ex = WS_EX_WINDOWEDGE; if (p_main_window) { @@ -881,7 +854,6 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre // r_style_ex |= WS_EX_TOOLWINDOW; //} } else { - if (p_resizable) { if (p_maximized) { r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE; @@ -900,7 +872,6 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre } void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint, bool p_maximized) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -924,14 +895,12 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain } void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN) { - RECT rect; wd.fullscreen = false; @@ -953,21 +922,18 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) } if (p_mode == WINDOW_MODE_MAXIMIZED) { - ShowWindow(wd.hWnd, SW_MAXIMIZE); wd.maximized = true; wd.minimized = false; } if (p_mode == WINDOW_MODE_WINDOWED) { - ShowWindow(wd.hWnd, SW_RESTORE); wd.maximized = false; wd.minimized = false; } if (p_mode == WINDOW_MODE_MINIMIZED) { - ShowWindow(wd.hWnd, SW_MINIMIZE); wd.maximized = false; wd.minimized = true; @@ -996,8 +962,8 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); } } -DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const { +DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED); @@ -1015,7 +981,6 @@ DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_windo } bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), false); @@ -1026,76 +991,65 @@ bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const { } void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; switch (p_flag) { case WINDOW_FLAG_RESIZE_DISABLED: { - wd.resizable = !p_enabled; _update_window_style(p_window); } break; case WINDOW_FLAG_BORDERLESS: { - wd.borderless = p_enabled; _update_window_style(p_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"); wd.always_on_top = p_enabled; _update_window_style(p_window); } break; case WINDOW_FLAG_TRANSPARENT: { - // FIXME: Implement. } break; case WINDOW_FLAG_NO_FOCUS: { - wd.no_focus = p_enabled; _update_window_style(p_window); } break; - case WINDOW_FLAG_MAX: break; + case WINDOW_FLAG_MAX: + break; } } bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window) const { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), false); const WindowData &wd = windows[p_window]; switch (p_flag) { case WINDOW_FLAG_RESIZE_DISABLED: { - return !wd.resizable; } break; case WINDOW_FLAG_BORDERLESS: { - return wd.borderless; } break; case WINDOW_FLAG_ALWAYS_ON_TOP: { - return wd.always_on_top; } break; case WINDOW_FLAG_TRANSPARENT: { - // FIXME: Implement. } break; case WINDOW_FLAG_NO_FOCUS: { - return wd.no_focus; } break; - case WINDOW_FLAG_MAX: break; + case WINDOW_FLAG_MAX: + break; } return false; } void DisplayServerWindows::window_request_attention(WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -1109,8 +1063,8 @@ void DisplayServerWindows::window_request_attention(WindowID p_window) { info.uCount = 2; FlashWindowEx(&info); } -void DisplayServerWindows::window_move_to_foreground(WindowID p_window) { +void DisplayServerWindows::window_move_to_foreground(WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -1120,7 +1074,6 @@ void DisplayServerWindows::window_move_to_foreground(WindowID p_window) { } bool DisplayServerWindows::window_can_draw(WindowID p_window) const { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), false); @@ -1129,7 +1082,6 @@ bool DisplayServerWindows::window_can_draw(WindowID p_window) const { } bool DisplayServerWindows::can_any_window_draw() const { - _THREAD_SAFE_METHOD_ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { @@ -1142,7 +1094,6 @@ bool DisplayServerWindows::can_any_window_draw() const { } void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p_window) { - _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -1156,8 +1107,8 @@ void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p ImmAssociateContext(wd.hWnd, (HIMC)0); } } -void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { +void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!windows.has(p_window)); @@ -1178,7 +1129,6 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI } void DisplayServerWindows::console_set_visible(bool p_enabled) { - _THREAD_SAFE_METHOD_ if (console_visible == p_enabled) @@ -1186,12 +1136,12 @@ void DisplayServerWindows::console_set_visible(bool p_enabled) { 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); @@ -1232,12 +1182,12 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { cursor_shape = p_shape; } + DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const { return cursor_shape; } void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) { - // Get the system display DC HDC hDC = GetDC(nullptr); @@ -1287,11 +1237,9 @@ void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTra } void DisplayServerWindows::cursor_set_custom_image(const RES &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); if (cursor_c) { @@ -1429,72 +1377,102 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) { AllowSetForegroundWindow(pid); } -DisplayServer::LatinKeyboardVariant DisplayServerWindows::get_latin_keyboard_variant() const { +int DisplayServerWindows::keyboard_get_layout_count() const { + return GetKeyboardLayoutList(0, NULL); +} - _THREAD_SAFE_METHOD_ +int DisplayServerWindows::keyboard_get_current_layout() const { + HKL cur_layout = GetKeyboardLayout(0); - unsigned long azerty[] = { - 0x00020401, // Arabic (102) AZERTY - 0x0001080c, // Belgian (Comma) - 0x0000080c, // Belgian French - 0x0000040c, // French - 0 // <--- STOP MARK - }; - unsigned long qwertz[] = { - 0x0000041a, // Croation - 0x00000405, // Czech - 0x00000407, // German - 0x00010407, // German (IBM) - 0x0000040e, // Hungarian - 0x0000046e, // Luxembourgish - 0x00010415, // Polish (214) - 0x00000418, // Romanian (Legacy) - 0x0000081a, // Serbian (Latin) - 0x0000041b, // Slovak - 0x00000424, // Slovenian - 0x0001042e, // Sorbian Extended - 0x0002042e, // Sorbian Standard - 0x0000042e, // Sorbian Standard (Legacy) - 0x0000100c, // Swiss French - 0x00000807, // Swiss German - 0 // <--- STOP MARK - }; - unsigned long dvorak[] = { - 0x00010409, // US-Dvorak - 0x00030409, // US-Dvorak for left hand - 0x00040409, // US-Dvorak for right hand - 0 // <--- STOP MARK - }; + int layout_count = GetKeyboardLayoutList(0, NULL); + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + + for (int i = 0; i < layout_count; i++) { + if (cur_layout == layouts[i]) { + memfree(layouts); + return i; + } + } + memfree(layouts); + return -1; +} - char name[KL_NAMELENGTH + 1]; - name[0] = 0; - GetKeyboardLayoutNameA(name); +void DisplayServerWindows::keyboard_set_current_layout(int p_index) { + int layout_count = GetKeyboardLayoutList(0, NULL); - unsigned long hex = strtoul(name, nullptr, 16); + ERR_FAIL_INDEX(p_index, layout_count); - int i = 0; - while (azerty[i] != 0) { - if (azerty[i] == hex) return LATIN_KEYBOARD_AZERTY; - i++; + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS); + memfree(layouts); +} + +String DisplayServerWindows::keyboard_get_layout_language(int p_index) const { + int layout_count = GetKeyboardLayoutList(0, NULL); + + ERR_FAIL_INDEX_V(p_index, layout_count, ""); + + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + + wchar_t buf[LOCALE_NAME_MAX_LENGTH]; + memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t)); + LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); + + memfree(layouts); + + return String(buf).substr(0, 2); +} + +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; + + HKEY hkey; + wchar_t layout_text[1024]; + memset(layout_text, 0, 1024 * sizeof(wchar_t)); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)id.c_str(), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { + return ret; } - i = 0; - while (qwertz[i] != 0) { - if (qwertz[i] == hex) return LATIN_KEYBOARD_QWERTZ; - i++; + DWORD buffer = 1024; + DWORD vtype = REG_SZ; + if (RegQueryValueExW(hkey, L"Layout Text", NULL, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) { + ret = String(layout_text); } + RegCloseKey(hkey); + return ret; +} + +String DisplayServerWindows::keyboard_get_layout_name(int p_index) const { + int layout_count = GetKeyboardLayoutList(0, NULL); + + ERR_FAIL_INDEX_V(p_index, layout_count, ""); + + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); - i = 0; - while (dvorak[i] != 0) { - if (dvorak[i] == hex) return LATIN_KEYBOARD_DVORAK; - i++; + 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()) { + wchar_t buf[LOCALE_NAME_MAX_LENGTH]; + memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t)); + LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); + + wchar_t name[1024]; + memset(name, 0, 1024 * sizeof(wchar_t)); + GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024); + + ret = String(name); } + memfree(layouts); - return LATIN_KEYBOARD_QWERTY; + return ret; } void DisplayServerWindows::process_events() { - _THREAD_SAFE_METHOD_ MSG msg; @@ -1504,7 +1482,6 @@ void DisplayServerWindows::process_events() { } while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); DispatchMessageW(&msg); } @@ -1516,7 +1493,6 @@ void DisplayServerWindows::process_events() { } void DisplayServerWindows::force_process_and_drop_events() { - _THREAD_SAFE_METHOD_ drop_events = true; @@ -1526,13 +1502,14 @@ void DisplayServerWindows::force_process_and_drop_events() { void DisplayServerWindows::release_rendering_thread() { } + void DisplayServerWindows::make_rendering_thread() { } + void DisplayServerWindows::swap_buffers() { } void DisplayServerWindows::set_native_icon(const String &p_filename) { - _THREAD_SAFE_METHOD_ FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); @@ -1625,8 +1602,8 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { memdelete(f); memdelete(icon_dir); } -void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { +void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(!p_icon.is_valid()); @@ -1658,9 +1635,7 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { const uint8_t *r = icon->get_data().ptr(); for (int i = 0; i < h; i++) { - for (int j = 0; j < w; j++) { - const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4]; uint8_t *wpx = &wr[(i * w + j) * 4]; wpx[0] = rpx[2]; @@ -1681,6 +1656,7 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { void DisplayServerWindows::vsync_set_use_via_compositor(bool p_enable) { } + bool DisplayServerWindows::vsync_is_using_via_compositor() const { return false; } @@ -1697,7 +1673,6 @@ void DisplayServerWindows::set_context(Context p_context) { #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) return; @@ -1719,7 +1694,6 @@ void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float } 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) @@ -1741,7 +1715,6 @@ void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, } void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent p_event) { - if (!wd.event_callback.is_null()) { Variant event = int(p_event); Variant *eventp = &event; @@ -1756,7 +1729,12 @@ void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event } void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) { + _THREAD_SAFE_METHOD_ + if (in_dispatch_input_event) { + return; + } + in_dispatch_input_event = true; Variant ev = p_event; Variant *evp = &ev; Variant ret; @@ -1768,6 +1746,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) ERR_FAIL_COND(!windows.has(event_from_window->get_window_id())); Callable callable = windows[event_from_window->get_window_id()].input_event_callback; if (callable.is_null()) { + in_dispatch_input_event = false; return; } callable.call((const Variant **)&evp, 1, ret, ce); @@ -1781,14 +1760,13 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) callable.call((const Variant **)&evp, 1, ret, ce); } } + + in_dispatch_input_event = false; } LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - if (drop_events) { - if (user_proc) { - return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); } else { return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -1796,24 +1774,37 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA }; 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(); + window_created = true; break; } } + if (!window_created) { + // Window creation in progress. + window_id = window_id_counter; + ERR_FAIL_COND_V(!windows.has(window_id), 0); + } + switch (uMsg) // Check For Windows Messages { case WM_SETFOCUS: { - windows[window_id].window_has_focus = true; last_focused_window = window_id; // Restore mouse mode _set_mouse_mode_impl(mouse_mode); + if (!app_focused) { + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN); + } + app_focused = true; + } break; } case WM_KILLFOCUS: { @@ -1829,6 +1820,19 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } touch_state.clear(); + bool self_steal = false; + HWND new_hwnd = (HWND)wParam; + if (IsWindow(new_hwnd)) { + self_steal = true; + } + + if (!self_steal) { + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); + } + app_focused = false; + } + break; } case WM_ACTIVATE: // Watch For Window Activate Message @@ -1836,7 +1840,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA windows[window_id].minimized = HIWORD(wParam) != 0; if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { - _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN); windows[window_id].window_focused = true; alt_mem = false; @@ -1849,7 +1852,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA alt_mem = false; }; - return 0; // Return To The Message Loop + if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { + wintab_WTEnable(windows[window_id].wtctx, GET_WM_ACTIVATE_STATE(wParam, lParam)); + } + + return 0; // Return To The Message Loop } case WM_GETMINMAXINFO: { if (windows[window_id].resizable && !windows[window_id].fullscreen) { @@ -1889,13 +1896,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_CLOSE: // Did We Receive A Close Message? { - _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST); return 0; // Jump Back } case WM_MOUSELEAVE: { - old_invalid = true; outside = true; @@ -1929,6 +1934,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_shift(shift_mem); mm->set_alt(alt_mem); + mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f); + mm->set_button_mask(last_button_state); Point2i c(windows[window_id].width / 2, windows[window_id].height / 2); @@ -1947,7 +1954,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY)); } else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) { - int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN); int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN); int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN); @@ -1977,12 +1983,128 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } delete[] lpb; } break; + case WT_CSRCHANGE: + case WT_PROXIMITY: { + if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { + AXIS pressure; + if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { + windows[window_id].min_pressure = int(pressure.axMin); + windows[window_id].max_pressure = int(pressure.axMax); + } + AXIS orientation[3]; + if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_ORIENTATION, &orientation)) { + windows[window_id].tilt_supported = orientation[0].axResolution && orientation[1].axResolution; + } + return 0; + } + } break; + case WT_PACKET: { + if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { + PACKET packet; + if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) { + float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure); + windows[window_id].last_pressure = pressure; + windows[window_id].last_pressure_update = 0; + + double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180); + double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180)); + + if (windows[window_id].tilt_supported) { + windows[window_id].last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)); + } else { + windows[window_id].last_tilt = Vector2(); + } + + POINT coords; + GetCursorPos(&coords); + 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) + break; + + Ref<InputEventMouseMotion> mm; + mm.instance(); + 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_pressure(windows[window_id].last_pressure); + mm->set_tilt(windows[window_id].last_tilt); + + mm->set_button_mask(last_button_state); + + mm->set_position(Vector2(coords.x, coords.y)); + mm->set_global_position(Vector2(coords.x, coords.y)); + + if (mouse_mode == MOUSE_MODE_CAPTURED) { + Point2i c(windows[window_id].width / 2, windows[window_id].height / 2); + old_x = c.x; + old_y = c.y; + + if (mm->get_position() == c) { + center = c; + return 0; + } + + Point2i ncenter = mm->get_position(); + center = ncenter; + POINT pos = { (int)c.x, (int)c.y }; + ClientToScreen(windows[window_id].hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } + + Input::get_singleton()->set_mouse_position(mm->get_position()); + mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); + + if (old_invalid) { + old_x = mm->get_position().x; + old_y = mm->get_position().y; + old_invalid = false; + } + + 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); + } + return 0; + } + } break; + case WM_POINTERENTER: { + if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { + break; + } + + if ((OS::get_singleton()->get_current_tablet_driver() != "winink") || !winink_available) { + break; + } + + uint32_t pointer_id = LOWORD(wParam); + POINTER_INPUT_TYPE pointer_type = PT_POINTER; + if (!win8p_GetPointerType(pointer_id, &pointer_type)) { + break; + } + + if (pointer_type != PT_PEN) { + break; + } + + windows[window_id].block_mm = true; + return 0; + } break; + case WM_POINTERLEAVE: { + windows[window_id].block_mm = false; + return 0; + } break; case WM_POINTERUPDATE: { if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { break; } - if (!win8p_GetPointerType || !win8p_GetPointerPenInfo) { + if ((OS::get_singleton()->get_current_tablet_driver() != "winink") || !winink_available) { break; } @@ -2038,8 +2160,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm.instance(); mm->set_window_id(window_id); - mm->set_pressure(pen_info.pressure ? (float)pen_info.pressure / 1024 : 0); - mm->set_tilt(Vector2(pen_info.tiltX ? (float)pen_info.tiltX / 90 : 0, pen_info.tiltY ? (float)pen_info.tiltY / 90 : 0)); + if (pen_info.penMask & PEN_MASK_PRESSURE) { + mm->set_pressure((float)pen_info.pressure / 1024); + } else { + mm->set_pressure((HIWORD(wParam) & POINTER_MESSAGE_FLAG_FIRSTBUTTON) ? 1.0f : 0.0f); + } + if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) { + mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90)); + } mm->set_control((wParam & MK_CONTROL) != 0); mm->set_shift((wParam & MK_SHIFT) != 0); @@ -2057,7 +2185,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_global_position(Vector2(coords.x, coords.y)); if (mouse_mode == MOUSE_MODE_CAPTURED) { - Point2i c(windows[window_id].width / 2, windows[window_id].height / 2); old_x = c.x; old_y = c.y; @@ -2078,7 +2205,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); if (old_invalid) { - old_x = mm->get_position().x; old_y = mm->get_position().y; old_invalid = false; @@ -2088,12 +2214,16 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA old_x = mm->get_position().x; old_y = mm->get_position().y; if (windows[window_id].window_has_focus) { - Input::get_singleton()->parse_input_event(mm); + Input::get_singleton()->accumulate_input_event(mm); } return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event } break; case WM_MOUSEMOVE: { + if (windows[window_id].block_mm) { + break; + } + if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { break; } @@ -2138,13 +2268,28 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_shift((wParam & MK_SHIFT) != 0); mm->set_alt(alt_mem); + if ((OS::get_singleton()->get_current_tablet_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 update recently. + if (windows[window_id].last_pressure_update < 10) { + windows[window_id].last_pressure_update++; + } else { + windows[window_id].last_tilt = Vector2(); + windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f; + } + } else { + windows[window_id].last_tilt = Vector2(); + windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f; + } + + mm->set_pressure(windows[window_id].last_pressure); + mm->set_tilt(windows[window_id].last_tilt); + mm->set_button_mask(last_button_state); mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); if (mouse_mode == MOUSE_MODE_CAPTURED) { - Point2i c(windows[window_id].width / 2, windows[window_id].height / 2); old_x = c.x; old_y = c.y; @@ -2165,7 +2310,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); if (old_invalid) { - old_x = mm->get_position().x; old_y = mm->get_position().y; old_invalid = false; @@ -2200,7 +2344,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_XBUTTONDBLCLK: case WM_XBUTTONDOWN: case WM_XBUTTONUP: { - Ref<InputEventMouseButton> mb; mb.instance(); mb->set_window_id(window_id); @@ -2246,7 +2389,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mb->set_doubleclick(true); } break; case WM_MOUSEWHEEL: { - mb->set_pressed(true); int motion = (short)HIWORD(wParam); if (!motion) @@ -2259,7 +2401,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_MOUSEHWHEEL: { - mb->set_pressed(true); int motion = (short)HIWORD(wParam); if (!motion) @@ -2274,7 +2415,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } break; case WM_XBUTTONDOWN: { - mb->set_pressed(true); if (HIWORD(wParam) == XBUTTON1) mb->set_button_index(BUTTON_XBUTTON1); @@ -2282,7 +2422,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mb->set_button_index(BUTTON_XBUTTON2); } break; case WM_XBUTTONUP: { - mb->set_pressed(false); if (HIWORD(wParam) == XBUTTON1) mb->set_button_index(BUTTON_XBUTTON1); @@ -2290,7 +2429,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mb->set_button_index(BUTTON_XBUTTON2); } break; case WM_XBUTTONDBLCLK: { - mb->set_pressed(true); if (HIWORD(wParam) == XBUTTON1) mb->set_button_index(BUTTON_XBUTTON1); @@ -2316,17 +2454,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) { - mb->set_position(Vector2(old_x, old_y)); } if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) { if (mb->is_pressed()) { - if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED) SetCapture(hWnd); } else { - if (--pressrc <= 0) { if (mouse_mode != MOUSE_MODE_CAPTURED) { ReleaseCapture(); @@ -2367,7 +2502,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA 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; @@ -2387,7 +2521,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA windows[window_id].height = window_h; #if defined(VULKAN_ENABLED) - if (rendering_driver == "vulkan") { + if ((rendering_driver == "vulkan") && window_created) { context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].height); } #endif @@ -2399,7 +2533,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } 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; @@ -2463,11 +2596,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_SYSKEYUP: case WM_KEYUP: case WM_KEYDOWN: { - if (wParam == VK_SHIFT) - shift_mem = uMsg == WM_KEYDOWN; + shift_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); if (wParam == VK_CONTROL) - control_mem = uMsg == WM_KEYDOWN; + control_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); if (wParam == VK_MENU) { alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); if (lParam & (1 << 24)) @@ -2487,7 +2619,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA [[fallthrough]]; } case WM_CHAR: { - ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE); // Make sure we don't include modifiers for the modifier key itself. @@ -2510,12 +2641,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_INPUTLANGCHANGEREQUEST: { - // FIXME: Do something? } break; case WM_TOUCH: { - BOOL bHandled = FALSE; UINT cInputs = LOWORD(wParam); PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs); @@ -2530,10 +2659,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA ScreenToClient(hWnd, &touch_pos); //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); }; } @@ -2553,17 +2680,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_DEVICECHANGE: { - joypad->probe_joypads(); } 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 (hCursor == nullptr) + if (hCursor == nullptr) { hCursor = SetCursor(nullptr); - else + } else { SetCursor(nullptr); + } } else { if (hCursor != nullptr) { CursorShape c = cursor_shape; @@ -2576,7 +2703,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_DROPFILES: { - HDROP hDropInfo = (HDROP)wParam; const int buffsize = 4096; wchar_t buf[buffsize]; @@ -2586,7 +2712,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Vector<String> files; for (int i = 0; i < fcount; i++) { - DragQueryFileW(hDropInfo, i, buf, buffsize); String file = buf; files.push_back(file); @@ -2603,9 +2728,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; default: { - if (user_proc) { - return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); }; }; @@ -2615,7 +2738,6 @@ 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) return ds_win->WndProc(hWnd, uMsg, wParam, lParam); @@ -2624,14 +2746,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { } void DisplayServerWindows::_process_key_events() { - for (int i = 0; i < key_event_pos; i++) { - KeyEvent &ke = key_event_buffer[i]; switch (ke.uMsg) { - case WM_CHAR: { - if ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR)) { + // 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))) { Ref<InputEventKey> k; k.instance(); @@ -2659,7 +2779,6 @@ void DisplayServerWindows::_process_key_events() { } break; case WM_KEYUP: case WM_KEYDOWN: { - Ref<InputEventKey> k; k.instance(); @@ -2702,8 +2821,46 @@ void DisplayServerWindows::_process_key_events() { key_event_pos = 0; } -DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) { +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(); + wd.block_mm = false; + if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) { + wintab_WTEnable(wd.wtctx, false); + wintab_WTClose(wd.wtctx); + wd.wtctx = 0; + } + if ((p_new_driver == "wintab") && wintab_available) { + wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc); + wd.wtlc.lcOptions |= CXO_MESSAGES; + wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; + wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE; + wd.wtlc.lcPktMode = 0; + wd.wtlc.lcOutOrgX = 0; + wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX; + wd.wtlc.lcOutOrgY = 0; + wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY; + wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false); + if (wd.wtctx) { + wintab_WTEnable(wd.wtctx, true); + AXIS pressure; + if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { + wd.min_pressure = int(pressure.axMin); + wd.max_pressure = int(pressure.axMax); + } + AXIS orientation[3]; + if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) { + wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution; + } + wintab_WTEnable(wd.wtctx, true); + } else { + print_verbose("WinTab context creation failed."); + } + } + } +} +DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) { DWORD dwExStyle; DWORD dwStyle; @@ -2716,11 +2873,32 @@ 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) { + int nearest_area = 0; + Rect2i screen_rect; + for (int i = 0; i < get_screen_count(); i++) { + Rect2i r; + r.position = screen_get_position(i); + r.size = screen_get_size(i); + Rect2 inters = r.clip(p_rect); + int area = inters.size.width * inters.size.height; + if (area >= nearest_area) { + screen_rect = r; + nearest_area = area; + } + } + + WindowRect.left = screen_rect.position.x; + WindowRect.right = screen_rect.position.x + screen_rect.size.x; + WindowRect.top = screen_rect.position.y; + WindowRect.bottom = screen_rect.position.y + screen_rect.size.y; + } + AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); WindowID id = window_id_counter; { - WindowData wd; + WindowData &wd = windows[id]; wd.hWnd = CreateWindowExW( dwExStyle, @@ -2735,6 +2913,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, nullptr, nullptr, hInstance, nullptr); if (!wd.hWnd) { MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); + windows.erase(id); return INVALID_WINDOW_ID; } #ifdef VULKAN_ENABLED @@ -2743,7 +2922,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, if (context_vulkan->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) { memdelete(context_vulkan); context_vulkan = nullptr; - ERR_FAIL_V(INVALID_WINDOW_ID); + windows.erase(id); + ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Vulkan Window."); } } #endif @@ -2759,6 +2939,39 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, DragAcceptFiles(wd.hWnd, true); + if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available) { + wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc); + wd.wtlc.lcOptions |= CXO_MESSAGES; + wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; + wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE; + wd.wtlc.lcPktMode = 0; + wd.wtlc.lcOutOrgX = 0; + wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX; + wd.wtlc.lcOutOrgY = 0; + wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY; + wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false); + if (wd.wtctx) { + wintab_WTEnable(wd.wtctx, true); + AXIS pressure; + if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { + wd.min_pressure = int(pressure.axMin); + wd.max_pressure = int(pressure.axMax); + } + AXIS orientation[3]; + if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) { + wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution; + } + } else { + print_verbose("WinTab context creation failed."); + } + } else { + wd.wtctx = 0; + } + + wd.last_pressure = 0; + wd.last_pressure_update = 0; + wd.last_tilt = Vector2(); + // IME wd.im_himc = ImmGetContext(wd.hWnd); ImmReleaseContext(wd.hWnd, wd.im_himc); @@ -2768,14 +2981,22 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wd.width = p_rect.size.width; wd.height = p_rect.size.height; - windows[id] = wd; - window_id_counter++; } return id; } +// WinTab API +bool DisplayServerWindows::wintab_available = false; +WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr; +WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr; +WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr; +WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr; +WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr; + +// Windows Ink API +bool DisplayServerWindows::winink_available = false; GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr; GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr; @@ -2786,14 +3007,6 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS { } SHC_PROCESS_DPI_AWARENESS; DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - - //Note: Functions for pen input, available on Windows 8+ - HMODULE user32_lib = LoadLibraryW(L"user32.dll"); - if (user32_lib) { - win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); - win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo"); - } - drop_events = false; key_event_pos = 0; @@ -2863,7 +3076,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - context_vulkan = memnew(VulkanContextWindows); if (context_vulkan->initialize() != OK) { memdelete(context_vulkan); @@ -2875,7 +3087,6 @@ 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)); if (context_gles2->initialize() != OK) { @@ -2900,7 +3111,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win 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)); + ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); + for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { window_set_flag(WindowFlags(i), true, main_window); @@ -2914,7 +3128,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - rendering_device_vulkan = memnew(RenderingDeviceVulkan); rendering_device_vulkan->initialize(context_vulkan); @@ -2965,35 +3178,19 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() { } DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - return memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_flags, p_resolution, r_error)); } void DisplayServerWindows::register_windows_driver() { - register_create_function("windows", create_func, get_rendering_drivers_func); } DisplayServerWindows::~DisplayServerWindows() { - delete joypad; touch_state.clear(); cursors_cache.clear(); -#if defined(VULKAN_ENABLED) - if (rendering_driver == "vulkan") { - - if (rendering_device_vulkan) { - rendering_device_vulkan->finalize(); - memdelete(rendering_device_vulkan); - } - - if (context_vulkan) - memdelete(context_vulkan); - } -#endif - if (user_proc) { SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc); }; @@ -3004,7 +3201,22 @@ DisplayServerWindows::~DisplayServerWindows() { context_vulkan->window_destroy(MAIN_WINDOW_ID); } #endif - + if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) { + wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx); + windows[MAIN_WINDOW_ID].wtctx = 0; + } DestroyWindow(windows[MAIN_WINDOW_ID].hWnd); } + +#if defined(VULKAN_ENABLED) + if (rendering_driver == "vulkan") { + if (rendering_device_vulkan) { + rendering_device_vulkan->finalize(); + memdelete(rendering_device_vulkan); + } + + if (context_vulkan) + memdelete(context_vulkan); + } +#endif } |