diff --git a/core/os/os.h b/core/os/os.h index e15bc90e86e..17b688efc4b 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -286,6 +286,7 @@ public: virtual bool request_permissions() { return true; } virtual Vector get_granted_permissions() const { return Vector(); } + bool is_hidpi_allowed() const { return _allow_hidpi; } virtual void process_and_drop_events() {} OS(); virtual ~OS(); diff --git a/main/main.cpp b/main/main.cpp index 8407259124b..df603744b68 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1964,7 +1964,7 @@ bool Main::start() { #ifdef WINDOWS_ENABLED String win_iconpath = GLOBAL_DEF("application/config/windows_native_icon", "Variant()"); if (win_iconpath != "") { - OS::get_singleton()->set_native_icon(win_iconpath); + DisplayServer::get_singleton()->set_native_icon(win_iconpath); hasicon = true; } #endif diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 8e94c7b35df..89ee4dfa7a7 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -10,6 +10,7 @@ common_win = [ "godot_windows.cpp", "crash_handler_windows.cpp", "os_windows.cpp", + "display_server_windows.cpp", "key_mapping_windows.cpp", "joypad_windows.cpp", "windows_terminal_logger.cpp", diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp new file mode 100644 index 00000000000..d705ce631a7 --- /dev/null +++ b/platform/windows/display_server_windows.cpp @@ -0,0 +1,2814 @@ +#include "display_server_windows.h" +#include "core/io/marshalls.h" +#include "main/main.h" +#include "os_windows.h" +#include "scene/resources/texture.h" + +#include + +#ifndef WM_POINTERUPDATE +#define WM_POINTERUPDATE 0x0245 +#endif + +#ifdef DEBUG_ENABLED +static String format_error_message(DWORD id) { + + LPWSTR messageBuffer = NULL; + size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); + + String msg = "Error " + itos(id) + ": " + String(messageBuffer, size); + + LocalFree(messageBuffer); + + return msg; +} +#endif // DEBUG_ENABLED + +bool DisplayServerWindows::has_feature(Feature p_feature) const { + switch (p_feature) { + case FEATURE_SUBWINDOWS: + case FEATURE_TOUCHSCREEN: + case FEATURE_MOUSE: + case FEATURE_MOUSE_WARP: + 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: + case FEATURE_ICON: + case FEATURE_NATIVE_ICON: + case FEATURE_SWAP_BUFFERS: + case FEATURE_KEEP_SCREEN_ON: + return true; + default: + return false; + } +} + +String DisplayServerWindows::get_name() const { + return "Windows"; +} + +void DisplayServerWindows::alert(const String &p_alert, const String &p_title) { + MessageBoxW(NULL, p_alert.c_str(), p_title.c_str(), 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]; + + RECT clipRect; + GetClientRect(wd.hWnd, &clipRect); + ClientToScreen(wd.hWnd, (POINT *)&clipRect.left); + 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); + SetCursorPos(pos.x, pos.y); + SetCapture(wd.hWnd); + } + } else { + ReleaseCapture(); + ClipCursor(NULL); + } + + if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) { + hCursor = SetCursor(NULL); + } else { + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + cursor_set_shape(c); + } +} +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; +} +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)) { + return; //no window focused? + } + + 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; + ClientToScreen(windows[last_focused_window].hWnd, &p); + + SetCursorPos(p.x, p.y); + } +} +Point2i DisplayServerWindows::mouse_get_position() const { + 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)) { + return; //no window focused? + } + + // 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("\n", "\r\n"); + + if (!OpenClipboard(windows[last_focused_window].hWnd)) { + ERR_FAIL_MSG("Unable to open clipboard."); + } + EmptyClipboard(); + + HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(CharType)); + ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents."); + + LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem); + memcpy(lptstrCopy, text.c_str(), (text.length() + 1) * sizeof(CharType)); + GlobalUnlock(mem); + + SetClipboardData(CF_UNICODETEXT, mem); + + // set the CF_TEXT version (not needed?) + CharString utf8 = text.utf8(); + mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1); + ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents."); + + LPTSTR ptr = (LPTSTR)GlobalLock(mem); + memcpy(ptr, utf8.get_data(), utf8.length()); + ptr[utf8.length()] = 0; + GlobalUnlock(mem); + + SetClipboardData(CF_TEXT, mem); + + CloseClipboard(); +} +String DisplayServerWindows::clipboard_get() const { + + _THREAD_SAFE_METHOD_ + + if (!windows.has(last_focused_window)) { + return String(); //no window focused? + } + + 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); + if (mem != NULL) { + + LPWSTR ptr = (LPWSTR)GlobalLock(mem); + if (ptr != NULL) { + + ret = String((CharType *)ptr); + GlobalUnlock(mem); + }; + }; + + } else if (IsClipboardFormatAvailable(CF_TEXT)) { + + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem != NULL) { + + LPTSTR ptr = (LPTSTR)GlobalLock(mem); + if (ptr != NULL) { + + ret.parse_utf8((const char *)ptr); + GlobalUnlock(mem); + }; + }; + }; + + CloseClipboard(); + + return ret; +} + +typedef struct { + int count; + int screen; + HMONITOR monitor; +} 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; + } + + data->count++; + return TRUE; +} + +static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + + int *data = (int *)dwData; + (*data)++; + return TRUE; +} + +int DisplayServerWindows::get_screen_count() const { + _THREAD_SAFE_METHOD_ + + int data = 0; + EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcCount, (LPARAM)&data); + return data; +} + +typedef struct { + int count; + int screen; + Point2 pos; +} 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; + data->pos.y = lprcMonitor->top; + } + + data->count++; + return TRUE; +} +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() }; + EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcPos, (LPARAM)&data); + return data.pos; +} + +typedef struct { + int count; + int screen; + Size2 size; +} EnumSizeData; + +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; + data->size.y = lprcMonitor->bottom - lprcMonitor->top; + } + + data->count++; + return TRUE; +} + +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() }; + EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data); + return data.size; +} + +typedef struct { + int count; + int screen; + int dpi; +} EnumDpiData; + +enum _MonitorDpiType { + MDT_Effective_DPI = 0, + MDT_Angular_DPI = 1, + MDT_Raw_DPI = 2, + MDT_Default = MDT_Effective_DPI +}; + +static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Default) { + + int dpiX = 96, dpiY = 96; + + static HMODULE Shcore = NULL; + typedef HRESULT(WINAPI * GetDPIForMonitor_t)(HMONITOR hmonitor, _MonitorDpiType dpiType, UINT * dpiX, UINT * dpiY); + static GetDPIForMonitor_t getDPIForMonitor = NULL; + + if (Shcore == NULL) { + Shcore = LoadLibraryW(L"Shcore.dll"); + getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : NULL; + + if ((Shcore == NULL) || (getDPIForMonitor == NULL)) { + 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); + if (SUCCEEDED(hr) && (x > 0) && (y > 0)) { + + dpiX = (int)x; + dpiY = (int)y; + } + } else { + static int overallX = 0, overallY = 0; + if (overallX <= 0 || overallY <= 0) { + HDC hdc = GetDC(NULL); + if (hdc) { + overallX = GetDeviceCaps(hdc, LOGPIXELSX); + overallY = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + } + } + if (overallX > 0 && overallY > 0) { + dpiX = overallX; + dpiY = overallY; + } + } + + return (dpiX + dpiY) / 2; +} + +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); + } + + data->count++; + return TRUE; +} + +int DisplayServerWindows::screen_get_dpi(int p_screen) const { + _THREAD_SAFE_METHOD_ + + EnumDpiData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, 72 }; + EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcDpi, (LPARAM)&data); + return data.dpi; +} +bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const { +#ifndef _MSC_VER +#warning touchscreen not working +#endif + return false; +} + +void DisplayServerWindows::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) { +} +DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(int p_screen) const { + return SCREEN_LANDSCAPE; +} + +void DisplayServerWindows::screen_set_keep_on(bool p_enable) { +} +bool DisplayServerWindows::screen_is_kept_on() const { + return false; +} + +Vector DisplayServerWindows::get_window_list() const { + + _THREAD_SAFE_METHOD_ + + Vector ret; + for (Map::Element *E = windows.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +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_rect); + for (int i = 0; i < WINDOW_FLAG_MAX; i++) { + if (p_flags & (1 << i)) { + window_set_flag(WindowFlags(i), true, window_id); + } + } + + return window_id; +} +void DisplayServerWindows::delete_sub_window(WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted."); + + WindowData &wd = windows[p_window]; + + while (wd.transient_children.size()) { + window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID); + } + + if (wd.transient_parent != INVALID_WINDOW_ID) { + window_set_transient(p_window, INVALID_WINDOW_ID); + } + +#ifdef VULKAN_ENABLED + if (rendering_driver == "vulkan") { + context_vulkan->window_destroy(p_window); + } +#endif + + DestroyWindow(windows[p_window].hWnd); + windows.erase(p_window); +} + +void DisplayServerWindows::window_set_resize_callback(const Callable &p_callable, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + windows[p_window].resize_callback = p_callable; +} + +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) { + + _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) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + windows[p_window].input_text_callback = p_callable; +} + +void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + windows[p_window].drop_files_callback = p_callable; +} + +void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + SetWindowTextW(windows[p_window].hWnd, p_title.c_str()); +} + +int DisplayServerWindows::window_get_current_screen(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), -1); + + EnumScreenData data = { 0, 0, MonitorFromWindow(windows[p_window].hWnd, MONITOR_DEFAULTTONEAREST) }; + EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcScreen, (LPARAM)&data); + return data.screen; +} +void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + 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); +} + +Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), Point2i()); + const WindowData &wd = windows[p_window]; + + if (wd.minimized) { + return wd.last_pos; + } + + RECT r; + GetWindowRect(wd.hWnd, &r); + return Point2(r.left, r.top); +} +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) { + old_x = mouse_pos.x; + old_y = mouse_pos.y; + old_invalid = false; + InputFilter::get_singleton()->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y)); + } + } +} +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; + RECT r; + GetWindowRect(wd.hWnd, &r); + MoveWindow(wd.hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE); + + // Don't let the mouse leave the window when moved + if (mouse_mode == MOUSE_MODE_CONFINED) { + RECT rect; + GetClientRect(wd.hWnd, &rect); + ClientToScreen(wd.hWnd, (POINT *)&rect.left); + ClientToScreen(wd.hWnd, (POINT *)&rect.right); + ClipCursor(&rect); + } + + wd.last_pos = p_position; + _update_real_mouse_position(p_window); +} + +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 + + ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID); + ERR_FAIL_COND(!windows.has(wd_window.transient_parent)); + + WindowData &wd_parent = windows[wd_window.transient_parent]; + + wd_window.transient_parent = INVALID_WINDOW_ID; + wd_parent.transient_children.erase(p_window); + + SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, NULL); + } 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"); + WindowData &wd_parent = windows[p_parent]; + + wd_window.transient_parent = p_parent; + wd_parent.transient_children.insert(p_window); + + SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd); + } +} + +void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + if ((p_size != Size2()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) { + ERR_PRINT("Maximum window size can't be smaller than minimum window size!"); + return; + } + wd.max_size = p_size; +} +Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); + const WindowData &wd = windows[p_window]; + return wd.max_size; +} + +void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + if ((p_size != Size2()) && (wd.max_size != Size2()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) { + ERR_PRINT("Minimum window size can't be larger than maximum window size!"); + return; + } + wd.min_size = p_size; +} +Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); + const WindowData &wd = windows[p_window]; + return wd.min_size; +} + +void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + int w = p_size.width; + int h = p_size.height; + + wd.width = w; + wd.height = h; +#if defined(VULKAN_ENABLED) + if (rendering_driver == "vulkan") { + context_vulkan->window_resize(p_window, w, h); + } +#endif + + if (wd.fullscreen) { + return; + } + + RECT rect; + GetWindowRect(wd.hWnd, &rect); + + if (!wd.borderless) { + RECT crect; + GetClientRect(wd.hWnd, &crect); + + w += (rect.right - rect.left) - (crect.right - crect.left); + h += (rect.bottom - rect.top) - (crect.bottom - crect.top); + } + + 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) { + RECT crect; + GetClientRect(wd.hWnd, &crect); + ClientToScreen(wd.hWnd, (POINT *)&crect.left); + ClientToScreen(wd.hWnd, (POINT *)&crect.right); + ClipCursor(&crect); + } +} +Size2i DisplayServerWindows::window_get_size(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); + const WindowData &wd = windows[p_window]; + + if (wd.minimized) { + return Size2(wd.width, wd.height); + } + + RECT r; + if (GetClientRect(wd.hWnd, &r)) { // Only area inside of window border + return Size2(r.right - r.left, r.bottom - r.top); + } + return Size2(); +} +Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); + const WindowData &wd = windows[p_window]; + + RECT r; + if (GetWindowRect(wd.hWnd, &r)) { // Includes area of the window border + return Size2(r.right - r.left, r.bottom - r.top); + } + return Size2(); +} + +void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint, bool p_maximized) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + if (wd.fullscreen || wd.borderless) { + SetWindowLongPtr(wd.hWnd, GWL_STYLE, WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE); + } else { + if (wd.resizable) { + if (p_maximized) { + SetWindowLongPtr(wd.hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE); + } else { + SetWindowLongPtr(wd.hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE); + } + } else { + SetWindowLongPtr(wd.hWnd, GWL_STYLE, WS_CAPTION | WS_MINIMIZEBOX | WS_POPUPWINDOW | WS_VISIBLE); + } + } + + SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE); + + if (p_repaint) { + RECT rect; + GetWindowRect(wd.hWnd, &rect); + MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); + } +} + +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; + + if (wd.pre_fs_valid) { + rect = wd.pre_fs_rect; + } else { + rect.left = 0; + rect.right = wd.width; + rect.top = 0; + rect.bottom = wd.height; + } + + _update_window_style(p_window, false, wd.was_maximized); + + MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); + + wd.pre_fs_valid = true; + } + + 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; + } + + if (p_mode == WINDOW_MODE_FULLSCREEN && !wd.fullscreen) { + if (wd.minimized) { + ShowWindow(wd.hWnd, SW_RESTORE); + } + wd.was_maximized = wd.maximized; + + if (wd.pre_fs_valid) { + GetWindowRect(wd.hWnd, &wd.pre_fs_rect); + } + + int cs = window_get_current_screen(p_window); + Point2 pos = screen_get_position(cs); + Size2 size = screen_get_size(cs); + + wd.fullscreen = true; + wd.maximized = false; + wd.minimized = false; + + _update_window_style(false); + + MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); + } +} +DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED); + const WindowData &wd = windows[p_window]; + + if (wd.fullscreen) { + return WINDOW_MODE_FULLSCREEN; + } else if (wd.minimized) { + return WINDOW_MODE_MINIMIZED; + } else if (wd.maximized) { + return WINDOW_MODE_MAXIMIZED; + } else { + return WINDOW_MODE_WINDOWED; + } +} + +bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), false); + const WindowData &wd = windows[p_window]; + + return true; //no idea +} + +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: { + + } 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: { + + } break; + } + + return false; +} + +void DisplayServerWindows::window_request_attention(WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + FLASHWINFO info; + info.cbSize = sizeof(FLASHWINFO); + info.hwnd = wd.hWnd; + info.dwFlags = FLASHW_TRAY; + info.dwTimeout = 0; + info.uCount = 2; + FlashWindowEx(&info); +} +void DisplayServerWindows::window_move_to_foreground(WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + SetForegroundWindow(wd.hWnd); +} + +bool DisplayServerWindows::window_can_draw(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), false); + const WindowData &wd = windows[p_window]; + return wd.minimized; +} + +bool DisplayServerWindows::can_any_window_draw() const { + + _THREAD_SAFE_METHOD_ + + for (Map::Element *E = windows.front(); E; E = E->next()) { + if (!E->get().minimized) { + return true; + } + } + + return false; +} + +void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + if (p_active) { + ImmAssociateContext(wd.hWnd, wd.im_himc); + + window_set_ime_position(wd.im_position, p_window); + } else { + ImmAssociateContext(wd.hWnd, (HIMC)0); + } +} +void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + wd.im_position = p_pos; + + HIMC himc = ImmGetContext(wd.hWnd); + if (himc == (HIMC)0) + return; + + COMPOSITIONFORM cps; + cps.dwStyle = CFS_FORCE_POSITION; + cps.ptCurrentPos.x = wd.im_position.x; + cps.ptCurrentPos.y = wd.im_position.y; + ImmSetCompositionWindow(himc, &cps); + 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) + return; + + if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { + cursor_shape = p_shape; + return; + } + + static const LPCTSTR win_cursors[CURSOR_MAX] = { + IDC_ARROW, + IDC_IBEAM, + IDC_HAND, //finger + IDC_CROSS, + IDC_WAIT, + IDC_APPSTARTING, + IDC_ARROW, + IDC_ARROW, + IDC_NO, + IDC_SIZENS, + IDC_SIZEWE, + IDC_SIZENESW, + IDC_SIZENWSE, + IDC_SIZEALL, + IDC_SIZENS, + IDC_SIZEWE, + IDC_HELP + }; + + if (cursors[p_shape] != NULL) { + SetCursor(cursors[p_shape]); + } else { + SetCursor(LoadCursor(hInstance, win_cursors[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(NULL); + + // Create helper DC + HDC hMainDC = CreateCompatibleDC(hDC); + HDC hAndMaskDC = CreateCompatibleDC(hDC); + HDC hXorMaskDC = CreateCompatibleDC(hDC); + + // Get the dimensions of the source bitmap + BITMAP bm; + GetObject(hSourceBitmap, sizeof(BITMAP), &bm); + + // Create the mask bitmaps + hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color + hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color + + // Release the system display DC + ReleaseDC(NULL, hDC); + + // Select the bitmaps to helper DC + HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap); + HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap); + HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap); + + // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap + // with 'clrTransparent' will be white pixels of the monochrome bitmap + SetBkColor(hMainDC, clrTransparent); + BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY); + + // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap + // with 'clrTransparent' will be black and rest the pixels same as corresponding + // pixels of the source bitmap + SetBkColor(hXorMaskDC, RGB(0, 0, 0)); + SetTextColor(hXorMaskDC, RGB(255, 255, 255)); + BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY); + BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND); + + // Deselect bitmaps from the helper DC + SelectObject(hMainDC, hOldMainBitmap); + SelectObject(hAndMaskDC, hOldAndMaskBitmap); + SelectObject(hXorMaskDC, hOldXorMaskBitmap); + + // Delete the helper DC + DeleteDC(hXorMaskDC); + DeleteDC(hAndMaskDC); + DeleteDC(hMainDC); +} + +void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + + _THREAD_SAFE_METHOD_ + + if (p_cursor.is_valid()) { + + Map>::Element *cursor_c = cursors_cache.find(p_shape); + + if (cursor_c) { + if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) { + cursor_set_shape(p_shape); + return; + } + + cursors_cache.erase(p_shape); + } + + Ref texture = p_cursor; + Ref atlas_texture = p_cursor; + Ref image; + Size2 texture_size; + Rect2 atlas_rect; + + if (texture.is_valid()) { + image = texture->get_data(); + } + + if (!image.is_valid() && atlas_texture.is_valid()) { + texture = atlas_texture->get_atlas(); + + atlas_rect.size.width = texture->get_width(); + atlas_rect.size.height = texture->get_height(); + atlas_rect.position.x = atlas_texture->get_region().position.x; + atlas_rect.position.y = atlas_texture->get_region().position.y; + + texture_size.width = atlas_texture->get_region().size.x; + texture_size.height = atlas_texture->get_region().size.y; + } else if (image.is_valid()) { + texture_size.width = texture->get_width(); + texture_size.height = texture->get_height(); + } + + ERR_FAIL_COND(!texture.is_valid()); + ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); + 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(); + + ERR_FAIL_COND(!image.is_valid()); + + UINT image_size = texture_size.width * texture_size.height; + + // Create the BITMAP with alpha channel + COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size); + + for (UINT index = 0; index < image_size; index++) { + int row_index = floor(index / texture_size.width) + atlas_rect.position.y; + int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; + + if (atlas_texture.is_valid()) { + column_index = MIN(column_index, atlas_rect.size.width - 1); + row_index = MIN(row_index, atlas_rect.size.height - 1); + } + + *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32(); + } + + // Using 4 channels, so 4 * 8 bits + HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer); + COLORREF clrTransparent = -1; + + // Create the AND and XOR masks for the bitmap + HBITMAP hAndMask = NULL; + HBITMAP hXorMask = NULL; + + GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask); + + if (NULL == hAndMask || NULL == hXorMask) { + memfree(buffer); + DeleteObject(bitmap); + return; + } + + // Finally, create the icon + ICONINFO iconinfo; + iconinfo.fIcon = FALSE; + iconinfo.xHotspot = p_hotspot.x; + iconinfo.yHotspot = p_hotspot.y; + iconinfo.hbmMask = hAndMask; + iconinfo.hbmColor = hXorMask; + + if (cursors[p_shape]) + DestroyIcon(cursors[p_shape]); + + cursors[p_shape] = CreateIconIndirect(&iconinfo); + + Vector params; + params.push_back(p_cursor); + params.push_back(p_hotspot); + cursors_cache.insert(p_shape, params); + + if (p_shape == cursor_shape) { + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + SetCursor(cursors[p_shape]); + } + } + + if (hAndMask != NULL) { + DeleteObject(hAndMask); + } + + if (hXorMask != NULL) { + DeleteObject(hXorMask); + } + + memfree(buffer); + DeleteObject(bitmap); + } else { + // Reset to default system cursor + if (cursors[p_shape]) { + DestroyIcon(cursors[p_shape]); + cursors[p_shape] = NULL; + } + + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + cursor_set_shape(c); + + cursors_cache.erase(p_shape); + } +} + +bool DisplayServerWindows::get_swap_ok_cancel() { + return true; +} + +void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) { + _THREAD_SAFE_METHOD_ + + AllowSetForegroundWindow(pid); +} + +DisplayServer::LatinKeyboardVariant DisplayServerWindows::get_latin_keyboard_variant() const { + + _THREAD_SAFE_METHOD_ + + 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 + }; + + char name[KL_NAMELENGTH + 1]; + name[0] = 0; + GetKeyboardLayoutNameA(name); + + unsigned long hex = strtoul(name, NULL, 16); + + int i = 0; + while (azerty[i] != 0) { + if (azerty[i] == hex) return LATIN_KEYBOARD_AZERTY; + i++; + } + + i = 0; + while (qwertz[i] != 0) { + if (qwertz[i] == hex) return LATIN_KEYBOARD_QWERTZ; + i++; + } + + i = 0; + while (dvorak[i] != 0) { + if (dvorak[i] == hex) return LATIN_KEYBOARD_DVORAK; + i++; + } + + return LATIN_KEYBOARD_QWERTY; +} + +void DisplayServerWindows::process_events() { + + _THREAD_SAFE_METHOD_ + + MSG msg; + + if (!drop_events) { + joypad->process_joypads(); + } + + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + if (!drop_events) { + _process_key_events(); + InputFilter::get_singleton()->flush_accumulated_events(); + } +} + +void DisplayServerWindows::force_process_and_drop_events() { + + _THREAD_SAFE_METHOD_ + + drop_events = true; + process_events(); + drop_events = false; +} + +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); + ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'."); + + ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR)); + int pos = 0; + + icon_dir->idReserved = f->get_32(); + pos += sizeof(WORD); + f->seek(pos); + + icon_dir->idType = f->get_32(); + pos += sizeof(WORD); + f->seek(pos); + + ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!"); + + icon_dir->idCount = f->get_32(); + pos += sizeof(WORD); + f->seek(pos); + + 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_cc = 0; + int big_icon_index = -1; // Select largest + int big_icon_width = 16; + int big_icon_cc = 0; + + for (int i = 0; i < icon_dir->idCount; i++) { + int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount; + int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth; + if (width == 16) { + if (colors >= small_icon_cc) { + small_icon_index = i; + small_icon_cc = colors; + } + } + if (width >= big_icon_width) { + if (colors >= big_icon_cc) { + big_icon_index = i; + big_icon_width = width; + big_icon_cc = colors; + } + } + } + + ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!"); + + if (small_icon_index == -1) { + WARN_PRINT("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!"); + small_icon_index = big_icon_index; + small_icon_cc = big_icon_cc; + } + + // Read the big icon + DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes; + Vector data_big; + data_big.resize(bytecount_big); + pos = icon_dir->idEntries[big_icon_index].dwImageOffset; + f->seek(pos); + f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big); + 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 + DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes; + Vector data_small; + data_small.resize(bytecount_small); + pos = icon_dir->idEntries[small_icon_index].dwImageOffset; + f->seek(pos); + f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small); + 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 + int err = 0; + SetLastError(err); + + SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small); + err = GetLastError(); + ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + "."); + + SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big); + err = GetLastError(); + ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + "."); + + memdelete(f); + memdelete(icon_dir); +} +void DisplayServerWindows::set_icon(const Ref &p_icon) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!p_icon.is_valid()); + Ref 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 */ + int icon_len = 40 + h * w * 4; + Vector v; + v.resize(icon_len); + BYTE *icon_bmp = v.ptrw(); + + encode_uint32(40, &icon_bmp[0]); + encode_uint32(w, &icon_bmp[4]); + encode_uint32(h * 2, &icon_bmp[8]); + encode_uint16(1, &icon_bmp[12]); + encode_uint16(32, &icon_bmp[14]); + encode_uint32(BI_RGB, &icon_bmp[16]); + encode_uint32(w * h * 4, &icon_bmp[20]); + encode_uint32(0, &icon_bmp[24]); + encode_uint32(0, &icon_bmp[28]); + encode_uint32(0, &icon_bmp[32]); + encode_uint32(0, &icon_bmp[36]); + + uint8_t *wr = &icon_bmp[40]; + 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]; + wpx[1] = rpx[1]; + wpx[2] = rpx[0]; + wpx[3] = rpx[3]; + } + } + + HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); + + /* 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?) */ + SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon); +} + +void DisplayServerWindows::vsync_set_use_via_compositor(bool p_enable) { +} +bool DisplayServerWindows::vsync_is_using_via_compositor() const { + return false; +} + +void DisplayServerWindows::set_context(Context p_context) { +} + +#define MI_WP_SIGNATURE 0xFF515700 +#define SIGNATURE_MASK 0xFFFFFF00 +// 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) +#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; + + if (p_pressed) { + touch_state.insert(idx, Vector2(p_x, p_y)); + } else { + touch_state.erase(idx); + } + + Ref event; + event.instance(); + event->set_index(idx); + event->set_window_id(p_window); + event->set_pressed(p_pressed); + event->set_position(Vector2(p_x, p_y)); + + InputFilter::get_singleton()->accumulate_input_event(event); +} + +void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) { + + Map::Element *curr = touch_state.find(idx); + // Defensive + if (!curr) + return; + + if (curr->get() == Vector2(p_x, p_y)) + return; + + Ref event; + event.instance(); + 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()); + + InputFilter::get_singleton()->accumulate_input_event(event); + + curr->get() = Vector2(p_x, 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; + Variant ret; + Callable::CallError ce; + wd.event_callback.call((const Variant **)&eventp, 1, ret, ce); + } +} + +void DisplayServerWindows::_dispatch_input_events(const Ref &p_event) { + ((DisplayServerWindows *)(get_singleton()))->_dispatch_input_event(p_event); +} + +void DisplayServerWindows::_dispatch_input_event(const Ref &p_event) { + + Variant ev = p_event; + Variant *evp = &ev; + Variant ret; + Callable::CallError ce; + + Ref event_from_window = p_event; + if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) { + //send to a window + 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()) { + return; + } + callable.call((const Variant **)&evp, 1, ret, ce); + } else { + //send to all windows + for (Map::Element *E = windows.front(); E; E = E->next()) { + Callable callable = E->get().input_event_callback; + if (callable.is_null()) { + continue; + } + callable.call((const Variant **)&evp, 1, ret, ce); + } + } +} + +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); + } + }; + + WindowID window_id = INVALID_WINDOW_ID; + + for (Map::Element *E = windows.front(); E; E = E->next()) { + if (E->get().hWnd == hWnd) { + window_id = E->key(); + break; + } + } + + 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); + + 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 + ReleaseCapture(); + + // Release every touch to avoid sticky points + for (Map::Element *E = touch_state.front(); E; E = E->next()) { + _touch_event(window_id, false, E->get().x, E->get().y, E->key()); + } + touch_state.clear(); + + break; + } + case WM_ACTIVATE: // Watch For Window Activate Message + { + 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; + control_mem = false; + shift_mem = false; + } else { // WM_INACTIVE + InputFilter::get_singleton()->release_pressed_events(); + _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT); + windows[window_id].window_focused = false; + alt_mem = false; + }; + + return 0; // Return To The Message Loop + } + 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 + 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; + min_max_info->ptMinTrackSize.y = windows[window_id].min_size.y + decor.y; + } + if (windows[window_id].max_size != Size2()) { + min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x; + min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y; + } + return 0; + } else { + break; + } + } + case WM_PAINT: + + Main::force_redraw(); + break; + + case WM_SYSCOMMAND: // Intercept System Commands + { + 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_KEYMENU: + if ((lParam >> 16) <= 0) + return 0; + } + break; // Exit + } + + case WM_CLOSE: // Did We Receive A Close Message? + { + + _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST); + + //force_quit=true; + return 0; // Jump Back + } + case WM_MOUSELEAVE: { + + old_invalid = true; + outside = true; + + _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + + } break; + case WM_INPUT: { + if (mouse_mode != MOUSE_MODE_CAPTURED || !use_raw_input) { + break; + } + + UINT dwSize; + + GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); + LPBYTE lpb = new BYTE[dwSize]; + if (lpb == NULL) { + return 0; + } + + 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 mm; + mm.instance(); + + mm->set_window_id(window_id); + mm->set_control(control_mem); + mm->set_shift(shift_mem); + mm->set_alt(alt_mem); + + mm->set_button_mask(last_button_state); + + Point2i c(windows[window_id].width / 2, windows[window_id].height / 2); + + // 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); + InputFilter::get_singleton()->set_mouse_position(c); + mm->set_speed(Vector2(0, 0)); + + if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) { + 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); + int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN); + + Vector2 abs_pos( + (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 + coords.x = abs_pos.x; + coords.y = abs_pos.y; + + ScreenToClient(hWnd, &coords); + + 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()) + InputFilter::get_singleton()->accumulate_input_event(mm); + } + delete[] lpb; + } break; + case WM_POINTERUPDATE: { + if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { + break; + } + + if (!win8p_GetPointerType || !win8p_GetPointerPenInfo) { + 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; + } + + POINTER_PEN_INFO pen_info; + if (!win8p_GetPointerPenInfo(pointer_id, &pen_info)) { + break; + } + + if (InputFilter::get_singleton()->is_emulating_mouse_from_touch()) { + // Universal translation enabled; ignore OS translation + LPARAM extra = GetMessageExtraInfo(); + if (IsTouchEvent(extra)) { + break; + } + } + + if (outside) { + //mouse enter + + if (mouse_mode != MOUSE_MODE_CAPTURED) { + _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); + } + + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + 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); + } + + // 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 mm; + 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)); + + mm->set_control((wParam & MK_CONTROL) != 0); + mm->set_shift((wParam & MK_SHIFT) != 0); + mm->set_alt(alt_mem); + + mm->set_button_mask(last_button_state); + + POINT coords; //client coords + coords.x = GET_X_LPARAM(lParam); + coords.y = GET_Y_LPARAM(lParam); + + ScreenToClient(windows[window_id].hWnd, &coords); + + 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(hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } + + InputFilter::get_singleton()->set_mouse_position(mm->get_position()); + mm->set_speed(InputFilter::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) { + InputFilter::get_singleton()->parse_input_event(mm); + } + + return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event + } break; + case WM_MOUSEMOVE: { + if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { + break; + } + + if (InputFilter::get_singleton()->is_emulating_mouse_from_touch()) { + // Universal translation enabled; ignore OS translation + LPARAM extra = GetMessageExtraInfo(); + if (IsTouchEvent(extra)) { + break; + } + } + + if (outside) { + //mouse enter + + if (mouse_mode != MOUSE_MODE_CAPTURED) { + _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); + } + + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + 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); + } + + // 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 mm; + mm.instance(); + 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_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; + + 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); + } + + InputFilter::get_singleton()->set_mouse_position(mm->get_position()); + mm->set_speed(InputFilter::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) + InputFilter::get_singleton()->accumulate_input_event(mm); + + } break; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + if (InputFilter::get_singleton()->is_emulating_mouse_from_touch()) { + // Universal translation enabled; ignore OS translations for left button + LPARAM extra = GetMessageExtraInfo(); + if (IsTouchEvent(extra)) { + break; + } + } + [[fallthrough]]; + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: { + + Ref mb; + mb.instance(); + mb->set_window_id(window_id); + + switch (uMsg) { + case WM_LBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(1); + } break; + case WM_LBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(1); + } break; + case WM_MBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(3); + } break; + case WM_MBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(3); + } break; + case WM_RBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(2); + } break; + case WM_RBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(2); + } break; + case WM_LBUTTONDBLCLK: { + mb->set_pressed(true); + mb->set_button_index(1); + mb->set_doubleclick(true); + } break; + case WM_RBUTTONDBLCLK: { + mb->set_pressed(true); + mb->set_button_index(2); + mb->set_doubleclick(true); + } break; + case WM_MBUTTONDBLCLK: { + mb->set_pressed(true); + mb->set_button_index(3); + mb->set_doubleclick(true); + } break; + case WM_MOUSEWHEEL: { + + mb->set_pressed(true); + int motion = (short)HIWORD(wParam); + if (!motion) + return 0; + + if (motion > 0) + mb->set_button_index(BUTTON_WHEEL_UP); + else + mb->set_button_index(BUTTON_WHEEL_DOWN); + + } break; + case WM_MOUSEHWHEEL: { + + mb->set_pressed(true); + int motion = (short)HIWORD(wParam); + if (!motion) + return 0; + + if (motion < 0) { + mb->set_button_index(BUTTON_WHEEL_LEFT); + mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); + } else { + mb->set_button_index(BUTTON_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); + } 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); + } 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); + } 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_button_mask(last_button_state); + + 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(); + } + pressrc = 0; + } + } + } else { + // for reasons unknown to mankind, wheel comes in screen coordinates + POINT coords; + coords.x = mb->get_position().x; + coords.y = mb->get_position().y; + + ScreenToClient(hWnd, &coords); + + mb->set_position(Vector2(coords.x, coords.y)); + } + + mb->set_global_position(mb->get_position()); + + InputFilter::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 + Ref mbd = mb->duplicate(); + mbd->set_window_id(window_id); + last_button_state &= ~(1 << (mbd->get_button_index() - 1)); + mbd->set_button_mask(last_button_state); + mbd->set_pressed(false); + InputFilter::get_singleton()->accumulate_input_event(mbd); + } + + } break; + + case WM_MOVE: { + if (!IsIconic(windows[window_id].hWnd)) { + int x = LOWORD(lParam); + int y = HIWORD(lParam); + windows[window_id].last_pos = Point2(x, y); + } + } 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 defined(VULKAN_ENABLED) + if (rendering_driver == "vulkan") { + context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].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 (!windows[window_id].resize_callback.is_null()) { + + Variant size = Size2(windows[window_id].width, windows[window_id].height); + Variant *sizep = &size; + Variant ret; + Callable::CallError ce; + windows[window_id].resize_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, NULL, 0x0); + SelectObject(hDC_dib, hBitmap); + + ZeroMemory(dib_data, dib_size.x * dib_size.y * 4); + } +#endif + //return 0; // Jump Back + } break; + + case WM_ENTERSIZEMOVE: { + InputFilter::get_singleton()->release_pressed_events(); + move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC)NULL); + } break; + case WM_EXITSIZEMOVE: { + KillTimer(windows[window_id].hWnd, move_timer_id); + } break; + case WM_TIMER: { + if (wParam == move_timer_id) { + _process_key_events(); + if (!Main::is_iterating()) { + Main::iteration(); + } + } + } break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_KEYDOWN: { + + if (wParam == VK_SHIFT) + shift_mem = uMsg == WM_KEYDOWN; + if (wParam == VK_CONTROL) + control_mem = uMsg == WM_KEYDOWN; + if (wParam == VK_MENU) { + alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); + if (lParam & (1 << 24)) + gr_mem = alt_mem; + } + + if (mouse_mode == MOUSE_MODE_CAPTURED) { + // When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves + if (wParam == VK_F4 && alt_mem && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) { + _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: { + + ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE); + + // Make sure we don't include modifiers for the modifier key itself. + KeyEvent ke; + ke.shift = (wParam != VK_SHIFT) ? shift_mem : false; + ke.alt = (!(wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false; + ke.control = (wParam != VK_CONTROL) ? control_mem : false; + ke.meta = meta_mem; + ke.uMsg = uMsg; + ke.window_id = window_id; + + if (ke.uMsg == WM_SYSKEYDOWN) + ke.uMsg = WM_KEYDOWN; + if (ke.uMsg == WM_SYSKEYUP) + ke.uMsg = WM_KEYUP; + + ke.wParam = wParam; + ke.lParam = lParam; + key_event_buffer[key_event_pos++] = ke; + + } break; + case WM_INPUTLANGCHANGEREQUEST: { + + // FIXME: Do something? + } break; + + case WM_TOUCH: { + + BOOL bHandled = FALSE; + UINT cInputs = LOWORD(wParam); + PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs); + if (pInputs) { + if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) { + for (UINT i = 0; i < cInputs; i++) { + TOUCHINPUT ti = pInputs[i]; + POINT touch_pos = { + TOUCH_COORD_TO_PIXEL(ti.x), + TOUCH_COORD_TO_PIXEL(ti.y), + }; + 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); + }; + } + bHandled = TRUE; + } else { + /* handle the error here */ + } + memdelete_arr(pInputs); + } else { + /* 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_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 == NULL) + hCursor = SetCursor(NULL); + else + SetCursor(NULL); + } else { + if (hCursor != NULL) { + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + cursor_set_shape(c); + hCursor = NULL; + } + } + } + + } break; + case WM_DROPFILES: { + + HDROP hDropInfo = (HDROP)wParam; + const int buffsize = 4096; + wchar_t buf[buffsize]; + + int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, NULL, 0); + + Vector files; + + for (int i = 0; i < fcount; i++) { + + DragQueryFileW(hDropInfo, i, buf, buffsize); + String file = buf; + files.push_back(file); + } + + if (files.size() && !windows[window_id].drop_files_callback.is_null()) { + Variant v = files; + Variant *vp = &v; + Variant ret; + 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); +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + DisplayServerWindows *ds_win = static_cast(DisplayServer::get_singleton()); + if (ds_win) + return ds_win->WndProc(hWnd, uMsg, wParam, lParam); + else + return DefWindowProcW(hWnd, uMsg, wParam, 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)) { + Ref k; + k.instance(); + + 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_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_unicode(ke.wParam); + if (k->get_unicode() && gr_mem) { + k->set_alt(false); + k->set_control(false); + } + + if (k->get_unicode() < 32) + k->set_unicode(0); + + InputFilter::get_singleton()->accumulate_input_event(k); + } + + //do nothing + } break; + case WM_KEYUP: + case WM_KEYDOWN: { + + Ref k; + k.instance(); + + 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_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); + } else { + k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam)); + } + + k->set_physical_keycode(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) { + k->set_unicode(key_event_buffer[i + 1].wParam); + } + if (k->get_unicode() && gr_mem) { + k->set_alt(false); + k->set_control(false); + } + + if (k->get_unicode() < 32) + k->set_unicode(0); + + k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30)))); + + InputFilter::get_singleton()->accumulate_input_event(k); + + } break; + } + } + + key_event_pos = 0; +} + +DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, const Rect2i &p_rect) { + + DWORD dwExStyle; + DWORD dwStyle; + + dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + dwStyle = WS_OVERLAPPEDWINDOW; + + RECT WindowRect; + + WindowRect.left = p_rect.position.x; + WindowRect.right = p_rect.position.x + p_rect.size.x; + WindowRect.top = p_rect.position.y; + WindowRect.bottom = p_rect.position.y + p_rect.size.y; + + AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); + + WindowID id = window_id_counter; + { + WindowData wd; + + wd.hWnd = CreateWindowExW( + dwExStyle, + L"Engine", L"", + dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2, + (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + NULL, NULL, hInstance, NULL); + if (!wd.hWnd) { + MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); + return INVALID_WINDOW_ID; + } +#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) { + memdelete(context_vulkan); + context_vulkan = NULL; + ERR_FAIL_V(INVALID_WINDOW_ID); + } + } +#endif + + RegisterTouchWindow(wd.hWnd, 0); + + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = wd.hWnd; + tme.dwHoverTime = HOVER_DEFAULT; + TrackMouseEvent(&tme); + + DragAcceptFiles(wd.hWnd, true); + + ShowWindow(wd.hWnd, SW_SHOW); // Show The Window + SetForegroundWindow(wd.hWnd); // Slightly Higher Priority + SetFocus(wd.hWnd); // Sets Keyboard Focus To + + // IME + wd.im_himc = ImmGetContext(wd.hWnd); + ImmReleaseContext(wd.hWnd, wd.im_himc); + + wd.im_position = Vector2(); + wd.last_pos = p_rect.position; + wd.width = p_rect.size.width; + wd.height = p_rect.size.height; + + windows[id] = wd; + + window_id_counter++; + } + + return id; +} + +GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr; +GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr; + +typedef enum _SHC_PROCESS_DPI_AWARENESS { + SHC_PROCESS_DPI_UNAWARE = 0, + SHC_PROCESS_SYSTEM_DPI_AWARE = 1, + SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2 +} 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; + + alt_mem = false; + gr_mem = false; + shift_mem = false; + control_mem = false; + meta_mem = false; + console_visible = IsWindowVisible(GetConsoleWindow()); + hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance(); + + pressrc = 0; + old_invalid = true; + mouse_mode = MOUSE_MODE_VISIBLE; + + outside = true; + + if (OS::get_singleton()->is_hidpi_allowed()) { + HMODULE Shcore = LoadLibraryW(L"Shcore.dll"); + + if (Shcore != NULL) { + typedef HRESULT(WINAPI * SetProcessDpiAwareness_t)(SHC_PROCESS_DPI_AWARENESS); + + SetProcessDpiAwareness_t SetProcessDpiAwareness = (SetProcessDpiAwareness_t)GetProcAddress(Shcore, "SetProcessDpiAwareness"); + + if (SetProcessDpiAwareness) { + SetProcessDpiAwareness(SHC_PROCESS_SYSTEM_DPI_AWARE); + } + } + } + + memset(&wc, 0, sizeof(WNDCLASSEXW)); + wc.cbSize = sizeof(WNDCLASSEXW); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; + wc.lpfnWndProc = (WNDPROC)::WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + //wc.hInstance = hInstance; + wc.hInstance = hInstance ? hInstance : GetModuleHandle(NULL); + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = L"Engine"; + + if (!RegisterClassExW(&wc)) { + MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONEXCLAMATION); + r_error = ERR_UNAVAILABLE; + return; + } + + use_raw_input = true; + + RAWINPUTDEVICE Rid[1]; + + Rid[0].usUsagePage = 0x01; + Rid[0].usUsage = 0x02; + Rid[0].dwFlags = 0; + Rid[0].hwndTarget = 0; + + if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) { + //registration failed. + use_raw_input = false; + } + + rendering_driver = "vulkan"; + +#if defined(VULKAN_ENABLED) + if (rendering_driver == "vulkan") { + + context_vulkan = memnew(VulkanContextWindows); + if (context_vulkan->initialize() != OK) { + memdelete(context_vulkan); + context_vulkan = NULL; + r_error = ERR_UNAVAILABLE; + return; + } + } +#endif +#if defined(OPENGL_ENABLED) + if (rendering_driver_index == VIDEO_DRIVER_GLES2) { + + context_gles2 = memnew(ContextGL_Windows(hWnd, false)); + + if (context_gles2->initialize() != OK) { + memdelete(context_gles2); + context_gles2 = NULL; + ERR_FAIL_V(ERR_UNAVAILABLE); + } + + context_gles2->set_use_vsync(video_mode.use_vsync); + set_vsync_via_compositor(video_mode.vsync_via_compositor); + + if (RasterizerGLES2::is_viable() == OK) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } else { + memdelete(context_gles2); + context_gles2 = NULL; + ERR_FAIL_V(ERR_UNAVAILABLE); + } + } +#endif + WindowID main_window = _create_window(p_mode, Rect2i(Point2i(), p_resolution)); + for (int i = 0; i < WINDOW_FLAG_MAX; i++) { + if (p_flags & (1 << i)) { + window_set_flag(WindowFlags(i), true, main_window); + } + } + +#if defined(VULKAN_ENABLED) + + if (rendering_driver == "vulkan") { + + rendering_device_vulkan = memnew(RenderingDeviceVulkan); + rendering_device_vulkan->initialize(context_vulkan); + + RasterizerRD::make_current(); + } +#endif + + move_timer_id = 1; + + //set_ime_active(false); + + if (!OS::get_singleton()->is_in_low_processor_usage_mode()) { + //SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); + SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); + DWORD index = 0; + HANDLE handle = AvSetMmThreadCharacteristics("Games", &index); + 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. + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + } + + cursor_shape = CURSOR_ARROW; + + _update_real_mouse_position(MAIN_WINDOW_ID); + + joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd); + + r_error = OK; + + ((OS_Windows *)OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd); + InputFilter::get_singleton()->set_event_dispatch_function(_dispatch_input_events); +} + +Vector DisplayServerWindows::get_rendering_drivers_func() { + Vector drivers; + +#ifdef VULKAN_ENABLED + drivers.push_back("vulkan"); +#endif +#ifdef OPENGL_ENABLED + drivers.push_back("opengl"); +#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) { + + 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); + }; + + if (windows.has(MAIN_WINDOW_ID)) { +#ifdef VULKAN_ENABLED + if (rendering_driver == "vulkan") { + context_vulkan->window_destroy(MAIN_WINDOW_ID); + } +#endif + + DestroyWindow(windows[MAIN_WINDOW_ID].hWnd); + } +} diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h new file mode 100644 index 00000000000..5fef08fbd5b --- /dev/null +++ b/platform/windows/display_server_windows.h @@ -0,0 +1,379 @@ +#ifndef DISPLAY_SERVER_WINDOWS_H +#define DISPLAY_SERVER_WINDOWS_H + +#include "servers/display_server.h" + +#include "core/input/input_filter.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "crash_handler_windows.h" +#include "drivers/unix/ip_unix.h" +#include "drivers/wasapi/audio_driver_wasapi.h" +#include "drivers/winmidi/midi_driver_winmidi.h" +#include "joypad_windows.h" +#include "key_mapping_windows.h" +#include "servers/audio_server.h" +#include "servers/visual/rasterizer.h" +#include "servers/visual/rasterizer_rd/rasterizer_rd.h" +#include "servers/visual_server.h" + +#ifdef XAUDIO2_ENABLED +#include "drivers/xaudio2/audio_driver_xaudio2.h" +#endif + +#if defined(OPENGL_ENABLED) +#include "context_gl_windows.h" +#endif + +#if defined(VULKAN_ENABLED) +#include "drivers/vulkan/rendering_device_vulkan.h" +#include "platform/windows/vulkan_context_win.h" +#endif + +#include +#include +#include +#include +#include + +#ifndef POINTER_STRUCTURES + +#define POINTER_STRUCTURES + +typedef DWORD POINTER_INPUT_TYPE; +typedef UINT32 POINTER_FLAGS; +typedef UINT32 PEN_FLAGS; +typedef UINT32 PEN_MASK; + +enum tagPOINTER_INPUT_TYPE { + PT_POINTER = 0x00000001, + PT_TOUCH = 0x00000002, + PT_PEN = 0x00000003, + PT_MOUSE = 0x00000004, + PT_TOUCHPAD = 0x00000005 +}; + +typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { + POINTER_CHANGE_NONE, + POINTER_CHANGE_FIRSTBUTTON_DOWN, + POINTER_CHANGE_FIRSTBUTTON_UP, + POINTER_CHANGE_SECONDBUTTON_DOWN, + POINTER_CHANGE_SECONDBUTTON_UP, + POINTER_CHANGE_THIRDBUTTON_DOWN, + POINTER_CHANGE_THIRDBUTTON_UP, + POINTER_CHANGE_FOURTHBUTTON_DOWN, + POINTER_CHANGE_FOURTHBUTTON_UP, + POINTER_CHANGE_FIFTHBUTTON_DOWN, + POINTER_CHANGE_FIFTHBUTTON_UP, +} POINTER_BUTTON_CHANGE_TYPE; + +typedef struct tagPOINTER_INFO { + POINTER_INPUT_TYPE pointerType; + UINT32 pointerId; + UINT32 frameId; + POINTER_FLAGS pointerFlags; + HANDLE sourceDevice; + HWND hwndTarget; + POINT ptPixelLocation; + POINT ptHimetricLocation; + POINT ptPixelLocationRaw; + POINT ptHimetricLocationRaw; + DWORD dwTime; + UINT32 historyCount; + INT32 InputData; + DWORD dwKeyStates; + UINT64 PerformanceCount; + POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; +} POINTER_INFO; + +typedef struct tagPOINTER_PEN_INFO { + POINTER_INFO pointerInfo; + PEN_FLAGS penFlags; + PEN_MASK penMask; + UINT32 pressure; + UINT32 rotation; + INT32 tiltX; + INT32 tiltY; +} POINTER_PEN_INFO; + +#endif //POINTER_STRUCTURES + +typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type); +typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info); + +typedef struct { + BYTE bWidth; // Width, in pixels, of the image + BYTE bHeight; // Height, in pixels, of the image + BYTE bColorCount; // Number of colors in image (0 if >=8bpp) + BYTE bReserved; // Reserved ( must be 0) + WORD wPlanes; // Color Planes + WORD wBitCount; // Bits per pixel + DWORD dwBytesInRes; // How many bytes in this resource? + DWORD dwImageOffset; // Where in the file is this image? +} ICONDIRENTRY, *LPICONDIRENTRY; + +typedef struct { + WORD idReserved; // Reserved (must be 0) + WORD idType; // Resource Type (1 for icons) + WORD idCount; // How many images? + ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) +} ICONDIR, *LPICONDIR; + +class DisplayServerWindows : public DisplayServer { + //No need to register, it's platform-specific and nothing is added + //GDCLASS(DisplayServerWindows, DisplayServer) + + _THREAD_SAFE_CLASS_ + + static GetPointerTypePtr win8p_GetPointerType; + static GetPointerPenInfoPtr win8p_GetPointerPenInfo; + + void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap); + + enum { + KEY_EVENT_BUFFER_SIZE = 512 + }; + + struct KeyEvent { + + WindowID window_id; + bool alt, shift, control, meta; + UINT uMsg; + WPARAM wParam; + LPARAM lParam; + }; + + KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE]; + int key_event_pos; + + bool old_invalid; + bool outside; + int old_x, old_y; + Point2i center; + +#if defined(OPENGL_ENABLED) + ContextGL_Windows *context_gles2; +#endif + +#if defined(VULKAN_ENABLED) + VulkanContextWindows *context_vulkan; + RenderingDeviceVulkan *rendering_device_vulkan; +#endif + + Map touch_state; + + int pressrc; + HINSTANCE hInstance; // Holds The Instance Of The Application + String rendering_driver; + + struct WindowData { + HWND hWnd; + //layered window + + bool preserve_window_size = false; + bool pre_fs_valid = false; + RECT pre_fs_rect; + bool maximized = false; + bool minimized = false; + bool fullscreen = false; + bool borderless = false; + bool resizable = true; + bool window_focused = false; + bool was_maximized = false; + bool always_on_top = false; + bool window_has_focus = false; + + HBITMAP hBitmap; //DIB section for layered window + uint8_t *dib_data = nullptr; + Size2 dib_size; + HDC hDC_dib; + Size2 min_size; + Size2 max_size; + int width = 0, height = 0; + + Size2 window_rect; + Point2 last_pos; + + // IME + HIMC im_himc; + Vector2 im_position; + + bool layered_window = false; + + Callable resize_callback; + Callable event_callback; + Callable input_event_callback; + Callable input_text_callback; + Callable drop_files_callback; + + WindowID transient_parent = INVALID_WINDOW_ID; + Set transient_children; + }; + + JoypadWindows *joypad; + + WindowID _create_window(WindowMode p_mode, const Rect2i &p_rect); + WindowID window_id_counter = MAIN_WINDOW_ID; + Map windows; + + WindowID last_focused_window = INVALID_WINDOW_ID; + + uint32_t move_timer_id; + + HCURSOR hCursor; + + WNDPROC user_proc; + + void _send_window_event(const WindowData &wd, WindowEvent p_event); + + MouseMode mouse_mode; + bool alt_mem = false; + bool gr_mem = false; + bool shift_mem = false; + bool control_mem = false; + bool meta_mem = false; + bool force_quit = false; + uint32_t last_button_state = 0; + bool use_raw_input = false; + bool drop_events = false; + bool console_visible = false; + + WNDCLASSEXW wc; + + HCURSOR cursors[CURSOR_MAX] = { NULL }; + CursorShape cursor_shape; + Map> cursors_cache; + + void _drag_event(WindowID p_window, float p_x, float p_y, int idx); + void _touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx); + + void _update_window_style(WindowID p_window, bool p_repaint = true, bool p_maximized = false); + + void _update_real_mouse_position(WindowID p_window); + + void _set_mouse_mode_impl(MouseMode p_mode); + + void _process_key_events(); + + static void _dispatch_input_events(const Ref &p_event); + void _dispatch_input_event(const Ref &p_event); + +public: + LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + virtual bool has_feature(Feature p_feature) const; + virtual String get_name() const; + + virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); + + virtual void mouse_set_mode(MouseMode p_mode); + virtual MouseMode mouse_get_mode() const; + + virtual void mouse_warp_to_position(const Point2i &p_to); + virtual Point2i mouse_get_position() const; + virtual int mouse_get_button_state() const; + + virtual void clipboard_set(const String &p_text); + virtual String clipboard_get() const; + + virtual int get_screen_count() const; + virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + + virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW); + ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + + virtual void screen_set_keep_on(bool p_enable); //disable screensaver + virtual bool screen_is_kept_on() const; + + virtual Vector get_window_list() const; + + virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()); + virtual void delete_sub_window(WindowID p_window); + + virtual void window_set_resize_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + + virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + + virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); + + virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID); + + virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID); + + virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID); + + virtual void window_set_transient(WindowID p_window, WindowID p_parent); + + virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const; + virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const; //wtf is this? should probable use proper name + + virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID); + virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID); + virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID); + virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID); + + virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual bool can_any_window_draw() const; + + virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID); + + virtual void console_set_visible(bool p_enabled); + virtual bool is_console_visible() const; + + virtual void cursor_set_shape(CursorShape p_shape); + virtual CursorShape cursor_get_shape() const; + virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); + + virtual bool get_swap_ok_cancel(); + + virtual void enable_for_stealing_focus(OS::ProcessID pid); + + virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + + virtual void process_events(); + + virtual void force_process_and_drop_events(); + + virtual void release_rendering_thread(); + virtual void make_rendering_thread(); + virtual void swap_buffers(); + + virtual void set_native_icon(const String &p_filename); + virtual void set_icon(const Ref &p_icon); + + virtual void vsync_set_use_via_compositor(bool p_enable); + virtual bool vsync_is_using_via_compositor() const; + + virtual void set_context(Context p_context); + + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static Vector get_rendering_drivers_func(); + static void register_windows_driver(); + + DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + ~DisplayServerWindows(); +}; + +#endif // DISPLAY_SERVER_WINDOWS_H diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index 49432435b93..9de1b7b1940 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -52,9 +52,9 @@ DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) JoypadWindows::JoypadWindows() { } -JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) { +JoypadWindows::JoypadWindows(HWND *hwnd) { - input = _input; + input = InputFilter::get_singleton(); hWnd = hwnd; joypad_count = 0; dinput = NULL; @@ -436,46 +436,46 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) { // BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);" // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks if (LOWORD(p_dpad) == 0xFFFF) { - dpad_val = InputDefault::HAT_MASK_CENTER; + dpad_val = InputFilter::HAT_MASK_CENTER; } if (p_dpad == 0) { - dpad_val = InputDefault::HAT_MASK_UP; + dpad_val = InputFilter::HAT_MASK_UP; } else if (p_dpad == 4500) { - dpad_val = (InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_RIGHT); + dpad_val = (InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_RIGHT); } else if (p_dpad == 9000) { - dpad_val = InputDefault::HAT_MASK_RIGHT; + dpad_val = InputFilter::HAT_MASK_RIGHT; } else if (p_dpad == 13500) { - dpad_val = (InputDefault::HAT_MASK_RIGHT | InputDefault::HAT_MASK_DOWN); + dpad_val = (InputFilter::HAT_MASK_RIGHT | InputFilter::HAT_MASK_DOWN); } else if (p_dpad == 18000) { - dpad_val = InputDefault::HAT_MASK_DOWN; + dpad_val = InputFilter::HAT_MASK_DOWN; } else if (p_dpad == 22500) { - dpad_val = (InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_LEFT); + dpad_val = (InputFilter::HAT_MASK_DOWN | InputFilter::HAT_MASK_LEFT); } else if (p_dpad == 27000) { - dpad_val = InputDefault::HAT_MASK_LEFT; + dpad_val = InputFilter::HAT_MASK_LEFT; } else if (p_dpad == 31500) { - dpad_val = (InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_UP); + dpad_val = (InputFilter::HAT_MASK_LEFT | InputFilter::HAT_MASK_UP); } input->joy_hat(p_device, dpad_val); }; -InputDefault::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const { +InputFilter::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const { - InputDefault::JoyAxis jx; + InputFilter::JoyAxis jx; if (Math::abs(p_val) < MIN_JOY_AXIS) { jx.min = p_trigger ? 0 : -1; jx.value = 0.0f; diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h index ab85bc60aca..f010fd08ff8 100644 --- a/platform/windows/joypad_windows.h +++ b/platform/windows/joypad_windows.h @@ -52,7 +52,7 @@ class JoypadWindows { public: JoypadWindows(); - JoypadWindows(InputDefault *_input, HWND *hwnd); + JoypadWindows(HWND *hwnd); ~JoypadWindows(); void probe_joypads(); @@ -117,7 +117,7 @@ private: HWND *hWnd; HANDLE xinput_dll; LPDIRECTINPUT8 dinput; - InputDefault *input; + InputFilter *input; int id_to_change; int joypad_count; @@ -141,7 +141,7 @@ private: void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp); - InputDefault::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const; + InputFilter::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const; XInputGetState_t xinput_get_state; XInputSetState_t xinput_set_state; }; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index c49f096ec52..f78c87be93b 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -37,15 +37,6 @@ #include "core/debugger/script_debugger.h" #include "core/io/marshalls.h" #include "core/version_generated.gen.h" - -#if defined(OPENGL_ENABLED) -#include "drivers/gles2/rasterizer_gles2.h" -#endif - -#if defined(VULKAN_ENABLED) -#include "servers/visual/rasterizer_rd/rasterizer_rd.h" -#endif - #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" #include "drivers/windows/rw_lock_windows.h" @@ -53,6 +44,7 @@ #include "joypad_windows.h" #include "lang_table.h" #include "main/main.h" +#include "platform/windows/display_server_windows.h" #include "servers/audio_server.h" #include "servers/visual/visual_server_raster.h" #include "servers/visual/visual_server_wrap_mt.h" @@ -86,30 +78,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #define GetProcAddress (void *)GetProcAddress #endif -typedef struct { - int count; - int screen; - Size2 size; -} EnumSizeData; - -typedef struct { - int count; - int screen; - Point2 pos; -} EnumPosData; - -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; - data->size.y = lprcMonitor->bottom - lprcMonitor->top; - } - - data->count++; - return TRUE; -} - #ifdef DEBUG_ENABLED static String format_error_message(DWORD id) { @@ -125,8 +93,6 @@ static String format_error_message(DWORD id) { } #endif // DEBUG_ENABLED -extern HINSTANCE godot_hinstance; - void RedirectIOToConsole() { int hConHandle; @@ -208,24 +174,16 @@ BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) { } } -GetPointerTypePtr OS_Windows::win8p_GetPointerType = NULL; -GetPointerPenInfoPtr OS_Windows::win8p_GetPointerPenInfo = NULL; - void OS_Windows::initialize_debugging() { SetConsoleCtrlHandler(HandlerRoutine, TRUE); } -void OS_Windows::initialize_core() { +void OS_Windows::initialize() { crash_handler.initialize(); - last_button_state = 0; - //RedirectIOToConsole(); - maximized = false; - minimized = false; - borderless = false; ThreadWindows::make_default(); RWLockWindows::make_default(); @@ -255,1371 +213,9 @@ void OS_Windows::initialize_core() { process_map = memnew((Map)); IP_Unix::make_default(); - - cursor_shape = CURSOR_ARROW; -} - -bool OS_Windows::can_draw() const { - - return !minimized; -}; - -#define MI_WP_SIGNATURE 0xFF515700 -#define SIGNATURE_MASK 0xFFFFFF00 -// 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) -#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80)) - -void OS_Windows::_touch_event(bool p_pressed, float p_x, float p_y, int idx) { - - // Defensive - if (touch_state.has(idx) == p_pressed) - return; - - if (p_pressed) { - touch_state.insert(idx, Vector2(p_x, p_y)); - } else { - touch_state.erase(idx); - } - - Ref event; - event.instance(); - event->set_index(idx); - event->set_pressed(p_pressed); - event->set_position(Vector2(p_x, p_y)); - - if (main_loop) { - input->accumulate_input_event(event); - } -}; - -void OS_Windows::_drag_event(float p_x, float p_y, int idx) { - - Map::Element *curr = touch_state.find(idx); - // Defensive - if (!curr) - return; - - if (curr->get() == Vector2(p_x, p_y)) - return; - - Ref event; - event.instance(); - event->set_index(idx); - event->set_position(Vector2(p_x, p_y)); - event->set_relative(Vector2(p_x, p_y) - curr->get()); - - if (main_loop) - input->accumulate_input_event(event); - - curr->get() = Vector2(p_x, p_y); -}; - -LRESULT OS_Windows::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); - } - }; - - switch (uMsg) // Check For Windows Messages - { - case WM_SETFOCUS: { - window_has_focus = true; - - // Restore mouse mode - _set_mouse_mode_impl(mouse_mode); - - break; - } - case WM_KILLFOCUS: { - window_has_focus = false; - - // 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::Element *E = touch_state.front(); E; E = E->next()) { - _touch_event(false, E->get().x, E->get().y, E->key()); - } - touch_state.clear(); - - break; - } - case WM_ACTIVATE: // Watch For Window Activate Message - { - minimized = HIWORD(wParam) != 0; - if (!main_loop) { - return 0; - }; - if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { - - main_loop->notification(NOTIFICATION_WM_FOCUS_IN); - window_focused = true; - alt_mem = false; - control_mem = false; - shift_mem = false; - } else { // WM_INACTIVE - input->release_pressed_events(); - main_loop->notification(NOTIFICATION_WM_FOCUS_OUT); - window_focused = false; - alt_mem = false; - }; - - return 0; // Return To The Message Loop - } - case WM_GETMINMAXINFO: { - if (video_mode.resizable && !video_mode.fullscreen) { - Size2 decor = get_real_window_size() - get_window_size(); // Size of window decorations - MINMAXINFO *min_max_info = (MINMAXINFO *)lParam; - if (min_size != Size2()) { - min_max_info->ptMinTrackSize.x = min_size.x + decor.x; - min_max_info->ptMinTrackSize.y = min_size.y + decor.y; - } - if (max_size != Size2()) { - min_max_info->ptMaxTrackSize.x = max_size.x + decor.x; - min_max_info->ptMaxTrackSize.y = max_size.y + decor.y; - } - return 0; - } else { - break; - } - } - case WM_PAINT: - - Main::force_redraw(); - break; - - case WM_SYSCOMMAND: // Intercept System Commands - { - 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_KEYMENU: - if ((lParam >> 16) <= 0) - return 0; - } - break; // Exit - } - - case WM_CLOSE: // Did We Receive A Close Message? - { - if (main_loop) - main_loop->notification(NOTIFICATION_WM_CLOSE_REQUEST); - //force_quit=true; - return 0; // Jump Back - } - case WM_MOUSELEAVE: { - - old_invalid = true; - outside = true; - if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED) - main_loop->notification(NOTIFICATION_WM_MOUSE_EXIT); - - } break; - case WM_INPUT: { - if (mouse_mode != MOUSE_MODE_CAPTURED || !use_raw_input) { - break; - } - - UINT dwSize; - - GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); - LPBYTE lpb = new BYTE[dwSize]; - if (lpb == NULL) { - return 0; - } - - 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 mm; - mm.instance(); - - mm->set_control(control_mem); - mm->set_shift(shift_mem); - mm->set_alt(alt_mem); - - mm->set_button_mask(last_button_state); - - Point2i c(video_mode.width / 2, video_mode.height / 2); - - // centering just so it works as before - POINT pos = { (int)c.x, (int)c.y }; - ClientToScreen(hWnd, &pos); - SetCursorPos(pos.x, pos.y); - - mm->set_position(c); - mm->set_global_position(c); - input->set_mouse_position(c); - mm->set_speed(Vector2(0, 0)); - - if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) { - 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); - int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN); - - Vector2 abs_pos( - (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 - coords.x = abs_pos.x; - coords.y = abs_pos.y; - - ScreenToClient(hWnd, &coords); - - 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 (window_has_focus && main_loop && mm->get_relative() != Vector2()) - input->accumulate_input_event(mm); - } - delete[] lpb; - } break; - case WM_POINTERUPDATE: { - if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { - break; - } - - if (!win8p_GetPointerType || !win8p_GetPointerPenInfo) { - 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; - } - - POINTER_PEN_INFO pen_info; - if (!win8p_GetPointerPenInfo(pointer_id, &pen_info)) { - break; - } - - if (input->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translation - LPARAM extra = GetMessageExtraInfo(); - if (IsTouchEvent(extra)) { - break; - } - } - - if (outside) { - //mouse enter - - if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED) - main_loop->notification(NOTIFICATION_WM_MOUSE_ENTER); - - CursorShape c = cursor_shape; - cursor_shape = CURSOR_MAX; - set_cursor_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); - } - - // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. - if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) - break; - - Ref mm; - mm.instance(); - - 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)); - - mm->set_control((wParam & MK_CONTROL) != 0); - mm->set_shift((wParam & MK_SHIFT) != 0); - mm->set_alt(alt_mem); - - mm->set_button_mask(last_button_state); - - POINT coords; //client coords - coords.x = GET_X_LPARAM(lParam); - coords.y = GET_Y_LPARAM(lParam); - - ScreenToClient(hWnd, &coords); - - 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(video_mode.width / 2, video_mode.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(hWnd, &pos); - SetCursorPos(pos.x, pos.y); - } - - input->set_mouse_position(mm->get_position()); - mm->set_speed(input->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 (window_has_focus && main_loop) - input->parse_input_event(mm); - - return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event - } break; - case WM_MOUSEMOVE: { - if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { - break; - } - - if (input->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translation - LPARAM extra = GetMessageExtraInfo(); - if (IsTouchEvent(extra)) { - break; - } - } - - if (outside) { - //mouse enter - - if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED) - main_loop->notification(NOTIFICATION_WM_MOUSE_ENTER); - - CursorShape c = cursor_shape; - cursor_shape = CURSOR_MAX; - set_cursor_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); - } - - // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. - if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) - break; - - Ref mm; - mm.instance(); - - mm->set_control((wParam & MK_CONTROL) != 0); - mm->set_shift((wParam & MK_SHIFT) != 0); - mm->set_alt(alt_mem); - - 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(video_mode.width / 2, video_mode.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(hWnd, &pos); - SetCursorPos(pos.x, pos.y); - } - - input->set_mouse_position(mm->get_position()); - mm->set_speed(input->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 (window_has_focus && main_loop) - input->accumulate_input_event(mm); - - } break; - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - if (input->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translations for left button - LPARAM extra = GetMessageExtraInfo(); - if (IsTouchEvent(extra)) { - break; - } - } - [[fallthrough]]; - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: - case WM_LBUTTONDBLCLK: - case WM_MBUTTONDBLCLK: - case WM_RBUTTONDBLCLK: - case WM_XBUTTONDBLCLK: - case WM_XBUTTONDOWN: - case WM_XBUTTONUP: { - - Ref mb; - mb.instance(); - - switch (uMsg) { - case WM_LBUTTONDOWN: { - mb->set_pressed(true); - mb->set_button_index(1); - } break; - case WM_LBUTTONUP: { - mb->set_pressed(false); - mb->set_button_index(1); - } break; - case WM_MBUTTONDOWN: { - mb->set_pressed(true); - mb->set_button_index(3); - } break; - case WM_MBUTTONUP: { - mb->set_pressed(false); - mb->set_button_index(3); - } break; - case WM_RBUTTONDOWN: { - mb->set_pressed(true); - mb->set_button_index(2); - } break; - case WM_RBUTTONUP: { - mb->set_pressed(false); - mb->set_button_index(2); - } break; - case WM_LBUTTONDBLCLK: { - mb->set_pressed(true); - mb->set_button_index(1); - mb->set_doubleclick(true); - } break; - case WM_RBUTTONDBLCLK: { - mb->set_pressed(true); - mb->set_button_index(2); - mb->set_doubleclick(true); - } break; - case WM_MBUTTONDBLCLK: { - mb->set_pressed(true); - mb->set_button_index(3); - mb->set_doubleclick(true); - } break; - case WM_MOUSEWHEEL: { - - mb->set_pressed(true); - int motion = (short)HIWORD(wParam); - if (!motion) - return 0; - - if (motion > 0) - mb->set_button_index(BUTTON_WHEEL_UP); - else - mb->set_button_index(BUTTON_WHEEL_DOWN); - - } break; - case WM_MOUSEHWHEEL: { - - mb->set_pressed(true); - int motion = (short)HIWORD(wParam); - if (!motion) - return 0; - - if (motion < 0) { - mb->set_button_index(BUTTON_WHEEL_LEFT); - mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); - } else { - mb->set_button_index(BUTTON_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); - } 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); - } 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); - } 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_button_mask(last_button_state); - - 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(); - } - pressrc = 0; - } - } - } else { - // for reasons unknown to mankind, wheel comes in screen coordinates - POINT coords; - coords.x = mb->get_position().x; - coords.y = mb->get_position().y; - - ScreenToClient(hWnd, &coords); - - mb->set_position(Vector2(coords.x, coords.y)); - } - - mb->set_global_position(mb->get_position()); - - if (main_loop) { - input->accumulate_input_event(mb); - if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) { - //send release for mouse wheel - Ref mbd = mb->duplicate(); - last_button_state &= ~(1 << (mbd->get_button_index() - 1)); - mbd->set_button_mask(last_button_state); - mbd->set_pressed(false); - input->accumulate_input_event(mbd); - } - } - } break; - - case WM_MOVE: { - if (!IsIconic(hWnd)) { - int x = LOWORD(lParam); - int y = HIWORD(lParam); - last_pos = Point2(x, y); - } - } 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 && !preserve_window_size) { - video_mode.width = window_w; - video_mode.height = window_h; - } else { - preserve_window_size = false; - set_window_size(Size2(video_mode.width, video_mode.height)); - } -#if defined(VULKAN_ENABLED) - if (video_driver_index == VIDEO_DRIVER_VULKAN) { - context_vulkan->window_resize(0, video_mode.width, video_mode.height); - } -#endif - } - - if (wParam == SIZE_MAXIMIZED) { - maximized = true; - minimized = false; - } else if (wParam == SIZE_MINIMIZED) { - maximized = false; - minimized = true; - } else if (wParam == SIZE_RESTORED) { - maximized = false; - minimized = false; - } - 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, NULL, 0x0); - SelectObject(hDC_dib, hBitmap); - - ZeroMemory(dib_data, dib_size.x * dib_size.y * 4); - } - //return 0; // Jump Back - } break; - - case WM_ENTERSIZEMOVE: { - input->release_pressed_events(); - move_timer_id = SetTimer(hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC)NULL); - } break; - case WM_EXITSIZEMOVE: { - KillTimer(hWnd, move_timer_id); - } break; - case WM_TIMER: { - if (wParam == move_timer_id) { - process_key_events(); - if (!Main::is_iterating()) { - Main::iteration(); - } - } - } break; - - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_KEYUP: - case WM_KEYDOWN: { - - if (wParam == VK_SHIFT) - shift_mem = uMsg == WM_KEYDOWN; - if (wParam == VK_CONTROL) - control_mem = uMsg == WM_KEYDOWN; - if (wParam == VK_MENU) { - alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); - if (lParam & (1 << 24)) - gr_mem = alt_mem; - } - - if (mouse_mode == MOUSE_MODE_CAPTURED) { - // When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves - if (wParam == VK_F4 && alt_mem && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) { - if (main_loop) - main_loop->notification(NOTIFICATION_WM_CLOSE_REQUEST); - } - } - /* - if (wParam==VK_WIN) TODO wtf is this? - meta_mem=uMsg==WM_KEYDOWN; - */ - [[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. - KeyEvent ke; - ke.shift = (wParam != VK_SHIFT) ? shift_mem : false; - ke.alt = (!(wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false; - ke.control = (wParam != VK_CONTROL) ? control_mem : false; - ke.meta = meta_mem; - ke.uMsg = uMsg; - - if (ke.uMsg == WM_SYSKEYDOWN) - ke.uMsg = WM_KEYDOWN; - if (ke.uMsg == WM_SYSKEYUP) - ke.uMsg = WM_KEYUP; - - ke.wParam = wParam; - ke.lParam = lParam; - key_event_buffer[key_event_pos++] = ke; - - } break; - case WM_INPUTLANGCHANGEREQUEST: { - - // FIXME: Do something? - } break; - - case WM_TOUCH: { - - BOOL bHandled = FALSE; - UINT cInputs = LOWORD(wParam); - PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs); - if (pInputs) { - if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) { - for (UINT i = 0; i < cInputs; i++) { - TOUCHINPUT ti = pInputs[i]; - POINT touch_pos = { - TOUCH_COORD_TO_PIXEL(ti.x), - TOUCH_COORD_TO_PIXEL(ti.y), - }; - ScreenToClient(hWnd, &touch_pos); - //do something with each touch input entry - if (ti.dwFlags & TOUCHEVENTF_MOVE) { - - _drag_event(touch_pos.x, touch_pos.y, ti.dwID); - } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) { - - _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID); - }; - } - bHandled = TRUE; - } else { - /* handle the error here */ - } - memdelete_arr(pInputs); - } else { - /* 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_SETCURSOR: { - if (LOWORD(lParam) == HTCLIENT) { - if (window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED)) { - //Hide the cursor - if (hCursor == NULL) - hCursor = SetCursor(NULL); - else - SetCursor(NULL); - } else { - if (hCursor != NULL) { - CursorShape c = cursor_shape; - cursor_shape = CURSOR_MAX; - set_cursor_shape(c); - hCursor = NULL; - } - } - } - - } break; - case WM_DROPFILES: { - - HDROP hDropInfo = (HDROP)wParam; - const int buffsize = 4096; - wchar_t buf[buffsize]; - - int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, NULL, 0); - - Vector files; - - for (int i = 0; i < fcount; i++) { - - DragQueryFileW(hDropInfo, i, buf, buffsize); - String file = buf; - files.push_back(file); - } - - if (files.size() && main_loop) { - main_loop->drop_files(files, 0); - } - - } break; - - default: { - - if (user_proc) { - - return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); - }; - }; - } - - return DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - -LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - - OS_Windows *os_win = static_cast(OS::get_singleton()); - if (os_win) - return os_win->WndProc(hWnd, uMsg, wParam, lParam); - else - return DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - -void OS_Windows::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)) { - Ref k; - k.instance(); - - k->set_shift(ke.shift); - k->set_alt(ke.alt); - k->set_control(ke.control); - k->set_metakey(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_unicode(ke.wParam); - if (k->get_unicode() && gr_mem) { - k->set_alt(false); - k->set_control(false); - } - - if (k->get_unicode() < 32) - k->set_unicode(0); - - input->accumulate_input_event(k); - } - - //do nothing - } break; - case WM_KEYUP: - case WM_KEYDOWN: { - - Ref k; - k.instance(); - - k->set_shift(ke.shift); - k->set_alt(ke.alt); - k->set_control(ke.control); - k->set_metakey(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); - } else { - k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam)); - } - - k->set_physical_keycode(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) { - k->set_unicode(key_event_buffer[i + 1].wParam); - } - if (k->get_unicode() && gr_mem) { - k->set_alt(false); - k->set_control(false); - } - - if (k->get_unicode() < 32) - k->set_unicode(0); - - k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30)))); - - input->accumulate_input_event(k); - - } break; - } - } - - key_event_pos = 0; -} - -enum _MonitorDpiType { - MDT_Effective_DPI = 0, - MDT_Angular_DPI = 1, - MDT_Raw_DPI = 2, - MDT_Default = MDT_Effective_DPI -}; - -static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Default) { - - int dpiX = 96, dpiY = 96; - - static HMODULE Shcore = NULL; - typedef HRESULT(WINAPI * GetDPIForMonitor_t)(HMONITOR hmonitor, _MonitorDpiType dpiType, UINT * dpiX, UINT * dpiY); - static GetDPIForMonitor_t getDPIForMonitor = NULL; - - if (Shcore == NULL) { - Shcore = LoadLibraryW(L"Shcore.dll"); - getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : NULL; - - if ((Shcore == NULL) || (getDPIForMonitor == NULL)) { - 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); - if (SUCCEEDED(hr) && (x > 0) && (y > 0)) { - - dpiX = (int)x; - dpiY = (int)y; - } - } else { - static int overallX = 0, overallY = 0; - if (overallX <= 0 || overallY <= 0) { - HDC hdc = GetDC(NULL); - if (hdc) { - overallX = GetDeviceCaps(hdc, LOGPIXELSX); - overallY = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); - } - } - if (overallX > 0 && overallY > 0) { - dpiX = overallX; - dpiY = overallY; - } - } - - return (dpiX + dpiY) / 2; -} - -typedef enum _SHC_PROCESS_DPI_AWARENESS { - SHC_PROCESS_DPI_UNAWARE = 0, - SHC_PROCESS_SYSTEM_DPI_AWARE = 1, - SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2 -} SHC_PROCESS_DPI_AWARENESS; - -int OS_Windows::get_current_video_driver() const { - return video_driver_index; -} - -Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { - main_loop = NULL; - outside = true; - window_has_focus = true; - WNDCLASSEXW wc; - - if (is_hidpi_allowed()) { - HMODULE Shcore = LoadLibraryW(L"Shcore.dll"); - - if (Shcore != NULL) { - typedef HRESULT(WINAPI * SetProcessDpiAwareness_t)(SHC_PROCESS_DPI_AWARENESS); - - SetProcessDpiAwareness_t SetProcessDpiAwareness = (SetProcessDpiAwareness_t)GetProcAddress(Shcore, "SetProcessDpiAwareness"); - - if (SetProcessDpiAwareness) { - SetProcessDpiAwareness(SHC_PROCESS_SYSTEM_DPI_AWARE); - } - } - } - - video_mode = p_desired; - //printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false"); - RECT WindowRect; - - WindowRect.left = 0; - WindowRect.right = video_mode.width; - WindowRect.top = 0; - WindowRect.bottom = video_mode.height; - - memset(&wc, 0, sizeof(WNDCLASSEXW)); - wc.cbSize = sizeof(WNDCLASSEXW); - wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; - wc.lpfnWndProc = (WNDPROC)::WndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - //wc.hInstance = hInstance; - wc.hInstance = godot_hinstance ? godot_hinstance : GetModuleHandle(NULL); - wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); - wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = NULL; - wc.lpszMenuName = NULL; - wc.lpszClassName = L"Engine"; - - if (!RegisterClassExW(&wc)) { - MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONEXCLAMATION); - return ERR_UNAVAILABLE; - } - - use_raw_input = true; - - RAWINPUTDEVICE Rid[1]; - - Rid[0].usUsagePage = 0x01; - Rid[0].usUsage = 0x02; - Rid[0].dwFlags = 0; - Rid[0].hwndTarget = 0; - - if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) { - //registration failed. - use_raw_input = false; - } - - pre_fs_valid = true; - if (video_mode.fullscreen) { - - /* this returns DPI unaware size, commenting - DEVMODE current; - memset(¤t, 0, sizeof(current)); - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤t); - - WindowRect.right = current.dmPelsWidth; - WindowRect.bottom = current.dmPelsHeight; - - */ - - EnumSizeData data = { 0, 0, Size2() }; - EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data); - - WindowRect.right = data.size.width; - WindowRect.bottom = data.size.height; - - /* DEVMODE dmScreenSettings; - memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); - dmScreenSettings.dmSize=sizeof(dmScreenSettings); - dmScreenSettings.dmPelsWidth = video_mode.width; - dmScreenSettings.dmPelsHeight = video_mode.height; - dmScreenSettings.dmBitsPerPel = current.dmBitsPerPel; - dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; - - LONG err = ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN); - if (err!=DISP_CHANGE_SUCCESSFUL) { - - video_mode.fullscreen=false; - }*/ - pre_fs_valid = false; - } - - DWORD dwExStyle; - DWORD dwStyle; - - if (video_mode.fullscreen || video_mode.borderless_window) { - - dwExStyle = WS_EX_APPWINDOW; - dwStyle = WS_POPUP; - - } else { - dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - dwStyle = WS_OVERLAPPEDWINDOW; - if (!video_mode.resizable) { - dwStyle &= ~WS_THICKFRAME; - dwStyle &= ~WS_MAXIMIZEBOX; - } - } - - AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); - - char *windowid; -#ifdef MINGW_ENABLED - windowid = getenv("GODOT_WINDOWID"); -#else - size_t len; - _dupenv_s(&windowid, &len, "GODOT_WINDOWID"); -#endif - - if (windowid) { - -// strtoull on mingw -#ifdef MINGW_ENABLED - hWnd = (HWND)strtoull(windowid, NULL, 0); -#else - hWnd = (HWND)_strtoui64(windowid, NULL, 0); -#endif - free(windowid); - SetLastError(0); - user_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)::WndProc); - DWORD le = GetLastError(); - if (user_proc == 0 && le != 0) { - - printf("Error setting WNDPROC: %li\n", le); - }; - GetWindowLongPtr(hWnd, GWLP_WNDPROC); - - RECT rect; - if (!GetClientRect(hWnd, &rect)) { - MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); - return ERR_UNAVAILABLE; - }; - video_mode.width = rect.right; - video_mode.height = rect.bottom; - video_mode.fullscreen = false; - } else { - - hWnd = CreateWindowExW( - dwExStyle, - L"Engine", L"", - dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, - (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2, - (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2, - WindowRect.right - WindowRect.left, - WindowRect.bottom - WindowRect.top, - NULL, NULL, hInstance, NULL); - if (!hWnd) { - MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); - return ERR_UNAVAILABLE; - } - }; - - if (video_mode.always_on_top) { - SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } - - //!!!!!!!!!!!!!!!!!!!!!!!!!! - //TODO - do Vulkan and GLES2 support checks, driver selection and fallback - video_driver_index = p_video_driver; - print_verbose("Driver: " + String(get_video_driver_name(video_driver_index)) + " [" + itos(video_driver_index) + "]"); - //!!!!!!!!!!!!!!!!!!!!!!!!!! - - // Init context and rendering device -#if defined(OPENGL_ENABLED) - if (video_driver_index == VIDEO_DRIVER_GLES2) { - - context_gles2 = memnew(ContextGL_Windows(hWnd, false)); - - if (context_gles2->initialize() != OK) { - memdelete(context_gles2); - context_gles2 = NULL; - ERR_FAIL_V(ERR_UNAVAILABLE); - } - - context_gles2->set_use_vsync(video_mode.use_vsync); - set_vsync_via_compositor(video_mode.vsync_via_compositor); - - if (RasterizerGLES2::is_viable() == OK) { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - } else { - memdelete(context_gles2); - context_gles2 = NULL; - ERR_FAIL_V(ERR_UNAVAILABLE); - } - } -#endif -#if defined(VULKAN_ENABLED) - if (video_driver_index == VIDEO_DRIVER_VULKAN) { - - context_vulkan = memnew(VulkanContextWindows); - if (context_vulkan->initialize() != OK) { - memdelete(context_vulkan); - context_vulkan = NULL; - ERR_FAIL_V(ERR_UNAVAILABLE); - } - if (context_vulkan->window_create(hWnd, hInstance, get_video_mode().width, get_video_mode().height) == -1) { - memdelete(context_vulkan); - context_vulkan = NULL; - ERR_FAIL_V(ERR_UNAVAILABLE); - } - - //temporary - rendering_device_vulkan = memnew(RenderingDeviceVulkan); - rendering_device_vulkan->initialize(context_vulkan); - - RasterizerRD::make_current(); - } -#endif - - visual_server = memnew(VisualServerRaster); - if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); - } - - visual_server->init(); - - input = memnew(InputDefault); - joypad = memnew(JoypadWindows(input, &hWnd)); - - AudioDriverManager::initialize(p_audio_driver); - - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hWnd; - tme.dwHoverTime = HOVER_DEFAULT; - TrackMouseEvent(&tme); - - RegisterTouchWindow(hWnd, 0); - - _ensure_user_data_dir(); - - DragAcceptFiles(hWnd, true); - - move_timer_id = 1; - - if (!is_no_window_mode_enabled()) { - ShowWindow(hWnd, SW_SHOW); // Show The Window - SetForegroundWindow(hWnd); // Slightly Higher Priority - SetFocus(hWnd); // Sets Keyboard Focus To - } - - if (p_desired.layered) { - set_window_per_pixel_transparency_enabled(true); - } - - // IME - im_himc = ImmGetContext(hWnd); - ImmReleaseContext(hWnd, im_himc); - - im_position = Vector2(); - - set_ime_active(false); - - if (!OS::get_singleton()->is_in_low_processor_usage_mode()) { - //SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); - SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); - DWORD index = 0; - HANDLE handle = AvSetMmThreadCharacteristics("Games", &index); - 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. - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - } - - update_real_mouse_position(); - - return OK; } -void OS_Windows::set_clipboard(const String &p_text) { - - // 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("\n", "\r\n"); - - if (!OpenClipboard(hWnd)) { - ERR_FAIL_MSG("Unable to open clipboard."); - } - EmptyClipboard(); - - HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(CharType)); - ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents."); - - LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem); - memcpy(lptstrCopy, text.c_str(), (text.length() + 1) * sizeof(CharType)); - GlobalUnlock(mem); - - SetClipboardData(CF_UNICODETEXT, mem); - - // set the CF_TEXT version (not needed?) - CharString utf8 = text.utf8(); - mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1); - ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents."); - - LPTSTR ptr = (LPTSTR)GlobalLock(mem); - memcpy(ptr, utf8.get_data(), utf8.length()); - ptr[utf8.length()] = 0; - GlobalUnlock(mem); - - SetClipboardData(CF_TEXT, mem); - - CloseClipboard(); -}; - -String OS_Windows::get_clipboard() const { - - String ret; - if (!OpenClipboard(hWnd)) { - ERR_FAIL_V_MSG("", "Unable to open clipboard."); - }; - - if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { - - HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); - if (mem != NULL) { - - LPWSTR ptr = (LPWSTR)GlobalLock(mem); - if (ptr != NULL) { - - ret = String((CharType *)ptr); - GlobalUnlock(mem); - }; - }; - - } else if (IsClipboardFormatAvailable(CF_TEXT)) { - - HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); - if (mem != NULL) { - - LPTSTR ptr = (LPTSTR)GlobalLock(mem); - if (ptr != NULL) { - - ret.parse_utf8((const char *)ptr); - GlobalUnlock(mem); - }; - }; - }; - - CloseClipboard(); - - return ret; -}; - void OS_Windows::delete_main_loop() { if (main_loop) @@ -1629,7 +225,6 @@ void OS_Windows::delete_main_loop() { void OS_Windows::set_main_loop(MainLoop *p_main_loop) { - input->set_main_loop(p_main_loop); main_loop = p_main_loop; } @@ -1643,38 +238,6 @@ void OS_Windows::finalize() { memdelete(main_loop); main_loop = NULL; - - memdelete(joypad); - memdelete(input); - touch_state.clear(); - - cursors_cache.clear(); - visual_server->finish(); - memdelete(visual_server); - -#if defined(OPENGL_ENABLED) - if (video_driver_index == VIDEO_DRIVER_GLES2) { - - if (context_gles2) - memdelete(context_gles2); - } -#endif -#if defined(VULKAN_ENABLED) - if (video_driver_index == VIDEO_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(hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc); - }; } void OS_Windows::finalize_core() { @@ -1685,576 +248,6 @@ void OS_Windows::finalize_core() { NetSocketPosix::cleanup(); } -void OS_Windows::alert(const String &p_alert, const String &p_title) { - - if (!is_no_window_mode_enabled()) - MessageBoxW(NULL, p_alert.c_str(), p_title.c_str(), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL); - else - print_line("ALERT: " + p_alert); -} - -void OS_Windows::set_mouse_mode(MouseMode p_mode) { - - if (mouse_mode == p_mode) - return; - - _set_mouse_mode_impl(p_mode); - - mouse_mode = p_mode; -} - -void OS_Windows::_set_mouse_mode_impl(MouseMode p_mode) { - - if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED) { - RECT clipRect; - GetClientRect(hWnd, &clipRect); - ClientToScreen(hWnd, (POINT *)&clipRect.left); - ClientToScreen(hWnd, (POINT *)&clipRect.right); - ClipCursor(&clipRect); - if (p_mode == MOUSE_MODE_CAPTURED) { - center = Point2i(video_mode.width / 2, video_mode.height / 2); - POINT pos = { (int)center.x, (int)center.y }; - ClientToScreen(hWnd, &pos); - SetCursorPos(pos.x, pos.y); - SetCapture(hWnd); - } - } else { - ReleaseCapture(); - ClipCursor(NULL); - } - - if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) { - hCursor = SetCursor(NULL); - } else { - CursorShape c = cursor_shape; - cursor_shape = CURSOR_MAX; - set_cursor_shape(c); - } -} -OS_Windows::MouseMode OS_Windows::get_mouse_mode() const { - - return mouse_mode; -} - -void OS_Windows::warp_mouse_position(const Point2 &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; - ClientToScreen(hWnd, &p); - - SetCursorPos(p.x, p.y); - } -} - -Point2 OS_Windows::get_mouse_position() const { - - return Point2(old_x, old_y); -} - -void OS_Windows::update_real_mouse_position() { - - POINT mouse_pos; - if (GetCursorPos(&mouse_pos) && ScreenToClient(hWnd, &mouse_pos)) { - if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= video_mode.width && mouse_pos.y <= video_mode.height) { - old_x = mouse_pos.x; - old_y = mouse_pos.y; - old_invalid = false; - input->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y)); - } - } -} - -int OS_Windows::get_mouse_button_state() const { - - return last_button_state; -} - -void OS_Windows::set_window_title(const String &p_title) { - - SetWindowTextW(hWnd, p_title.c_str()); -} - -void OS_Windows::set_video_mode(const VideoMode &p_video_mode, int p_screen) { -} - -OS::VideoMode OS_Windows::get_video_mode(int p_screen) const { - - return video_mode; -} -void OS_Windows::get_fullscreen_mode_list(List *p_list, int p_screen) const { -} - -static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - - int *data = (int *)dwData; - (*data)++; - return TRUE; -} - -int OS_Windows::get_screen_count() const { - - int data = 0; - EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcCount, (LPARAM)&data); - return data; -} - -typedef struct { - int count; - int screen; - HMONITOR monitor; -} 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; - } - - data->count++; - return TRUE; -} - -int OS_Windows::get_current_screen() const { - - EnumScreenData data = { 0, 0, MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST) }; - EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcScreen, (LPARAM)&data); - return data.screen; -} - -void OS_Windows::set_current_screen(int p_screen) { - - Vector2 ofs = get_window_position() - get_screen_position(get_current_screen()); - set_window_position(ofs + get_screen_position(p_screen)); -} - -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; - data->pos.y = lprcMonitor->top; - } - - data->count++; - return TRUE; -} - -Point2 OS_Windows::get_screen_position(int p_screen) const { - - EnumPosData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Point2() }; - EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcPos, (LPARAM)&data); - return data.pos; -} - -Size2 OS_Windows::get_screen_size(int p_screen) const { - - EnumSizeData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Size2() }; - EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data); - return data.size; -} - -typedef struct { - int count; - int screen; - int dpi; -} EnumDpiData; - -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); - } - - data->count++; - return TRUE; -} - -int OS_Windows::get_screen_dpi(int p_screen) const { - - EnumDpiData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, 72 }; - EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcDpi, (LPARAM)&data); - return data.dpi; -} - -Point2 OS_Windows::get_window_position() const { - - if (minimized) { - return last_pos; - } - - RECT r; - GetWindowRect(hWnd, &r); - return Point2(r.left, r.top); -} - -void OS_Windows::set_window_position(const Point2 &p_position) { - - if (video_mode.fullscreen) return; - RECT r; - GetWindowRect(hWnd, &r); - MoveWindow(hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE); - - // Don't let the mouse leave the window when moved - if (mouse_mode == MOUSE_MODE_CONFINED) { - RECT rect; - GetClientRect(hWnd, &rect); - ClientToScreen(hWnd, (POINT *)&rect.left); - ClientToScreen(hWnd, (POINT *)&rect.right); - ClipCursor(&rect); - } - - last_pos = p_position; - update_real_mouse_position(); -} - -Size2 OS_Windows::get_window_size() const { - - if (minimized) { - return Size2(video_mode.width, video_mode.height); - } - - RECT r; - if (GetClientRect(hWnd, &r)) { // Only area inside of window border - return Size2(r.right - r.left, r.bottom - r.top); - } - return Size2(); -} - -Size2 OS_Windows::get_max_window_size() const { - return max_size; -} - -Size2 OS_Windows::get_min_window_size() const { - return min_size; -} - -void OS_Windows::set_min_window_size(const Size2 p_size) { - - if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) { - ERR_PRINT("Minimum window size can't be larger than maximum window size!"); - return; - } - min_size = p_size; -} - -void OS_Windows::set_max_window_size(const Size2 p_size) { - - if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) { - ERR_PRINT("Maximum window size can't be smaller than minimum window size!"); - return; - } - max_size = p_size; -} - -Size2 OS_Windows::get_real_window_size() const { - - RECT r; - if (GetWindowRect(hWnd, &r)) { // Includes area of the window border - return Size2(r.right - r.left, r.bottom - r.top); - } - return Size2(); -} - -void OS_Windows::set_window_size(const Size2 p_size) { - - int w = p_size.width; - int h = p_size.height; - - video_mode.width = w; - video_mode.height = h; -#if defined(VULKAN_ENABLED) - if (video_driver_index == VIDEO_DRIVER_VULKAN) { - context_vulkan->window_resize(0, video_mode.width, video_mode.height); - } -#endif - - if (video_mode.fullscreen) { - return; - } - - RECT rect; - GetWindowRect(hWnd, &rect); - - if (!video_mode.borderless_window) { - RECT crect; - GetClientRect(hWnd, &crect); - - w += (rect.right - rect.left) - (crect.right - crect.left); - h += (rect.bottom - rect.top) - (crect.bottom - crect.top); - } - - MoveWindow(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) { - RECT crect; - GetClientRect(hWnd, &crect); - ClientToScreen(hWnd, (POINT *)&crect.left); - ClientToScreen(hWnd, (POINT *)&crect.right); - ClipCursor(&crect); - } -} -void OS_Windows::set_window_fullscreen(bool p_enabled) { - - if (video_mode.fullscreen == p_enabled) - return; - - if (layered_window) - set_window_per_pixel_transparency_enabled(false); - - if (p_enabled) { - - was_maximized = maximized; - - if (pre_fs_valid) { - GetWindowRect(hWnd, &pre_fs_rect); - } - - int cs = get_current_screen(); - Point2 pos = get_screen_position(cs); - Size2 size = get_screen_size(cs); - - video_mode.fullscreen = true; - - _update_window_style(false); - - MoveWindow(hWnd, pos.x, pos.y, size.width, size.height, TRUE); - - } else { - - RECT rect; - - video_mode.fullscreen = false; - - if (pre_fs_valid) { - rect = pre_fs_rect; - } else { - rect.left = 0; - rect.right = video_mode.width; - rect.top = 0; - rect.bottom = video_mode.height; - } - - _update_window_style(false, was_maximized); - - MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); - - pre_fs_valid = true; - } -} -bool OS_Windows::is_window_fullscreen() const { - - return video_mode.fullscreen; -} -void OS_Windows::set_window_resizable(bool p_enabled) { - - if (video_mode.resizable == p_enabled) - return; - - video_mode.resizable = p_enabled; - - _update_window_style(); -} -bool OS_Windows::is_window_resizable() const { - - return video_mode.resizable; -} -void OS_Windows::set_window_minimized(bool p_enabled) { - - if (p_enabled) { - maximized = false; - minimized = true; - ShowWindow(hWnd, SW_MINIMIZE); - } else { - ShowWindow(hWnd, SW_RESTORE); - maximized = false; - minimized = false; - } -} -bool OS_Windows::is_window_minimized() const { - - return minimized; -} -void OS_Windows::set_window_maximized(bool p_enabled) { - - if (p_enabled) { - maximized = true; - minimized = false; - ShowWindow(hWnd, SW_MAXIMIZE); - } else { - ShowWindow(hWnd, SW_RESTORE); - maximized = false; - minimized = false; - } -} -bool OS_Windows::is_window_maximized() const { - - return maximized; -} - -void OS_Windows::set_window_always_on_top(bool p_enabled) { - if (video_mode.always_on_top == p_enabled) - return; - - video_mode.always_on_top = p_enabled; - - _update_window_style(); -} - -bool OS_Windows::is_window_always_on_top() const { - return video_mode.always_on_top; -} - -bool OS_Windows::is_window_focused() const { - - return window_focused; -} - -void OS_Windows::set_console_visible(bool p_enabled) { - if (console_visible == p_enabled) - return; - ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE); - console_visible = p_enabled; -} - -bool OS_Windows::is_console_visible() const { - return console_visible; -} - -bool OS_Windows::get_window_per_pixel_transparency_enabled() const { - - if (!is_layered_allowed()) return false; - return layered_window; -} - -void OS_Windows::set_window_per_pixel_transparency_enabled(bool p_enabled) { - - if (!is_layered_allowed()) return; - if (layered_window != p_enabled) { - if (p_enabled) { - set_borderless_window(true); - //enable per-pixel alpha - hDC_dib = CreateCompatibleDC(GetDC(hWnd)); - - SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED); - - RECT r; - GetWindowRect(hWnd, &r); - dib_size = Size2(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, NULL, 0x0); - SelectObject(hDC_dib, hBitmap); - - ZeroMemory(dib_data, dib_size.x * dib_size.y * 4); - - layered_window = true; - } else { - //disable per-pixel alpha - layered_window = false; - - SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED); - - //cleanup - DeleteObject(hBitmap); - DeleteDC(hDC_dib); - } - } -} - -uint8_t *OS_Windows::get_layered_buffer_data() { - - return (is_layered_allowed() && layered_window) ? dib_data : NULL; -} - -Size2 OS_Windows::get_layered_buffer_size() { - - return (is_layered_allowed() && layered_window) ? dib_size : Size2(); -} - -void OS_Windows::swap_layered_buffer() { - - if (is_layered_allowed() && layered_window) { - - //premultiply alpha - for (int y = 0; y < dib_size.y; y++) { - for (int x = 0; x < dib_size.x; x++) { - float alpha = (float)dib_data[y * (int)dib_size.x * 4 + x * 4 + 3] / (float)0xFF; - dib_data[y * (int)dib_size.x * 4 + x * 4 + 0] *= alpha; - dib_data[y * (int)dib_size.x * 4 + x * 4 + 1] *= alpha; - dib_data[y * (int)dib_size.x * 4 + x * 4 + 2] *= alpha; - } - } - //swap layered window buffer - POINT ptSrc = { 0, 0 }; - SIZE sizeWnd = { (long)dib_size.x, (long)dib_size.y }; - BLENDFUNCTION bf; - bf.BlendOp = AC_SRC_OVER; - bf.BlendFlags = 0; - bf.AlphaFormat = AC_SRC_ALPHA; - bf.SourceConstantAlpha = 0xFF; - UpdateLayeredWindow(hWnd, NULL, NULL, &sizeWnd, hDC_dib, &ptSrc, 0, &bf, ULW_ALPHA); - } -} - -void OS_Windows::set_borderless_window(bool p_borderless) { - if (video_mode.borderless_window == p_borderless) - return; - - if (!p_borderless && layered_window) - set_window_per_pixel_transparency_enabled(false); - - video_mode.borderless_window = p_borderless; - - preserve_window_size = true; - _update_window_style(); -} - -bool OS_Windows::get_borderless_window() { - return video_mode.borderless_window; -} - -void OS_Windows::_update_window_style(bool p_repaint, bool p_maximized) { - if (video_mode.fullscreen || video_mode.borderless_window) { - SetWindowLongPtr(hWnd, GWL_STYLE, WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE); - } else { - if (video_mode.resizable) { - if (p_maximized) { - SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE); - } else { - SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE); - } - } else { - SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_MINIMIZEBOX | WS_POPUPWINDOW | WS_VISIBLE); - } - } - - SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE); - - if (p_repaint) { - RECT rect; - GetWindowRect(hWnd, &rect); - MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); - } -} - Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { String path = p_path; @@ -2306,17 +299,6 @@ Error OS_Windows::get_dynamic_library_symbol_handle(void *p_library_handle, cons return OK; } -void OS_Windows::request_attention() { - - FLASHWINFO info; - info.cbSize = sizeof(FLASHWINFO); - info.hwnd = hWnd; - info.dwFlags = FLASHW_TRAY; - info.dwTimeout = 0; - info.uCount = 2; - FlashWindowEx(&info); -} - String OS_Windows::get_name() const { return "Windows"; @@ -2448,253 +430,6 @@ uint64_t OS_Windows::get_ticks_usec() const { return time; } -void OS_Windows::process_events() { - - MSG msg; - - if (!drop_events) { - joypad->process_joypads(); - } - - while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { - - TranslateMessage(&msg); - DispatchMessageW(&msg); - } - - if (!drop_events) { - process_key_events(); - input->flush_accumulated_events(); - } -} - -void OS_Windows::set_cursor_shape(CursorShape p_shape) { - - ERR_FAIL_INDEX(p_shape, CURSOR_MAX); - - if (cursor_shape == p_shape) - return; - - if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { - cursor_shape = p_shape; - return; - } - - static const LPCTSTR win_cursors[CURSOR_MAX] = { - IDC_ARROW, - IDC_IBEAM, - IDC_HAND, //finger - IDC_CROSS, - IDC_WAIT, - IDC_APPSTARTING, - IDC_ARROW, - IDC_ARROW, - IDC_NO, - IDC_SIZENS, - IDC_SIZEWE, - IDC_SIZENESW, - IDC_SIZENWSE, - IDC_SIZEALL, - IDC_SIZENS, - IDC_SIZEWE, - IDC_HELP - }; - - if (cursors[p_shape] != NULL) { - SetCursor(cursors[p_shape]); - } else { - SetCursor(LoadCursor(hInstance, win_cursors[p_shape])); - } - - cursor_shape = p_shape; -} - -OS::CursorShape OS_Windows::get_cursor_shape() const { - - return cursor_shape; -} - -void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { - - if (p_cursor.is_valid()) { - - Map>::Element *cursor_c = cursors_cache.find(p_shape); - - if (cursor_c) { - if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) { - set_cursor_shape(p_shape); - return; - } - - cursors_cache.erase(p_shape); - } - - Ref texture = p_cursor; - Ref atlas_texture = p_cursor; - Ref image; - Size2 texture_size; - Rect2 atlas_rect; - - if (texture.is_valid()) { - image = texture->get_data(); - } - - if (!image.is_valid() && atlas_texture.is_valid()) { - texture = atlas_texture->get_atlas(); - - atlas_rect.size.width = texture->get_width(); - atlas_rect.size.height = texture->get_height(); - atlas_rect.position.x = atlas_texture->get_region().position.x; - atlas_rect.position.y = atlas_texture->get_region().position.y; - - texture_size.width = atlas_texture->get_region().size.x; - texture_size.height = atlas_texture->get_region().size.y; - } else if (image.is_valid()) { - texture_size.width = texture->get_width(); - texture_size.height = texture->get_height(); - } - - ERR_FAIL_COND(!texture.is_valid()); - ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); - 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(); - - ERR_FAIL_COND(!image.is_valid()); - - UINT image_size = texture_size.width * texture_size.height; - - // Create the BITMAP with alpha channel - COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size); - - for (UINT index = 0; index < image_size; index++) { - int row_index = floor(index / texture_size.width) + atlas_rect.position.y; - int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; - - if (atlas_texture.is_valid()) { - column_index = MIN(column_index, atlas_rect.size.width - 1); - row_index = MIN(row_index, atlas_rect.size.height - 1); - } - - *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32(); - } - - // Using 4 channels, so 4 * 8 bits - HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer); - COLORREF clrTransparent = -1; - - // Create the AND and XOR masks for the bitmap - HBITMAP hAndMask = NULL; - HBITMAP hXorMask = NULL; - - GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask); - - if (NULL == hAndMask || NULL == hXorMask) { - memfree(buffer); - DeleteObject(bitmap); - return; - } - - // Finally, create the icon - ICONINFO iconinfo; - iconinfo.fIcon = FALSE; - iconinfo.xHotspot = p_hotspot.x; - iconinfo.yHotspot = p_hotspot.y; - iconinfo.hbmMask = hAndMask; - iconinfo.hbmColor = hXorMask; - - if (cursors[p_shape]) - DestroyIcon(cursors[p_shape]); - - cursors[p_shape] = CreateIconIndirect(&iconinfo); - - Vector params; - params.push_back(p_cursor); - params.push_back(p_hotspot); - cursors_cache.insert(p_shape, params); - - if (p_shape == cursor_shape) { - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { - SetCursor(cursors[p_shape]); - } - } - - if (hAndMask != NULL) { - DeleteObject(hAndMask); - } - - if (hXorMask != NULL) { - DeleteObject(hXorMask); - } - - memfree(buffer); - DeleteObject(bitmap); - } else { - // Reset to default system cursor - if (cursors[p_shape]) { - DestroyIcon(cursors[p_shape]); - cursors[p_shape] = NULL; - } - - CursorShape c = cursor_shape; - cursor_shape = CURSOR_MAX; - set_cursor_shape(c); - - cursors_cache.erase(p_shape); - } -} - -void OS_Windows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) { - - // Get the system display DC - HDC hDC = GetDC(NULL); - - // Create helper DC - HDC hMainDC = CreateCompatibleDC(hDC); - HDC hAndMaskDC = CreateCompatibleDC(hDC); - HDC hXorMaskDC = CreateCompatibleDC(hDC); - - // Get the dimensions of the source bitmap - BITMAP bm; - GetObject(hSourceBitmap, sizeof(BITMAP), &bm); - - // Create the mask bitmaps - hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color - hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color - - // Release the system display DC - ReleaseDC(NULL, hDC); - - // Select the bitmaps to helper DC - HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap); - HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap); - HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap); - - // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap - // with 'clrTransparent' will be white pixels of the monochrome bitmap - SetBkColor(hMainDC, clrTransparent); - BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY); - - // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap - // with 'clrTransparent' will be black and rest the pixels same as corresponding - // pixels of the source bitmap - SetBkColor(hXorMaskDC, RGB(0, 0, 0)); - SetTextColor(hXorMaskDC, RGB(255, 255, 255)); - BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY); - BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND); - - // Deselect bitmaps from the helper DC - SelectObject(hMainDC, hOldMainBitmap); - SelectObject(hAndMaskDC, hOldAndMaskBitmap); - SelectObject(hXorMaskDC, hOldXorMaskBitmap); - - // Delete the helper DC - DeleteDC(hXorMaskDC); - DeleteDC(hAndMaskDC); - DeleteDC(hMainDC); -} - Error OS_Windows::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { if (p_blocking && r_pipe) { @@ -2812,151 +547,6 @@ String OS_Windows::get_executable_path() const { return s; } -void OS_Windows::set_native_icon(const String &p_filename) { - - FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); - ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'."); - - ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR)); - int pos = 0; - - icon_dir->idReserved = f->get_32(); - pos += sizeof(WORD); - f->seek(pos); - - icon_dir->idType = f->get_32(); - pos += sizeof(WORD); - f->seek(pos); - - ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!"); - - icon_dir->idCount = f->get_32(); - pos += sizeof(WORD); - f->seek(pos); - - 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_cc = 0; - int big_icon_index = -1; // Select largest - int big_icon_width = 16; - int big_icon_cc = 0; - - for (int i = 0; i < icon_dir->idCount; i++) { - int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount; - int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth; - if (width == 16) { - if (colors >= small_icon_cc) { - small_icon_index = i; - small_icon_cc = colors; - } - } - if (width >= big_icon_width) { - if (colors >= big_icon_cc) { - big_icon_index = i; - big_icon_width = width; - big_icon_cc = colors; - } - } - } - - ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!"); - - if (small_icon_index == -1) { - WARN_PRINT("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!"); - small_icon_index = big_icon_index; - small_icon_cc = big_icon_cc; - } - - // Read the big icon - DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes; - Vector data_big; - data_big.resize(bytecount_big); - pos = icon_dir->idEntries[big_icon_index].dwImageOffset; - f->seek(pos); - f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big); - 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 - DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes; - Vector data_small; - data_small.resize(bytecount_small); - pos = icon_dir->idEntries[small_icon_index].dwImageOffset; - f->seek(pos); - f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small); - 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 - int err = 0; - SetLastError(err); - - SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small); - err = GetLastError(); - ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + "."); - - SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big); - err = GetLastError(); - ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + "."); - - memdelete(f); - memdelete(icon_dir); -} - -void OS_Windows::set_icon(const Ref &p_icon) { - - ERR_FAIL_COND(!p_icon.is_valid()); - Ref 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 */ - int icon_len = 40 + h * w * 4; - Vector v; - v.resize(icon_len); - BYTE *icon_bmp = v.ptrw(); - - encode_uint32(40, &icon_bmp[0]); - encode_uint32(w, &icon_bmp[4]); - encode_uint32(h * 2, &icon_bmp[8]); - encode_uint16(1, &icon_bmp[12]); - encode_uint16(32, &icon_bmp[14]); - encode_uint32(BI_RGB, &icon_bmp[16]); - encode_uint32(w * h * 4, &icon_bmp[20]); - encode_uint32(0, &icon_bmp[24]); - encode_uint32(0, &icon_bmp[28]); - encode_uint32(0, &icon_bmp[32]); - encode_uint32(0, &icon_bmp[36]); - - uint8_t *wr = &icon_bmp[40]; - 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]; - wpx[1] = rpx[1]; - wpx[2] = rpx[0]; - wpx[3] = rpx[3]; - } - } - - HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); - - /* Set the icon for the window */ - SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon); - - /* Set the icon in the task manager (should we do this?) */ - SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon); -} - bool OS_Windows::has_environment(const String &p_var) const { #ifdef MINGW_ENABLED @@ -2996,16 +586,6 @@ String OS_Windows::get_stdin_string(bool p_block) { return String(); } -void OS_Windows::enable_for_stealing_focus(ProcessID pid) { - - AllowSetForegroundWindow(pid); -} - -void OS_Windows::move_window_to_foreground() { - - SetForegroundWindow(hWnd); -} - Error OS_Windows::shell_open(String p_uri) { ShellExecuteW(NULL, NULL, p_uri.c_str(), NULL, NULL, SW_SHOWNORMAL); @@ -3068,101 +648,6 @@ int OS_Windows::get_processor_count() const { return sysinfo.dwNumberOfProcessors; } -OS::LatinKeyboardVariant OS_Windows::get_latin_keyboard_variant() const { - - 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 - }; - - char name[KL_NAMELENGTH + 1]; - name[0] = 0; - GetKeyboardLayoutNameA(name); - - unsigned long hex = strtoul(name, NULL, 16); - - int i = 0; - while (azerty[i] != 0) { - if (azerty[i] == hex) return LATIN_KEYBOARD_AZERTY; - i++; - } - - i = 0; - while (qwertz[i] != 0) { - if (qwertz[i] == hex) return LATIN_KEYBOARD_QWERTZ; - i++; - } - - i = 0; - while (dvorak[i] != 0) { - if (dvorak[i] == hex) return LATIN_KEYBOARD_DVORAK; - i++; - } - - return LATIN_KEYBOARD_QWERTY; -} - -void OS_Windows::release_rendering_thread() { -#if defined(OPENGL_ENABLED) - if (video_driver_index == VIDEO_DRIVER_GLES2) { - context_gles2->release_current(); - } -#endif -} - -void OS_Windows::make_rendering_thread() { -#if defined(OPENGL_ENABLED) - if (video_driver_index == VIDEO_DRIVER_GLES2) { - context_gles2->make_current(); - } -#endif -} - -void OS_Windows::swap_buffers() { -#if defined(OPENGL_ENABLED) - if (video_driver_index == VIDEO_DRIVER_GLES2) { - context_gles2->swap_buffers(); - } -#endif -#if defined(VULKAN_ENABLED) - if (video_driver_index == VIDEO_DRIVER_VULKAN) { - context_vulkan->swap_buffers(); - } -#endif -} - -void OS_Windows::force_process_input() { - process_events(); // get rid of pending events -} - void OS_Windows::run() { if (!main_loop) @@ -3172,7 +657,7 @@ void OS_Windows::run() { while (!force_quit) { - process_events(); // get rid of pending events + DisplayServer::get_singleton()->process_events(); // get rid of pending events if (Main::iteration()) break; }; @@ -3287,50 +772,6 @@ String OS_Windows::get_unique_id() const { return String(HwProfInfo.szHwProfileGuid); } -void OS_Windows::set_ime_active(const bool p_active) { - - if (p_active) { - ImmAssociateContext(hWnd, im_himc); - - set_ime_position(im_position); - } else { - ImmAssociateContext(hWnd, (HIMC)0); - } -} - -void OS_Windows::set_ime_position(const Point2 &p_pos) { - - im_position = p_pos; - - HIMC himc = ImmGetContext(hWnd); - if (himc == (HIMC)0) - return; - - COMPOSITIONFORM cps; - cps.dwStyle = CFS_FORCE_POSITION; - cps.ptCurrentPos.x = im_position.x; - cps.ptCurrentPos.y = im_position.y; - ImmSetCompositionWindow(himc, &cps); - ImmReleaseContext(hWnd, himc); -} - -bool OS_Windows::is_joy_known(int p_device) { - return input->is_joy_mapped(p_device); -} - -String OS_Windows::get_joy_guid(int p_device) const { - return input->get_joy_guid_remapped(p_device); -} - -void OS_Windows::_set_use_vsync(bool p_enable) { -#if defined(OPENGL_ENABLED) - if (video_driver_index == VIDEO_DRIVER_GLES2) { - if (context_gles2) - context_gles2->set_use_vsync(p_enable); - } -#endif -} - bool OS_Windows::_check_internal_feature_support(const String &p_feature) { return p_feature == "pc"; @@ -3344,20 +785,13 @@ bool OS_Windows::is_disable_crash_handler() const { return crash_handler.is_disabled(); } -void OS_Windows::process_and_drop_events() { - - drop_events = true; - process_events(); - drop_events = false; -} - Error OS_Windows::move_to_trash(const String &p_path) { SHFILEOPSTRUCTW sf; WCHAR *from = new WCHAR[p_path.length() + 2]; wcscpy_s(from, p_path.length() + 1, p_path.c_str()); from[p_path.length() + 1] = 0; - sf.hwnd = hWnd; + sf.hwnd = main_window; sf.wFunc = FO_DELETE; sf.pFrom = from; sf.pTo = NULL; @@ -3379,36 +813,12 @@ Error OS_Windows::move_to_trash(const String &p_path) { OS_Windows::OS_Windows(HINSTANCE _hInstance) { - drop_events = false; - key_event_pos = 0; - layered_window = false; - hBitmap = NULL; force_quit = false; - alt_mem = false; - gr_mem = false; - shift_mem = false; - control_mem = false; - meta_mem = false; - minimized = false; - was_maximized = false; - window_focused = true; - console_visible = IsWindowVisible(GetConsoleWindow()); - - //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"); - } hInstance = _hInstance; - pressrc = 0; - old_invalid = true; - mouse_mode = MOUSE_MODE_VISIBLE; #ifdef STDOUT_FILE stdo = fopen("stdout.txt", "wb"); #endif - user_proc = NULL; #ifdef WASAPI_ENABLED AudioDriverManager::add_driver(&driver_wasapi); @@ -3417,16 +827,14 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { AudioDriverManager::add_driver(&driver_xaudio2); #endif + DisplayServerWindows::register_windows_driver(); + Vector loggers; loggers.push_back(memnew(WindowsTerminalLogger)); _set_logger(memnew(CompositeLogger(loggers))); } OS_Windows::~OS_Windows() { - if (is_layered_allowed() && layered_window) { - DeleteObject(hBitmap); - DeleteDC(hDC_dib); - } #ifdef STDOUT_FILE fclose(stdo); #endif diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index d07423f2f38..040951668df 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -61,183 +61,19 @@ #include #include -#ifndef POINTER_STRUCTURES - -#define POINTER_STRUCTURES - -typedef DWORD POINTER_INPUT_TYPE; -typedef UINT32 POINTER_FLAGS; -typedef UINT32 PEN_FLAGS; -typedef UINT32 PEN_MASK; - -enum tagPOINTER_INPUT_TYPE { - PT_POINTER = 0x00000001, - PT_TOUCH = 0x00000002, - PT_PEN = 0x00000003, - PT_MOUSE = 0x00000004, - PT_TOUCHPAD = 0x00000005 -}; - -typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { - POINTER_CHANGE_NONE, - POINTER_CHANGE_FIRSTBUTTON_DOWN, - POINTER_CHANGE_FIRSTBUTTON_UP, - POINTER_CHANGE_SECONDBUTTON_DOWN, - POINTER_CHANGE_SECONDBUTTON_UP, - POINTER_CHANGE_THIRDBUTTON_DOWN, - POINTER_CHANGE_THIRDBUTTON_UP, - POINTER_CHANGE_FOURTHBUTTON_DOWN, - POINTER_CHANGE_FOURTHBUTTON_UP, - POINTER_CHANGE_FIFTHBUTTON_DOWN, - POINTER_CHANGE_FIFTHBUTTON_UP, -} POINTER_BUTTON_CHANGE_TYPE; - -typedef struct tagPOINTER_INFO { - POINTER_INPUT_TYPE pointerType; - UINT32 pointerId; - UINT32 frameId; - POINTER_FLAGS pointerFlags; - HANDLE sourceDevice; - HWND hwndTarget; - POINT ptPixelLocation; - POINT ptHimetricLocation; - POINT ptPixelLocationRaw; - POINT ptHimetricLocationRaw; - DWORD dwTime; - UINT32 historyCount; - INT32 InputData; - DWORD dwKeyStates; - UINT64 PerformanceCount; - POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; -} POINTER_INFO; - -typedef struct tagPOINTER_PEN_INFO { - POINTER_INFO pointerInfo; - PEN_FLAGS penFlags; - PEN_MASK penMask; - UINT32 pressure; - UINT32 rotation; - INT32 tiltX; - INT32 tiltY; -} POINTER_PEN_INFO; - -#endif - -typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type); -typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info); - -typedef struct { - BYTE bWidth; // Width, in pixels, of the image - BYTE bHeight; // Height, in pixels, of the image - BYTE bColorCount; // Number of colors in image (0 if >=8bpp) - BYTE bReserved; // Reserved ( must be 0) - WORD wPlanes; // Color Planes - WORD wBitCount; // Bits per pixel - DWORD dwBytesInRes; // How many bytes in this resource? - DWORD dwImageOffset; // Where in the file is this image? -} ICONDIRENTRY, *LPICONDIRENTRY; - -typedef struct { - WORD idReserved; // Reserved (must be 0) - WORD idType; // Resource Type (1 for icons) - WORD idCount; // How many images? - ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) -} ICONDIR, *LPICONDIR; - class JoypadWindows; class OS_Windows : public OS { - static GetPointerTypePtr win8p_GetPointerType; - static GetPointerPenInfoPtr win8p_GetPointerPenInfo; - - enum { - KEY_EVENT_BUFFER_SIZE = 512 - }; - #ifdef STDOUT_FILE FILE *stdo; #endif - struct KeyEvent { - - bool alt, shift, control, meta; - UINT uMsg; - WPARAM wParam; - LPARAM lParam; - }; - - KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE]; - int key_event_pos; - uint64_t ticks_start; uint64_t ticks_per_second; - bool old_invalid; - bool outside; - int old_x, old_y; - Point2i center; - -#if defined(OPENGL_ENABLED) - ContextGL_Windows *context_gles2; -#endif - -#if defined(VULKAN_ENABLED) - VulkanContextWindows *context_vulkan; - RenderingDeviceVulkan *rendering_device_vulkan; -#endif - - VisualServer *visual_server; - int pressrc; - HINSTANCE hInstance; // Holds The Instance Of The Application - HWND hWnd; - Point2 last_pos; - - HBITMAP hBitmap; //DIB section for layered window - uint8_t *dib_data; - Size2 dib_size; - HDC hDC_dib; - bool layered_window; - - uint32_t move_timer_id; - - HCURSOR hCursor; - - Size2 min_size; - Size2 max_size; - - Size2 window_rect; - VideoMode video_mode; - bool preserve_window_size = false; - + HINSTANCE hInstance; MainLoop *main_loop; - WNDPROC user_proc; - - // IME - HIMC im_himc; - Vector2 im_position; - - MouseMode mouse_mode; - bool alt_mem; - bool gr_mem; - bool shift_mem; - bool control_mem; - bool meta_mem; - bool force_quit; - bool window_has_focus; - uint32_t last_button_state; - bool use_raw_input; - bool drop_events; - - HCURSOR cursors[CURSOR_MAX] = { NULL }; - CursorShape cursor_shape; - Map> cursors_cache; - - InputDefault *input; - JoypadWindows *joypad; - Map touch_state; - - int video_driver_index; #ifdef WASAPI_ENABLED AudioDriverWASAPI driver_wasapi; #endif @@ -250,28 +86,19 @@ class OS_Windows : public OS { CrashHandler crash_handler; - void _drag_event(float p_x, float p_y, int idx); - void _touch_event(bool p_pressed, float p_x, float p_y, int idx); - - void _update_window_style(bool p_repaint = true, bool p_maximized = false); - - void _set_mouse_mode_impl(MouseMode p_mode); + bool force_quit; + HWND main_window; // functions used by main to initialize/deinitialize the OS protected: - virtual int get_current_video_driver() const; - - virtual void initialize_core(); - virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual void initialize(); virtual void set_main_loop(MainLoop *p_main_loop); virtual void delete_main_loop(); virtual void finalize(); virtual void finalize_core(); - - void process_events(); - void process_key_events(); + virtual String get_stdin_string(bool p_block); struct ProcessInfo { @@ -280,75 +107,7 @@ protected: }; Map *process_map; - bool pre_fs_valid; - RECT pre_fs_rect; - bool maximized; - bool minimized; - bool borderless; - bool window_focused; - bool console_visible; - bool was_maximized; - public: - LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - - virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); - String get_stdin_string(bool p_block); - - void set_mouse_mode(MouseMode p_mode); - MouseMode get_mouse_mode() const; - - virtual void warp_mouse_position(const Point2 &p_to); - virtual Point2 get_mouse_position() const; - void update_real_mouse_position(); - virtual int get_mouse_button_state() const; - virtual void set_window_title(const String &p_title); - - virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); - virtual VideoMode get_video_mode(int p_screen = 0) const; - virtual void get_fullscreen_mode_list(List *p_list, int p_screen = 0) const; - - virtual int get_screen_count() const; - virtual int get_current_screen() const; - virtual void set_current_screen(int p_screen); - virtual Point2 get_screen_position(int p_screen = -1) const; - virtual Size2 get_screen_size(int p_screen = -1) const; - virtual int get_screen_dpi(int p_screen = -1) const; - - virtual Point2 get_window_position() const; - virtual void set_window_position(const Point2 &p_position); - virtual Size2 get_window_size() const; - virtual Size2 get_real_window_size() const; - virtual Size2 get_max_window_size() const; - virtual Size2 get_min_window_size() const; - virtual void set_min_window_size(const Size2 p_size); - virtual void set_max_window_size(const Size2 p_size); - virtual void set_window_size(const Size2 p_size); - virtual void set_window_fullscreen(bool p_enabled); - virtual bool is_window_fullscreen() const; - virtual void set_window_resizable(bool p_enabled); - virtual bool is_window_resizable() const; - virtual void set_window_minimized(bool p_enabled); - virtual bool is_window_minimized() const; - virtual void set_window_maximized(bool p_enabled); - virtual bool is_window_maximized() const; - virtual void set_window_always_on_top(bool p_enabled); - virtual bool is_window_always_on_top() const; - virtual bool is_window_focused() const; - virtual void set_console_visible(bool p_enabled); - virtual bool is_console_visible() const; - virtual void request_attention(); - - virtual void set_borderless_window(bool p_borderless); - virtual bool get_borderless_window(); - - virtual bool get_window_per_pixel_transparency_enabled() const; - virtual void set_window_per_pixel_transparency_enabled(bool p_enabled); - - virtual uint8_t *get_layered_buffer_data(); - virtual Size2 get_layered_buffer_size(); - virtual void swap_layered_buffer(); - virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); virtual Error close_dynamic_library(void *p_library_handle); virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false); @@ -357,6 +116,8 @@ public: virtual String get_name() const; + virtual void initialize_joypads() {} + virtual Date get_date(bool utc) const; virtual Time get_time(bool utc) const; virtual TimeZoneInfo get_time_zone_info() const; @@ -364,7 +125,6 @@ public: virtual uint64_t get_system_time_secs() const; virtual uint64_t get_system_time_msecs() const; - virtual bool can_draw() const; virtual Error set_cwd(const String &p_cwd); virtual void delay_usec(uint32_t p_usec) const; @@ -378,28 +138,12 @@ public: virtual String get_environment(const String &p_var) const; virtual bool set_environment(const String &p_var, const String &p_value) const; - virtual void set_clipboard(const String &p_text); - virtual String get_clipboard() const; - - void set_cursor_shape(CursorShape p_shape); - CursorShape get_cursor_shape() const; - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); - void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap); - - void set_native_icon(const String &p_filename); - void set_icon(const Ref &p_icon); - virtual String get_executable_path() const; virtual String get_locale() const; virtual int get_processor_count() const; - virtual LatinKeyboardVariant get_latin_keyboard_variant() const; - - virtual void enable_for_stealing_focus(ProcessID pid); - virtual void move_window_to_foreground(); - virtual String get_config_path() const; virtual String get_data_path() const; virtual String get_cache_path() const; @@ -410,37 +154,21 @@ public: virtual String get_unique_id() const; - virtual void set_ime_active(const bool p_active); - virtual void set_ime_position(const Point2 &p_pos); - - virtual void release_rendering_thread(); - virtual void make_rendering_thread(); - virtual void swap_buffers(); - virtual Error shell_open(String p_uri); void run(); - virtual bool get_swap_ok_cancel() { return true; } - - virtual bool is_joy_known(int p_device); - virtual String get_joy_guid(int p_device) const; - - virtual void _set_use_vsync(bool p_enable); - //virtual bool is_vsync_enabled() const; - virtual bool _check_internal_feature_support(const String &p_feature); void disable_crash_handler(); bool is_disable_crash_handler() const; virtual void initialize_debugging(); - void force_process_input(); - virtual Error move_to_trash(const String &p_path); - virtual void process_and_drop_events(); + void set_main_window(HWND p_main_window) { main_window = p_main_window; } + HINSTANCE get_hinstance() { return hInstance; } OS_Windows(HINSTANCE _hInstance); ~OS_Windows(); }; diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp index 20e1b466824..66b5cf81132 100644 --- a/platform/windows/vulkan_context_win.cpp +++ b/platform/windows/vulkan_context_win.cpp @@ -35,7 +35,7 @@ const char *VulkanContextWindows::_get_platform_surface_extension() const { return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; } -int VulkanContextWindows::window_create(HWND p_window, HINSTANCE p_instance, int p_width, int p_height) { +int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) { VkWin32SurfaceCreateInfoKHR createInfo; createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; @@ -47,7 +47,7 @@ int VulkanContextWindows::window_create(HWND p_window, HINSTANCE p_instance, int VkSurfaceKHR surface; VkResult err = vkCreateWin32SurfaceKHR(_get_instance(), &createInfo, NULL, &surface); ERR_FAIL_COND_V(err, -1); - return _window_create(surface, p_width, p_height); + return _window_create(p_window_id, surface, p_width, p_height); } VulkanContextWindows::VulkanContextWindows() { diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h index 1289f2a299c..c9fea9369b9 100644 --- a/platform/windows/vulkan_context_win.h +++ b/platform/windows/vulkan_context_win.h @@ -39,7 +39,7 @@ class VulkanContextWindows : public VulkanContext { virtual const char *_get_platform_surface_extension() const; public: - int window_create(HWND p_window, HINSTANCE p_instance, int p_width, int p_height); + int window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height); VulkanContextWindows(); ~VulkanContextWindows(); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 102012f6d40..5b030be5a65 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -34,6 +34,8 @@ DisplayServer *DisplayServer::singleton = nullptr; DisplayServer::SwitchVSyncCallbackInThread DisplayServer::switch_vsync_function = nullptr; +bool DisplayServer::hidpi_allowed = false; + DisplayServer::DisplayServerCreate DisplayServer::server_create_functions[DisplayServer::MAX_SERVERS]; int DisplayServer::server_create_count = 0; diff --git a/servers/display_server.h b/servers/display_server.h index 5da9c762168..69e110d2b14 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -41,6 +41,7 @@ class DisplayServer : public Object { static DisplayServer *singleton; bool vsync_enabled = true; + static bool hidpi_allowed; public: _FORCE_INLINE_ static DisplayServer *get_singleton() {