summaryrefslogtreecommitdiff
path: root/platform/windows/display_server_windows.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/windows/display_server_windows.cpp')
-rw-r--r--platform/windows/display_server_windows.cpp746
1 files changed, 526 insertions, 220 deletions
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index d243d4c05d..9d0a2578fb 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -37,6 +37,11 @@
#include "scene/resources/texture.h"
#include <avrt.h>
+#include <dwmapi.h>
+
+#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
+#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
+#endif
#if defined(GLES3_ENABLED)
#include "drivers/gles3/rasterizer_gles3.h"
@@ -84,6 +89,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
case FEATURE_KEEP_SCREEN_ON:
+ case FEATURE_TEXT_TO_SPEECH:
return true;
default:
return false;
@@ -98,7 +104,10 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
// Mouse is grabbed (captured or confined).
- WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
WindowData &wd = windows[window_id];
@@ -113,11 +122,15 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
ClientToScreen(wd.hWnd, &pos);
SetCursorPos(pos.x, pos.y);
SetCapture(wd.hWnd);
+
+ _register_raw_input_devices(window_id);
}
} else {
// Mouse is free to move around (not captured or confined).
ReleaseCapture();
ClipCursor(nullptr);
+
+ _register_raw_input_devices(INVALID_WINDOW_ID);
}
if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
@@ -133,6 +146,72 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
}
}
+DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() const {
+ const List<WindowID>::Element *E = popup_list.back();
+ if (E) {
+ return E->get();
+ }
+
+ return last_focused_window;
+}
+
+void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) {
+ use_raw_input = true;
+
+ RAWINPUTDEVICE rid[1] = {};
+ rid[0].usUsagePage = 0x01;
+ rid[0].usUsage = 0x02;
+ rid[0].dwFlags = 0;
+
+ if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) {
+ // Follow the defined window
+ rid[0].hwndTarget = windows[p_target_window].hWnd;
+ } else {
+ // Follow the keyboard focus
+ rid[0].hwndTarget = 0;
+ }
+
+ if (RegisterRawInputDevices(rid, 1, sizeof(rid[0])) == FALSE) {
+ // Registration failed.
+ use_raw_input = false;
+ }
+}
+
+bool DisplayServerWindows::tts_is_speaking() const {
+ ERR_FAIL_COND_V(!tts, false);
+ return tts->is_speaking();
+}
+
+bool DisplayServerWindows::tts_is_paused() const {
+ ERR_FAIL_COND_V(!tts, false);
+ return tts->is_paused();
+}
+
+TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const {
+ ERR_FAIL_COND_V(!tts, TypedArray<Dictionary>());
+ return tts->get_voices();
+}
+
+void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ ERR_FAIL_COND(!tts);
+ tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
+}
+
+void DisplayServerWindows::tts_pause() {
+ ERR_FAIL_COND(!tts);
+ tts->pause();
+}
+
+void DisplayServerWindows::tts_resume() {
+ ERR_FAIL_COND(!tts);
+ tts->resume();
+}
+
+void DisplayServerWindows::tts_stop() {
+ ERR_FAIL_COND(!tts);
+ tts->stop();
+}
+
void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
_THREAD_SAFE_METHOD_
@@ -150,21 +229,23 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {
return mouse_mode;
}
-void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) {
+void DisplayServerWindows::warp_mouse(const Point2i &p_position) {
_THREAD_SAFE_METHOD_
- if (!windows.has(last_focused_window)) {
+ WindowID window_id = _get_focused_window_or_popup();
+
+ if (!windows.has(window_id)) {
return; // No focused window?
}
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- old_x = p_to.x;
- old_y = p_to.y;
+ old_x = p_position.x;
+ old_y = p_position.y;
} else {
POINT p;
- p.x = p_to.x;
- p.y = p_to.y;
- ClientToScreen(windows[last_focused_window].hWnd, &p);
+ p.x = p_position.x;
+ p.y = p_position.y;
+ ClientToScreen(windows[window_id].hWnd, &p);
SetCursorPos(p.x, p.y);
}
@@ -430,9 +511,8 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau
}
UINT x = 0, y = 0;
- HRESULT hr = E_FAIL;
if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) {
- hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
+ HRESULT hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
dpiX = (int)x;
dpiY = (int)y;
@@ -481,25 +561,44 @@ float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {
return data.rate;
}
-bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const {
-#ifndef _MSC_VER
-#warning touchscreen not working
-#endif
- return false;
-}
+void DisplayServerWindows::screen_set_keep_on(bool p_enable) {
+ if (keep_screen_on == p_enable) {
+ return;
+ }
-void DisplayServerWindows::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) {
-}
+ if (p_enable) {
+ const String reason = "Godot Engine running with display/window/energy_saving/keep_screen_on = true";
+ Char16String reason_utf16 = reason.utf16();
-DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(int p_screen) const {
- return SCREEN_LANDSCAPE;
-}
+ REASON_CONTEXT context;
+ context.Version = POWER_REQUEST_CONTEXT_VERSION;
+ context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
+ context.Reason.SimpleReasonString = (LPWSTR)(reason_utf16.ptrw());
+ power_request = PowerCreateRequest(&context);
+ if (power_request == INVALID_HANDLE_VALUE) {
+ print_error("Failed to enable screen_keep_on.");
+ return;
+ }
+ if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired) == 0) {
+ print_error("Failed to request system sleep override.");
+ return;
+ }
+ if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired) == 0) {
+ print_error("Failed to request display timeout override.");
+ return;
+ }
+ } else {
+ PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired);
+ PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired);
+ CloseHandle(power_request);
+ power_request = nullptr;
+ }
-void DisplayServerWindows::screen_set_keep_on(bool p_enable) {
+ keep_screen_on = p_enable;
}
bool DisplayServerWindows::screen_is_kept_on() const {
- return false;
+ return keep_screen_on;
}
Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {
@@ -572,8 +671,17 @@ void DisplayServerWindows::show_window(WindowID p_id) {
_update_window_style(p_id);
}
- ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.
- if (!wd.no_focus && !wd.is_popup) {
+ if (wd.maximized) {
+ ShowWindow(wd.hWnd, SW_SHOWMAXIMIZED);
+ SetForegroundWindow(wd.hWnd); // Slightly higher priority.
+ SetFocus(wd.hWnd); // Set keyboard focus.
+ } else if (wd.minimized) {
+ ShowWindow(wd.hWnd, SW_SHOWMINIMIZED);
+ } else if (wd.no_focus || wd.is_popup) {
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
+ ShowWindow(wd.hWnd, SW_SHOWNA);
+ } else {
+ ShowWindow(wd.hWnd, SW_SHOW);
SetForegroundWindow(wd.hWnd); // Slightly higher priority.
SetFocus(wd.hWnd); // Set keyboard focus.
}
@@ -590,7 +698,7 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
WindowData &wd = windows[p_window];
while (wd.transient_children.size()) {
- window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
+ window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);
}
if (wd.transient_parent != INVALID_WINDOW_ID) {
@@ -618,7 +726,9 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) {
#if defined(GLES3_ENABLED)
- gl_manager->window_make_current(p_window_id);
+ if (gl_manager) {
+ gl_manager->window_make_current(p_window_id);
+ }
#endif
}
@@ -808,7 +918,7 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- if (wd.fullscreen) {
+ if (wd.fullscreen || wd.maximized) {
return;
}
@@ -844,8 +954,8 @@ void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclus
if (wd.exclusive != p_exclusive) {
wd.exclusive = p_exclusive;
if (wd.transient_parent != INVALID_WINDOW_ID) {
- WindowData &wd_parent = windows[wd.transient_parent];
if (wd.exclusive) {
+ WindowData &wd_parent = windows[wd.transient_parent];
SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);
} else {
SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);
@@ -941,6 +1051,10 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
+ if (wd.fullscreen || wd.maximized) {
+ return;
+ }
+
int w = p_size.width;
int h = p_size.height;
@@ -958,10 +1072,6 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
}
#endif
- if (wd.fullscreen) {
- return;
- }
-
RECT rect;
GetWindowRect(wd.hWnd, &rect);
@@ -1071,6 +1181,10 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
+ if (icon.is_valid()) {
+ set_icon(icon);
+ }
+
SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));
if (p_repaint) {
@@ -1230,7 +1344,30 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
_update_window_style(p_window);
} break;
case WINDOW_FLAG_TRANSPARENT: {
- // FIXME: Implement.
+ if (p_enabled) {
+ //enable per-pixel alpha
+
+ DWM_BLURBEHIND bb;
+ ZeroMemory(&bb, sizeof(bb));
+ HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
+ bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
+ bb.hRgnBlur = hRgn;
+ bb.fEnable = TRUE;
+ DwmEnableBlurBehindWindow(wd.hWnd, &bb);
+
+ wd.layered_window = true;
+ } else {
+ //disable per-pixel alpha
+ wd.layered_window = false;
+
+ DWM_BLURBEHIND bb;
+ ZeroMemory(&bb, sizeof(bb));
+ HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
+ bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
+ bb.hRgnBlur = hRgn;
+ bb.fEnable = FALSE;
+ DwmEnableBlurBehindWindow(wd.hWnd, &bb);
+ }
} break;
case WINDOW_FLAG_NO_FOCUS: {
wd.no_focus = p_enabled;
@@ -1241,7 +1378,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
wd.is_popup = p_enabled;
} break;
- case WINDOW_FLAG_MAX:
+ default:
break;
}
}
@@ -1262,7 +1399,7 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window
return wd.always_on_top;
} break;
case WINDOW_FLAG_TRANSPARENT: {
- // FIXME: Implement.
+ return wd.layered_window;
} break;
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
@@ -1270,7 +1407,7 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
- case WINDOW_FLAG_MAX:
+ default:
break;
}
@@ -1281,7 +1418,7 @@ void DisplayServerWindows::window_request_attention(WindowID p_window) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- WindowData &wd = windows[p_window];
+ const WindowData &wd = windows[p_window];
FLASHWINFO info;
info.cbSize = sizeof(FLASHWINFO);
@@ -1393,7 +1530,7 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
IDC_HELP
};
- if (cursors[p_shape] != nullptr) {
+ if (cursors_cache.has(p_shape)) {
SetCursor(cursors[p_shape]);
} else {
SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
@@ -1406,60 +1543,11 @@ DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {
return cursor_shape;
}
-void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) {
- // Get the system display DC.
- HDC hDC = GetDC(nullptr);
-
- // 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(nullptr, 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 the 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 the 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) {
+void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
_THREAD_SAFE_METHOD_
if (p_cursor.is_valid()) {
- Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
+ RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
if (cursor_c) {
if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
@@ -1507,8 +1595,26 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh
UINT image_size = texture_size.width * texture_size.height;
// Create the BITMAP with alpha channel.
- COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
-
+ COLORREF *buffer = nullptr;
+
+ BITMAPV5HEADER bi;
+ ZeroMemory(&bi, sizeof(bi));
+ bi.bV5Size = sizeof(bi);
+ bi.bV5Width = texture_size.width;
+ bi.bV5Height = -texture_size.height;
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = 32;
+ bi.bV5Compression = BI_BITFIELDS;
+ bi.bV5RedMask = 0x00ff0000;
+ bi.bV5GreenMask = 0x0000ff00;
+ bi.bV5BlueMask = 0x000000ff;
+ bi.bV5AlphaMask = 0xff000000;
+
+ HDC dc = GetDC(nullptr);
+ HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);
+ HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr);
+
+ bool fully_transparent = true;
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;
@@ -1517,39 +1623,28 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh
column_index = MIN(column_index, atlas_rect.size.width - 1);
row_index = MIN(row_index, atlas_rect.size.height - 1);
}
+ const Color &c = image->get_pixel(column_index, row_index);
+ fully_transparent = fully_transparent && (c.a == 0.f);
- *(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 = nullptr;
- HBITMAP hXorMask = nullptr;
-
- GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask);
-
- if (nullptr == hAndMask || nullptr == hXorMask) {
- memfree(buffer);
- DeleteObject(bitmap);
- return;
+ *(buffer + index) = c.to_argb32();
}
// 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);
+ if (fully_transparent) {
+ cursors[p_shape] = nullptr;
+ } else {
+ ICONINFO iconinfo;
+ iconinfo.fIcon = FALSE;
+ iconinfo.xHotspot = p_hotspot.x;
+ iconinfo.yHotspot = p_hotspot.y;
+ iconinfo.hbmMask = mask;
+ iconinfo.hbmColor = bitmap;
+ cursors[p_shape] = CreateIconIndirect(&iconinfo);
+ }
Vector<Variant> params;
params.push_back(p_cursor);
@@ -1562,22 +1657,15 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh
}
}
- if (hAndMask != nullptr) {
- DeleteObject(hAndMask);
- }
-
- if (hXorMask != nullptr) {
- DeleteObject(hXorMask);
- }
-
- memfree(buffer);
+ DeleteObject(mask);
DeleteObject(bitmap);
+ ReleaseDC(nullptr, dc);
} else {
// Reset to default system cursor.
if (cursors[p_shape]) {
DestroyIcon(cursors[p_shape]);
- cursors[p_shape] = nullptr;
}
+ cursors[p_shape] = nullptr;
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;
@@ -1764,15 +1852,17 @@ void DisplayServerWindows::make_rendering_thread() {
void DisplayServerWindows::swap_buffers() {
#if defined(GLES3_ENABLED)
- gl_manager->swap_buffers();
+ if (gl_manager) {
+ gl_manager->swap_buffers();
+ }
#endif
}
void DisplayServerWindows::set_native_icon(const String &p_filename) {
_THREAD_SAFE_METHOD_
- FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
- ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'.");
+ Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'.");
ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));
int pos = 0;
@@ -1858,7 +1948,6 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
err = GetLastError();
ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
- memdelete(f);
memdelete(icon_dir);
}
@@ -1866,9 +1955,11 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!p_icon.is_valid());
- Ref<Image> icon = p_icon->duplicate();
- if (icon->get_format() != Image::FORMAT_RGBA8) {
- icon->convert(Image::FORMAT_RGBA8);
+ if (icon != p_icon) {
+ icon = p_icon->duplicate();
+ if (icon->get_format() != Image::FORMAT_RGBA8) {
+ icon->convert(Image::FORMAT_RGBA8);
+ }
}
int w = icon->get_width();
int h = icon->get_height();
@@ -1917,14 +2008,18 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
_THREAD_SAFE_METHOD_
#if defined(VULKAN_ENABLED)
- context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+ if (context_vulkan) {
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+ }
#endif
}
DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(VULKAN_ENABLED)
- return context_vulkan->get_vsync_mode(p_window);
+ if (context_vulkan) {
+ return context_vulkan->get_vsync_mode(p_window);
+ }
#endif
return DisplayServer::VSYNC_ENABLED;
}
@@ -1962,7 +2057,7 @@ void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float
}
void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) {
- Map<int, Vector2>::Element *curr = touch_state.find(idx);
+ RBMap<int, Vector2>::Element *curr = touch_state.find(idx);
if (!curr) {
return;
}
@@ -1989,12 +2084,12 @@ void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent
Variant *eventp = &event;
Variant ret;
Callable::CallError ce;
- wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
+ wd.event_callback.callp((const Variant **)&eventp, 1, ret, ce);
}
}
void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {
- ((DisplayServerWindows *)(get_singleton()))->_dispatch_input_event(p_event);
+ static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event);
}
void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {
@@ -2010,13 +2105,13 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
Callable::CallError ce;
{
- List<WindowID>::Element *E = popup_list.front();
+ List<WindowID>::Element *E = popup_list.back();
if (E && Object::cast_to<InputEventKey>(*p_event)) {
// Redirect keyboard input to active popup.
if (windows.has(E->get())) {
Callable callable = windows[E->get()].input_event_callback;
if (callable.is_valid()) {
- callable.call((const Variant **)&evp, 1, ret, ce);
+ callable.callp((const Variant **)&evp, 1, ret, ce);
}
}
in_dispatch_input_event = false;
@@ -2030,7 +2125,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
if (windows.has(event_from_window->get_window_id())) {
Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
if (callable.is_valid()) {
- callable.call((const Variant **)&evp, 1, ret, ce);
+ callable.callp((const Variant **)&evp, 1, ret, ce);
}
}
} else {
@@ -2038,7 +2133,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
for (const KeyValue<WindowID, WindowData> &E : windows) {
const Callable callable = E.value.input_event_callback;
if (callable.is_valid()) {
- callable.call((const Variant **)&evp, 1, ret, ce);
+ callable.callp((const Variant **)&evp, 1, ret, ce);
}
}
}
@@ -2081,20 +2176,24 @@ Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const
}
void DisplayServerWindows::popup_open(WindowID p_window) {
- WindowData &wd = windows[p_window];
+ _THREAD_SAFE_METHOD_
+
+ const WindowData &wd = windows[p_window];
if (wd.is_popup) {
- // Close all popups, up to current popup parent, or every popup if new window is not transient.
+ // Find current popup parent, or root popup if new window is not transient.
+ List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
while (E) {
if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {
- _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->prev();
- popup_list.erase(E);
- E = F;
+ C = E;
+ E = E->prev();
} else {
break;
}
}
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ }
time_since_popup = OS::get_singleton()->get_ticks_msec();
popup_list.push_back(p_window);
@@ -2102,17 +2201,25 @@ void DisplayServerWindows::popup_open(WindowID p_window) {
}
void DisplayServerWindows::popup_close(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
List<WindowID>::Element *E = popup_list.find(p_window);
while (E) {
- _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
List<WindowID>::Element *F = E->next();
+ WindowID win_id = E->get();
popup_list.erase(E);
+
+ if (win_id != p_window) {
+ // Only request close on related windows, not this window. We are already processing it.
+ _send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ }
E = F;
}
}
LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) {
_THREAD_SAFE_METHOD_
+
uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;
if (delta > 250) {
switch (wParam) {
@@ -2120,10 +2227,13 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)
case WM_NCRBUTTONDOWN:
case WM_NCMBUTTONDOWN:
case WM_LBUTTONDOWN:
+ case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN: {
MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam;
Point2i pos = Point2i(ms->pt.x, ms->pt.y);
+ List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
+ // Find top popup to close.
while (E) {
// Popup window area.
Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
@@ -2134,21 +2244,53 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)
} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {
break;
} else {
- _send_window_event(windows[E->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
- List<WindowID>::Element *F = E->prev();
- popup_list.erase(E);
- E = F;
+ C = E;
+ E = E->prev();
}
}
-
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ return 1;
+ }
} break;
}
}
return ::CallNextHookEx(mouse_monitor, code, wParam, lParam);
}
-// Our default window procedure to handle processing of window-related system messages/events.
-// Also known as DefProc or DefWindowProc.
+// Handle a single window message received while CreateWindowEx is still on the stack and our data
+// structures are not fully initialized.
+LRESULT DisplayServerWindows::_handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ switch (uMsg) {
+ case WM_GETMINMAXINFO: {
+ // We receive this during CreateWindowEx and we haven't initialized the window
+ // struct, so let Windows figure out the maximized size.
+ // Silently forward to user/default.
+ } break;
+ case WM_NCCREATE: {
+ // We tunnel an unowned pointer to our window context (WindowData) through the
+ // first possible message (WM_NCCREATE) to fix up our window context collection.
+ CREATESTRUCTW *pCreate = (CREATESTRUCTW *)lParam;
+ WindowData *pWindowData = reinterpret_cast<WindowData *>(pCreate->lpCreateParams);
+
+ // Fix this up so we can recognize the remaining messages.
+ pWindowData->hWnd = hWnd;
+ } break;
+ default: {
+ // Additional messages during window creation should happen after we fixed
+ // up the data structures on WM_NCCREATE, but this might change in the future,
+ // so report an error here and then we can implement them.
+ ERR_PRINT_ONCE(vformat("Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error.", uMsg));
+ } break;
+ }
+
+ if (user_proc) {
+ return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
+ }
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+
+// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events.
// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures
LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (drop_events) {
@@ -2162,7 +2304,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
WindowID window_id = INVALID_WINDOW_ID;
bool window_created = false;
- // Check whether window exists.
+ // Check whether window exists
+ // FIXME this is O(n), where n is the set of currently open windows and subwindows
+ // we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below
for (const KeyValue<WindowID, WindowData> &E : windows) {
if (E.value.hWnd == hWnd) {
window_id = E.key;
@@ -2171,10 +2315,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
- // Window doesn't exist or creation in progress, don't handle messages yet.
+ // WARNING: we get called with events before the window is registered in our collection
+ // specifically, even the call to CreateWindowEx already calls here while still on the stack,
+ // so there is no way to store the window handle in our collection before we get here
if (!window_created) {
- window_id = window_id_counter;
- ERR_FAIL_COND_V(!windows.has(window_id), 0);
+ // don't let code below operate on incompletely initialized window objects or missing window_id
+ return _handle_early_window_message(hWnd, uMsg, wParam, lParam);
}
// Process window messages.
@@ -2261,6 +2407,20 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_PAINT: {
Main::force_redraw();
} break;
+ case WM_SETTINGCHANGE: {
+ if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {
+ if (is_dark_mode_supported() && dark_title_available) {
+ BOOL value = is_dark_mode();
+ ::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
+ }
+ }
+ } break;
+ case WM_THEMECHANGED: {
+ if (is_dark_mode_supported() && dark_title_available) {
+ BOOL value = is_dark_mode();
+ ::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
+ }
+ } break;
case WM_SYSCOMMAND: // Intercept system commands.
{
switch (wParam) // Check system calls.
@@ -2284,10 +2444,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
return 0; // Jump back.
}
case WM_MOUSELEAVE: {
- old_invalid = true;
- outside = true;
+ if (window_mouseover_id == window_id) {
+ old_invalid = true;
+ window_mouseover_id = INVALID_WINDOW_ID;
- _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
} break;
case WM_INPUT: {
@@ -2357,7 +2519,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
old_y = coords.y;
}
- if (windows[window_id].window_has_focus && mm->get_relative() != Vector2()) {
+ if ((windows[window_id].window_has_focus || windows[window_id].is_popup) && mm->get_relative() != Vector2()) {
Input::get_singleton()->parse_input_event(mm);
}
}
@@ -2382,22 +2544,27 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
PACKET packet;
if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) {
- float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);
- windows[window_id].last_pressure = pressure;
+ POINT coords;
+ GetCursorPos(&coords);
+ ScreenToClient(windows[window_id].hWnd, &coords);
+
windows[window_id].last_pressure_update = 0;
+ float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);
double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180);
double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180));
+ bool inverted = packet.pkStatus & TPS_INVERT;
- if (windows[window_id].tilt_supported) {
- windows[window_id].last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt));
- } else {
- windows[window_id].last_tilt = Vector2();
+ Vector2 tilt = (windows[window_id].tilt_supported) ? Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)) : Vector2();
+
+ // Nothing changed, ignore event.
+ if (!old_invalid && coords.x == old_x && coords.y == old_y && windows[window_id].last_pressure == pressure && windows[window_id].last_tilt == tilt && windows[window_id].last_pen_inverted == inverted) {
+ break;
}
- POINT coords;
- GetCursorPos(&coords);
- ScreenToClient(windows[window_id].hWnd, &coords);
+ windows[window_id].last_pressure = pressure;
+ windows[window_id].last_tilt = tilt;
+ windows[window_id].last_pen_inverted = inverted;
// 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) {
@@ -2413,6 +2580,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_pressure(windows[window_id].last_pressure);
mm->set_tilt(windows[window_id].last_tilt);
+ mm->set_pen_inverted(windows[window_id].last_pen_inverted);
mm->set_button_mask(last_button_state);
@@ -2512,17 +2680,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
- if (outside) {
+ if (window_mouseover_id != window_id) {
// Mouse enter.
if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ // Leave previous window.
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;
cursor_set_shape(c);
- outside = false;
+ window_mouseover_id = window_id;
// Once-off notification, must call again.
track_mouse_leave_event(hWnd);
@@ -2545,6 +2717,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) {
mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90));
}
+ mm->set_pen_inverted(pen_info.penFlags & (PEN_FLAG_INVERTED | PEN_FLAG_ERASER));
mm->set_ctrl_pressed(GetKeyState(VK_CONTROL) < 0);
mm->set_shift_pressed(GetKeyState(VK_SHIFT) < 0);
@@ -2612,17 +2785,29 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
- if (outside) {
+ DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position());
+ if (!Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) {
+ // Don't consider the windowborder as part of the window.
+ over_id = INVALID_WINDOW_ID;
+ }
+ if (window_mouseover_id != over_id) {
// Mouse enter.
if (mouse_mode != MOUSE_MODE_CAPTURED) {
- _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ // Leave previous window.
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+
+ if (over_id != INVALID_WINDOW_ID) {
+ _send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER);
+ }
}
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;
cursor_set_shape(c);
- outside = false;
+ window_mouseover_id = over_id;
// Once-off notification, must call again.
track_mouse_leave_event(hWnd);
@@ -2633,9 +2818,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
break;
}
+ DisplayServer::WindowID receiving_window_id = _get_focused_window_or_popup();
+ if (receiving_window_id == INVALID_WINDOW_ID) {
+ receiving_window_id = window_id;
+ }
Ref<InputEventMouseMotion> mm;
mm.instantiate();
- mm->set_window_id(window_id);
+ mm->set_window_id(receiving_window_id);
mm->set_ctrl_pressed((wParam & MK_CONTROL) != 0);
mm->set_shift_pressed((wParam & MK_SHIFT) != 0);
mm->set_alt_pressed(alt_mem);
@@ -2647,14 +2836,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} else {
windows[window_id].last_tilt = Vector2();
windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;
+ windows[window_id].last_pen_inverted = false;
}
} else {
windows[window_id].last_tilt = Vector2();
windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;
+ windows[window_id].last_pen_inverted = false;
}
mm->set_pressure(windows[window_id].last_pressure);
mm->set_tilt(windows[window_id].last_tilt);
+ mm->set_pen_inverted(windows[window_id].last_pen_inverted);
mm->set_button_mask(last_button_state);
@@ -2689,9 +2881,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
old_x = mm->get_position().x;
old_y = mm->get_position().y;
- if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) {
- Input::get_singleton()->parse_input_event(mm);
+
+ if (!windows[receiving_window_id].window_has_focus) {
+ // In case of unfocused Popups, adjust event position.
+ Point2i pos = mm->get_position() - window_get_position(receiving_window_id) + window_get_position(window_id);
+ mm->set_position(pos);
+ mm->set_global_position(pos);
}
+ Input::get_singleton()->parse_input_event(mm);
} break;
case WM_LBUTTONDOWN:
@@ -2849,7 +3046,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
} else {
- // For reasons unknown to mankind, wheel comes in screen coordinates.
+ // For reasons unknown to humanity, wheel comes in screen coordinates.
POINT coords;
coords.x = mb->get_position().x;
coords.y = mb->get_position().y;
@@ -2917,7 +3114,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
rect_changed = true;
}
#if defined(VULKAN_ENABLED)
- if (context_vulkan && window_created) {
+ if (context_vulkan && window.context_created) {
// Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.
context_vulkan->window_resize(window_id, window.width, window.height);
}
@@ -2935,7 +3132,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
const Variant *args[] = { &size };
Variant ret;
Callable::CallError ce;
- window.rect_changed_callback.call(args, 1, ret, ce);
+ window.rect_changed_callback.callp(args, 1, ret, ce);
}
}
@@ -3095,7 +3292,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
Variant *vp = &v;
Variant ret;
Callable::CallError ce;
- windows[window_id].drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ windows[window_id].drop_files_callback.callp((const Variant **)&vp, 1, ret, ce);
}
} break;
default: {
@@ -3173,7 +3370,7 @@ void DisplayServerWindows::_process_key_events() {
k->set_ctrl_pressed(ke.control);
k->set_meta_pressed(ke.meta);
k->set_pressed(true);
- k->set_keycode((Key)KeyMappingWindows::get_keysym(ke.wParam));
+ k->set_keycode((Key)KeyMappingWindows::get_keysym(MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK)));
k->set_physical_keycode((Key)(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24))));
k->set_unicode(unicode);
if (k->get_unicode() && gr_mem) {
@@ -3265,8 +3462,8 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const
if ((p_new_driver == "wintab") && wintab_available) {
wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);
wd.wtlc.lcOptions |= CXO_MESSAGES;
- wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;
- wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
+ wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;
+ wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
wd.wtlc.lcPktMode = 0;
wd.wtlc.lcOutOrgX = 0;
wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;
@@ -3342,31 +3539,53 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
WindowRect.top,
WindowRect.right - WindowRect.left,
WindowRect.bottom - WindowRect.top,
- nullptr, nullptr, hInstance, nullptr);
+ nullptr,
+ nullptr,
+ hInstance,
+ // tunnel the WindowData we need to handle creation message
+ // lifetime is ensured because we are still on the stack when this is
+ // processed in the window proc
+ reinterpret_cast<void *>(&wd));
if (!wd.hWnd) {
MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
windows.erase(id);
- return INVALID_WINDOW_ID;
+ ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window.");
+ }
+ if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
+ wd.fullscreen = true;
+ if (p_mode == WINDOW_MODE_FULLSCREEN) {
+ wd.multiwindow_fs = true;
+ }
}
if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
wd.pre_fs_valid = true;
}
+ if (is_dark_mode_supported() && dark_title_available) {
+ BOOL value = is_dark_mode();
+ ::DwmSetWindowAttribute(wd.hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
+ }
+
#ifdef VULKAN_ENABLED
if (context_vulkan) {
- if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) {
+ if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) {
memdelete(context_vulkan);
context_vulkan = nullptr;
windows.erase(id);
ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Vulkan Window.");
}
+ wd.context_created = true;
}
#endif
#ifdef GLES3_ENABLED
if (gl_manager) {
- Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top);
- ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Failed to create an OpenGL window.");
+ if (gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) {
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ windows.erase(id);
+ ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");
+ }
}
#endif
@@ -3376,8 +3595,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
if ((tablet_get_current_driver() == "wintab") && wintab_available) {
wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);
wd.wtlc.lcOptions |= CXO_MESSAGES;
- wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;
- wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
+ wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;
+ wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
wd.wtlc.lcPktMode = 0;
wd.wtlc.lcOutOrgX = 0;
wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;
@@ -3402,6 +3621,16 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
wd.wtctx = 0;
}
+ if (p_mode == WINDOW_MODE_MAXIMIZED) {
+ wd.maximized = true;
+ wd.minimized = false;
+ }
+
+ if (p_mode == WINDOW_MODE_MINIMIZED) {
+ wd.maximized = false;
+ wd.minimized = true;
+ }
+
wd.last_pressure = 0;
wd.last_pressure_update = 0;
wd.last_tilt = Vector2();
@@ -3411,6 +3640,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
ImmReleaseContext(wd.hWnd, wd.im_himc);
wd.im_position = Vector2();
+
+ // FIXME this is wrong in cases where the window coordinates were changed due to full screen mode; use WindowRect
wd.last_pos = p_rect.position;
wd.width = p_rect.size.width;
wd.height = p_rect.size.height;
@@ -3429,6 +3660,15 @@ WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr;
WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;
WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
+// UXTheme API.
+bool DisplayServerWindows::dark_title_available = false;
+bool DisplayServerWindows::ux_theme_available = false;
+IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr;
+ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;
+GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;
+GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr;
+GetImmersiveUserColorSetPreferencePtr DisplayServerWindows::GetImmersiveUserColorSetPreference = nullptr;
+
// Windows Ink API.
bool DisplayServerWindows::winink_available = false;
GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
@@ -3440,6 +3680,23 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
} SHC_PROCESS_DPI_AWARENESS;
+bool DisplayServerWindows::is_dark_mode_supported() const {
+ return ux_theme_available && IsDarkModeAllowedForApp();
+}
+
+bool DisplayServerWindows::is_dark_mode() const {
+ return ux_theme_available && ShouldAppsUseDarkMode();
+}
+
+Color DisplayServerWindows::get_accent_color() const {
+ if (!ux_theme_available) {
+ return Color(0, 0, 0, 0);
+ }
+
+ int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"), false, 0);
+ return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);
+}
+
int DisplayServerWindows::tablet_get_driver_count() const {
return tablet_drivers.size();
}
@@ -3474,7 +3731,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {
}
}
-DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
drop_events = false;
key_event_pos = 0;
@@ -3483,16 +3740,49 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
shift_mem = false;
control_mem = false;
meta_mem = false;
- hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance();
+ hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance();
pressrc = 0;
old_invalid = true;
mouse_mode = MOUSE_MODE_VISIBLE;
- outside = true;
-
rendering_driver = p_rendering_driver;
+ // Init TTS
+ tts = memnew(TTS_Windows);
+
+ // Enforce default keep screen on value.
+ screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
+
+ // Load Windows version info.
+ OSVERSIONINFOW os_ver;
+ ZeroMemory(&os_ver, sizeof(OSVERSIONINFOW));
+ os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+
+ HMODULE nt_lib = LoadLibraryW(L"ntdll.dll");
+ if (nt_lib) {
+ RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(nt_lib, "RtlGetVersion");
+ if (RtlGetVersion) {
+ RtlGetVersion(&os_ver);
+ }
+ FreeLibrary(nt_lib);
+ }
+
+ // Load UXTheme.
+ HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");
+ if (ux_theme_lib) {
+ IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));
+ ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132));
+ GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));
+ GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));
+ GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
+
+ ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
+ if (os_ver.dwBuildNumber >= 22000) {
+ dark_title_available = true;
+ }
+ }
+
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
if (wintab_lib) {
@@ -3555,19 +3845,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
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;
- }
+ _register_raw_input_devices(INVALID_WINDOW_ID);
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
@@ -3605,6 +3883,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
+ if (p_position != nullptr) {
+ window_position = *p_position;
+ }
+
WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution));
ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window.");
@@ -3626,7 +3908,11 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
#endif
- if (!OS::get_singleton()->is_in_low_processor_usage_mode()) {
+ if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) {
+ // Increase priority for projects that are not in low-processor mode (typically games)
+ // to reduce the risk of frame stuttering.
+ // This is not done for the editor to prevent importers or resource bakers
+ // from making the system unresponsive.
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
DWORD index = 0;
HANDLE handle = AvSetMmThreadCharacteristics("Games", &index);
@@ -3647,7 +3933,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
r_error = OK;
- ((OS_Windows *)OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
+ static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
}
@@ -3664,12 +3950,24 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error));
if (r_error != OK) {
- OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.\n"
- "Please update your drivers or if you have a very old or integrated GPU upgrade it.",
- "Unable to initialize Video driver");
+ if (p_rendering_driver == "vulkan") {
+ String executable_name = OS::get_singleton()->get_executable_path().get_file();
+ OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
+ "Please try updating your GPU driver or try using the OpenGL 3 driver.\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n'./" +
+ executable_name + " --rendering-driver opengl3'.\n "
+ "If you have updated your graphics drivers recently, try rebooting.",
+ "Unable to initialize Video driver");
+ } else {
+ OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n"
+ "Please try updating your GPU driver.\n"
+ "If you have updated your graphics drivers recently, try rebooting.",
+ "Unable to initialize Video driver");
+ }
}
return ds;
}
@@ -3692,8 +3990,12 @@ DisplayServerWindows::~DisplayServerWindows() {
SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
}
+ // Close power request handle.
+ screen_set_keep_on(false);
+
#ifdef GLES3_ENABLED
// destroy windows .. NYI?
+ // FIXME wglDeleteContext is never called
#endif
if (windows.has(MAIN_WINDOW_ID)) {
@@ -3731,4 +4033,8 @@ DisplayServerWindows::~DisplayServerWindows() {
gl_manager = nullptr;
}
#endif
+ if (tts) {
+ memdelete(tts);
+ }
+ CoUninitialize();
}