diff options
Diffstat (limited to 'platform/windows/display_server_windows.cpp')
-rw-r--r-- | platform/windows/display_server_windows.cpp | 291 |
1 files changed, 189 insertions, 102 deletions
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e99539f4fe..91f7de7f26 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -30,12 +30,16 @@ #include "display_server_windows.h" +#include "os_windows.h" + #include "core/io/marshalls.h" -#include "core/math/geometry_2d.h" #include "main/main.h" -#include "os_windows.h" #include "scene/resources/texture.h" +#if defined(GLES3_ENABLED) +#include "drivers/gles3/rasterizer_gles3.h" +#endif + #include <avrt.h> #include <dwmapi.h> @@ -43,10 +47,6 @@ #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 #endif -#if defined(GLES3_ENABLED) -#include "drivers/gles3/rasterizer_gles3.h" -#endif - #if defined(__GNUC__) // Workaround GCC warning from -Wcast-function-type. #define GetProcAddress (void *)GetProcAddress @@ -90,6 +90,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const { case FEATURE_SWAP_BUFFERS: case FEATURE_KEEP_SCREEN_ON: case FEATURE_TEXT_TO_SPEECH: + case FEATURE_SCREEN_CAPTURE: return true; default: return false; @@ -178,37 +179,37 @@ void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) } bool DisplayServerWindows::tts_is_speaking() const { - ERR_FAIL_COND_V(!tts, false); + ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->is_speaking(); } bool DisplayServerWindows::tts_is_paused() const { - ERR_FAIL_COND_V(!tts, false); + ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->is_paused(); } TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const { - ERR_FAIL_COND_V(!tts, TypedArray<Dictionary>()); + ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->get_voices(); } void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { - ERR_FAIL_COND(!tts); + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt); } void DisplayServerWindows::tts_pause() { - ERR_FAIL_COND(!tts); + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->pause(); } void DisplayServerWindows::tts_resume() { - ERR_FAIL_COND(!tts); + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->resume(); } void DisplayServerWindows::tts_stop() { - ERR_FAIL_COND(!tts); + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->stop(); } @@ -264,15 +265,15 @@ BitField<MouseButtonMask> DisplayServerWindows::mouse_get_button_state() const { void DisplayServerWindows::clipboard_set(const String &p_text) { _THREAD_SAFE_METHOD_ - if (!windows.has(last_focused_window)) { - return; // No focused window? + if (!windows.has(MAIN_WINDOW_ID)) { + return; } // Convert LF line endings to CRLF in clipboard content. // Otherwise, line endings won't be visible when pasted in other software. String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // Avoid \r\r\n. - if (!OpenClipboard(windows[last_focused_window].hWnd)) { + if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) { ERR_FAIL_MSG("Unable to open clipboard."); } EmptyClipboard(); @@ -305,12 +306,12 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { String DisplayServerWindows::clipboard_get() const { _THREAD_SAFE_METHOD_ - if (!windows.has(last_focused_window)) { - return String(); // No focused window? + if (!windows.has(MAIN_WINDOW_ID)) { + return String(); } String ret; - if (!OpenClipboard(windows[last_focused_window].hWnd)) { + if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) { ERR_FAIL_V_MSG("", "Unable to open clipboard."); } @@ -387,6 +388,17 @@ int DisplayServerWindows::get_primary_screen() const { return data.screen; } +int DisplayServerWindows::get_keyboard_focus_screen() const { + HWND hwnd = GetForegroundWindow(); + if (hwnd) { + EnumScreenData data = { 0, 0, MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) }; + EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data); + return data.screen; + } else { + return get_primary_screen(); + } +} + typedef struct { int count; int screen; @@ -423,17 +435,7 @@ Point2i DisplayServerWindows::_get_screens_origin() const { Point2i DisplayServerWindows::screen_get_position(int p_screen) const { _THREAD_SAFE_METHOD_ - switch (p_screen) { - case SCREEN_PRIMARY: { - p_screen = get_primary_screen(); - } break; - case SCREEN_OF_MAIN_WINDOW: { - p_screen = window_get_current_screen(MAIN_WINDOW_ID); - } break; - default: - break; - } - + p_screen = _get_screen_index(p_screen); EnumPosData data = { 0, p_screen, Point2() }; EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPos, (LPARAM)&data); return data.pos - _get_screens_origin(); @@ -471,17 +473,7 @@ static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPR Size2i DisplayServerWindows::screen_get_size(int p_screen) const { _THREAD_SAFE_METHOD_ - switch (p_screen) { - case SCREEN_PRIMARY: { - p_screen = get_primary_screen(); - } break; - case SCREEN_OF_MAIN_WINDOW: { - p_screen = window_get_current_screen(MAIN_WINDOW_ID); - } break; - default: - break; - } - + p_screen = _get_screen_index(p_screen); EnumSizeData data = { 0, p_screen, Size2() }; EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcSize, (LPARAM)&data); return data.size; @@ -528,17 +520,7 @@ static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonit Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const { _THREAD_SAFE_METHOD_ - switch (p_screen) { - case SCREEN_PRIMARY: { - p_screen = get_primary_screen(); - } break; - case SCREEN_OF_MAIN_WINDOW: { - p_screen = window_get_current_screen(MAIN_WINDOW_ID); - } break; - default: - break; - } - + p_screen = _get_screen_index(p_screen); EnumRectData data = { 0, p_screen, Rect2i() }; EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcUsableSize, (LPARAM)&data); data.rect.position -= _get_screens_origin(); @@ -616,23 +598,36 @@ static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRE int DisplayServerWindows::screen_get_dpi(int p_screen) const { _THREAD_SAFE_METHOD_ - switch (p_screen) { - case SCREEN_PRIMARY: { - p_screen = get_primary_screen(); - } break; - case SCREEN_OF_MAIN_WINDOW: { - p_screen = window_get_current_screen(MAIN_WINDOW_ID); - } break; - default: - break; - } - + p_screen = _get_screen_index(p_screen); EnumDpiData data = { 0, p_screen, 72 }; EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data); return data.dpi; } -float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const { - _THREAD_SAFE_METHOD_ + +Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const { + Point2i pos = p_position + _get_screens_origin(); + + POINT p; + p.x = pos.x; + p.y = pos.y; + if (win81p_LogicalToPhysicalPointForPerMonitorDPI) { + win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p); + } + HDC dc = GetDC(0); + if (dc) { + COLORREF col = GetPixel(dc, p.x, p.y); + if (col != CLR_INVALID) { + ReleaseDC(NULL, dc); + return Color(float(col & 0x000000FF) / 256.0, float((col & 0x0000FF00) >> 8) / 256.0, float((col & 0x00FF0000) >> 16) / 256.0, 1.0); + } + ReleaseDC(NULL, dc); + } + + return Color(); +} + +Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const { + ERR_FAIL_INDEX_V(p_screen, get_screen_count(), Ref<Image>()); switch (p_screen) { case SCREEN_PRIMARY: { @@ -645,6 +640,65 @@ float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const { break; } + Point2i pos = screen_get_position(p_screen) + _get_screens_origin(); + Size2i size = screen_get_size(p_screen); + + POINT p1; + p1.x = pos.x; + p1.y = pos.y; + + POINT p2; + p2.x = pos.x + size.x; + p2.y = pos.y + size.y; + if (win81p_LogicalToPhysicalPointForPerMonitorDPI) { + win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p1); + win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p2); + } + + Ref<Image> img; + HDC dc = GetDC(0); + if (dc) { + HDC hdc = CreateCompatibleDC(dc); + int width = p2.x - p1.x; + int height = p2.y - p1.y; + if (hdc) { + HBITMAP hbm = CreateCompatibleBitmap(dc, width, height); + if (hbm) { + SelectObject(hdc, hbm); + BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY); + + BITMAPINFO bmp_info = {}; + bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader); + bmp_info.bmiHeader.biWidth = width; + bmp_info.bmiHeader.biHeight = -height; + bmp_info.bmiHeader.biPlanes = 1; + bmp_info.bmiHeader.biBitCount = 32; + bmp_info.bmiHeader.biCompression = BI_RGB; + + Vector<uint8_t> img_data; + img_data.resize(width * height * 4); + GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS); + + uint8_t *wr = (uint8_t *)img_data.ptrw(); + for (int i = 0; i < width * height; i++) { + SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); + } + img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data); + + DeleteObject(hbm); + } + DeleteDC(hdc); + } + ReleaseDC(NULL, dc); + } + + return img; +} + +float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const { + _THREAD_SAFE_METHOD_ + + p_screen = _get_screen_index(p_screen); EnumRefreshRateData data = { 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK }; EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data); return data.rate; @@ -781,9 +835,12 @@ void DisplayServerWindows::show_window(WindowID p_id) { SetFocus(wd.hWnd); // Set keyboard focus. } else if (wd.minimized) { ShowWindow(wd.hWnd, SW_SHOWMINIMIZED); - } else if (wd.no_focus || wd.is_popup) { + } else if (wd.no_focus) { // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow ShowWindow(wd.hWnd, SW_SHOWNA); + } else if (wd.is_popup) { + ShowWindow(wd.hWnd, SW_SHOWNA); + SetFocus(wd.hWnd); // Set keyboard focus. } else { ShowWindow(wd.hWnd, SW_SHOW); SetForegroundWindow(wd.hWnd); // Slightly higher priority. @@ -1271,7 +1328,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre r_style = WS_OVERLAPPEDWINDOW; } } else { - r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU; + r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; } } @@ -1327,6 +1384,8 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) wd.multiwindow_fs = false; wd.maximized = wd.was_maximized; + _update_window_style(p_window, false); + if (wd.pre_fs_valid) { rect = wd.pre_fs_rect; } else { @@ -1337,8 +1396,6 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) wd.pre_fs_valid = true; } - _update_window_style(p_window, false); - MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); if (restore_mouse_trails > 1) { @@ -1558,6 +1615,15 @@ void DisplayServerWindows::window_move_to_foreground(WindowID p_window) { } } +bool DisplayServerWindows::window_is_focused(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), false); + const WindowData &wd = windows[p_window]; + + return wd.window_focused; +} + bool DisplayServerWindows::window_can_draw(WindowID p_window) const { _THREAD_SAFE_METHOD_ @@ -1719,6 +1785,8 @@ DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const { void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { _THREAD_SAFE_METHOD_ + ERR_FAIL_INDEX(p_shape, CURSOR_MAX); + if (p_cursor.is_valid()) { RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape); @@ -1732,38 +1800,37 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor } Ref<Texture2D> texture = p_cursor; + ERR_FAIL_COND(!texture.is_valid()); Ref<AtlasTexture> atlas_texture = p_cursor; - Ref<Image> image; Size2 texture_size; Rect2 atlas_rect; - if (texture.is_valid()) { - image = texture->get_image(); - } - - if (!image.is_valid() && atlas_texture.is_valid()) { + if (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()) { + } else { 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_image(); + Ref<Image> image = texture->get_image(); ERR_FAIL_COND(!image.is_valid()); + if (image->is_compressed()) { + image = image->duplicate(true); + Error err = image->decompress(); + ERR_FAIL_COND_MSG(err != OK, "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock."); + } UINT image_size = texture_size.width * texture_size.height; @@ -1840,11 +1907,11 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor } cursors[p_shape] = nullptr; + cursors_cache.erase(p_shape); + CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; cursor_set_shape(c); - - cursors_cache.erase(p_shape); } } @@ -3402,16 +3469,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_SYSKEYUP: case WM_KEYUP: - if (windows[window_id].ime_suppress_next_keyup) { - windows[window_id].ime_suppress_next_keyup = false; - break; - } - [[fallthrough]]; case WM_SYSKEYDOWN: case WM_KEYDOWN: { - if (windows[window_id].ime_in_progress) { - break; - } if (wParam == VK_SHIFT) { shift_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); } @@ -3425,6 +3484,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } + if (windows[window_id].ime_suppress_next_keyup && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)) { + windows[window_id].ime_suppress_next_keyup = false; + break; + } + if (windows[window_id].ime_in_progress) { + break; + } + 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)) { @@ -3668,10 +3735,18 @@ void DisplayServerWindows::_process_key_events() { } k->set_window_id(ke.window_id); - k->set_shift_pressed(ke.shift); - k->set_alt_pressed(ke.alt); - k->set_ctrl_pressed(ke.control); - k->set_meta_pressed(ke.meta); + if (keycode != Key::SHIFT) { + k->set_shift_pressed(ke.shift); + } + if (keycode != Key::ALT) { + k->set_alt_pressed(ke.alt); + } + if (keycode != Key::CTRL) { + k->set_ctrl_pressed(ke.control); + } + if (keycode != Key::META) { + k->set_meta_pressed(ke.meta); + } k->set_pressed(true); k->set_keycode(keycode); k->set_physical_keycode(physical_keycode); @@ -3693,11 +3768,6 @@ void DisplayServerWindows::_process_key_events() { k.instantiate(); k->set_window_id(ke.window_id); - k->set_shift_pressed(ke.shift); - k->set_alt_pressed(ke.alt); - k->set_ctrl_pressed(ke.control); - k->set_meta_pressed(ke.meta); - k->set_pressed(ke.uMsg == WM_KEYDOWN); Key keycode = KeyMappingWindows::get_keysym(ke.wParam); @@ -3719,6 +3789,18 @@ void DisplayServerWindows::_process_key_events() { } } + if (keycode != Key::SHIFT) { + k->set_shift_pressed(ke.shift); + } + if (keycode != Key::ALT) { + k->set_alt_pressed(ke.alt); + } + if (keycode != Key::CTRL) { + k->set_ctrl_pressed(ke.control); + } + if (keycode != Key::META) { + k->set_meta_pressed(ke.meta); + } k->set_keycode(keycode); k->set_physical_keycode(physical_keycode); k->set_key_label(key_label); @@ -4003,6 +4085,7 @@ GetImmersiveUserColorSetPreferencePtr DisplayServerWindows::GetImmersiveUserColo bool DisplayServerWindows::winink_available = false; GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr; GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr; +LogicalToPhysicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_LogicalToPhysicalPointForPerMonitorDPI = nullptr; typedef enum _SHC_PROCESS_DPI_AWARENESS { SHC_PROCESS_DPI_UNAWARE = 0, @@ -4081,7 +4164,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win rendering_driver = p_rendering_driver; // Init TTS - tts = memnew(TTS_Windows); + bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech"); + if (tts_enabled) { + tts = memnew(TTS_Windows); + } // Enforce default keep screen on value. screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); @@ -4131,10 +4217,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } // Note: Windows Ink API for pen input, available on Windows 8+ only. + // Note: DPI conversion API, available on Windows 8.1+ only. HMODULE user32_lib = LoadLibraryW(L"user32.dll"); if (user32_lib) { win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo"); + win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI"); winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo; } @@ -4220,7 +4308,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win window_position = screen_get_position(p_screen) + (screen_get_size(p_screen) - p_resolution) / 2; } - WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution)); ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd); @@ -4292,7 +4380,7 @@ DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_drive vformat("Your video card drivers seem not to support the required Vulkan version.\n\n" "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n" "You can enable the OpenGL 3 driver by starting the engine from the\n" - "command line with the command:\n'%s --rendering-driver opengl3'\n\n" + "command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n" "If you have recently updated your video card drivers, try rebooting.", executable_name), "Unable to initialize Vulkan video driver"); @@ -4371,5 +4459,4 @@ DisplayServerWindows::~DisplayServerWindows() { if (tts) { memdelete(tts); } - CoUninitialize(); } |