summaryrefslogtreecommitdiffstats
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.cpp1053
1 files changed, 869 insertions, 184 deletions
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index fa73740e04..2e007b5efc 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -35,18 +35,28 @@
#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
+#include "core/version.h"
#include "drivers/png/png_driver_common.h"
#include "main/main.h"
-#include "scene/resources/atlas_texture.h"
+#if defined(VULKAN_ENABLED)
+#include "rendering_context_driver_vulkan_windows.h"
+#endif
+#if defined(D3D12_ENABLED)
+#include "drivers/d3d12/rendering_context_driver_d3d12.h"
+#endif
#if defined(GLES3_ENABLED)
#include "drivers/gles3/rasterizer_gles3.h"
#endif
#include <avrt.h>
#include <dwmapi.h>
+#include <propkey.h>
+#include <propvarutil.h>
+#include <shellapi.h>
#include <shlwapi.h>
#include <shobjidl.h>
+#include <wbemcli.h>
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
@@ -56,6 +66,8 @@
#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
#endif
+#define WM_INDICATOR_CALLBACK_MESSAGE (WM_USER + 1)
+
#if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
#define GetProcAddress (void *)GetProcAddress
@@ -84,6 +96,11 @@ static void track_mouse_leave_event(HWND hWnd) {
bool DisplayServerWindows::has_feature(Feature p_feature) const {
switch (p_feature) {
+#ifndef DISABLE_DEPRECATED
+ case FEATURE_GLOBAL_MENU: {
+ return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));
+ } break;
+#endif
case FEATURE_SUBWINDOWS:
case FEATURE_TOUCHSCREEN:
case FEATURE_MOUSE:
@@ -97,10 +114,13 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_ICON:
case FEATURE_NATIVE_ICON:
case FEATURE_NATIVE_DIALOG:
+ case FEATURE_NATIVE_DIALOG_INPUT:
+ case FEATURE_NATIVE_DIALOG_FILE:
case FEATURE_SWAP_BUFFERS:
case FEATURE_KEEP_SCREEN_ON:
case FEATURE_TEXT_TO_SPEECH:
case FEATURE_SCREEN_CAPTURE:
+ case FEATURE_STATUS_INDICATOR:
return true;
default:
return false;
@@ -184,8 +204,8 @@ void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window)
rid[1].hwndTarget = windows[p_target_window].hWnd;
} else {
// Follow the keyboard focus
- rid[0].hwndTarget = 0;
- rid[1].hwndTarget = 0;
+ rid[0].hwndTarget = nullptr;
+ rid[1].hwndTarget = nullptr;
}
if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) {
@@ -254,7 +274,7 @@ public:
QITABENT(FileDialogEventHandler, IFileDialogEvents),
QITABENT(FileDialogEventHandler, IFileDialogControlEvents),
#endif
- { 0, 0 },
+ { nullptr, 0 },
};
return QISearch(this, qit, riid, ppv);
}
@@ -744,30 +764,54 @@ Ref<Image> DisplayServerWindows::clipboard_get_image() const {
}
} else if (IsClipboardFormatAvailable(CF_DIB)) {
HGLOBAL mem = GetClipboardData(CF_DIB);
- if (mem != NULL) {
+ if (mem != nullptr) {
BITMAPINFO *ptr = static_cast<BITMAPINFO *>(GlobalLock(mem));
- if (ptr != NULL) {
+ if (ptr != nullptr) {
BITMAPINFOHEADER *info = &ptr->bmiHeader;
- PackedByteArray pba;
-
- for (LONG y = info->biHeight - 1; y > -1; y--) {
- for (LONG x = 0; x < info->biWidth; x++) {
- tagRGBQUAD *rgbquad = ptr->bmiColors + (info->biWidth * y) + x;
- pba.append(rgbquad->rgbRed);
- pba.append(rgbquad->rgbGreen);
- pba.append(rgbquad->rgbBlue);
- pba.append(rgbquad->rgbReserved);
+ void *dib_bits = (void *)(ptr->bmiColors);
+
+ // Draw DIB image to temporary DC surface and read it back as BGRA8.
+ HDC dc = GetDC(nullptr);
+ if (dc) {
+ HDC hdc = CreateCompatibleDC(dc);
+ if (hdc) {
+ HBITMAP hbm = CreateCompatibleBitmap(dc, info->biWidth, abs(info->biHeight));
+ if (hbm) {
+ SelectObject(hdc, hbm);
+ SetDIBitsToDevice(hdc, 0, 0, info->biWidth, abs(info->biHeight), 0, 0, 0, abs(info->biHeight), dib_bits, ptr, DIB_RGB_COLORS);
+
+ BITMAPINFO bmp_info = {};
+ bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
+ bmp_info.bmiHeader.biWidth = info->biWidth;
+ bmp_info.bmiHeader.biHeight = -abs(info->biHeight);
+ bmp_info.bmiHeader.biPlanes = 1;
+ bmp_info.bmiHeader.biBitCount = 32;
+ bmp_info.bmiHeader.biCompression = BI_RGB;
+
+ Vector<uint8_t> img_data;
+ img_data.resize(info->biWidth * abs(info->biHeight) * 4);
+ GetDIBits(hdc, hbm, 0, abs(info->biHeight), img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);
+
+ uint8_t *wr = (uint8_t *)img_data.ptrw();
+ for (int i = 0; i < info->biWidth * abs(info->biHeight); i++) {
+ SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.
+ if (info->biBitCount != 32) {
+ wr[i * 4 + 3] = 255; // Set A to solid if it's not in the source image.
+ }
+ }
+ image = Image::create_from_data(info->biWidth, abs(info->biHeight), false, Image::Format::FORMAT_RGBA8, img_data);
+
+ DeleteObject(hbm);
+ }
+ DeleteDC(hdc);
}
+ ReleaseDC(nullptr, dc);
}
- image.instantiate();
- image->create_from_data(info->biWidth, info->biHeight, false, Image::Format::FORMAT_RGBA8, pba);
-
GlobalUnlock(mem);
}
}
}
-
CloseClipboard();
return image;
@@ -826,7 +870,7 @@ int DisplayServerWindows::get_screen_count() const {
}
int DisplayServerWindows::get_primary_screen() const {
- EnumScreenData data = { 0, 0, 0 };
+ EnumScreenData data = { 0, 0, nullptr };
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPrim, (LPARAM)&data);
return data.screen;
}
@@ -861,8 +905,7 @@ static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRE
static BOOL CALLBACK _MonitorEnumProcOrigin(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumPosData *data = (EnumPosData *)dwData;
- data->pos.x = MIN(data->pos.x, lprcMonitor->left);
- data->pos.y = MIN(data->pos.y, lprcMonitor->top);
+ data->pos = data->pos.min(Point2(lprcMonitor->left, lprcMonitor->top));
return TRUE;
}
@@ -1074,16 +1117,16 @@ Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const {
p.x = pos.x;
p.y = pos.y;
if (win81p_LogicalToPhysicalPointForPerMonitorDPI) {
- win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p);
+ win81p_LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p);
}
- HDC dc = GetDC(0);
+ HDC dc = GetDC(nullptr);
if (dc) {
COLORREF col = GetPixel(dc, p.x, p.y);
if (col != CLR_INVALID) {
- ReleaseDC(NULL, dc);
+ ReleaseDC(nullptr, dc);
return Color(float(col & 0x000000FF) / 255.0f, float((col & 0x0000FF00) >> 8) / 255.0f, float((col & 0x00FF0000) >> 16) / 255.0f, 1.0f);
}
- ReleaseDC(NULL, dc);
+ ReleaseDC(nullptr, dc);
}
return Color();
@@ -1114,12 +1157,12 @@ Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {
p2.x = pos.x + size.x;
p2.y = pos.y + size.y;
if (win81p_LogicalToPhysicalPointForPerMonitorDPI) {
- win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p1);
- win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p2);
+ win81p_LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p1);
+ win81p_LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p2);
}
Ref<Image> img;
- HDC dc = GetDC(0);
+ HDC dc = GetDC(nullptr);
if (dc) {
HDC hdc = CreateCompatibleDC(dc);
int width = p2.x - p1.x;
@@ -1144,7 +1187,7 @@ Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {
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]);
+ SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.
}
img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);
@@ -1152,7 +1195,7 @@ Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {
}
DeleteDC(hdc);
}
- ReleaseDC(NULL, dc);
+ ReleaseDC(nullptr, dc);
}
return img;
@@ -1291,6 +1334,11 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
if (mainwindow_icon) {
SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);
}
+#ifdef RD_ENABLED
+ if (rendering_device) {
+ rendering_device->screen_create(window_id);
+ }
+#endif
return window_id;
}
@@ -1336,6 +1384,15 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
WindowData &wd = windows[p_window];
+ IPropertyStore *prop_store;
+ HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);
+ if (hr == S_OK) {
+ PROPVARIANT val;
+ PropVariantInit(&val);
+ prop_store->SetValue(PKEY_AppUserModel_ID, val);
+ prop_store->Release();
+ }
+
while (wd.transient_children.size()) {
window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);
}
@@ -1345,8 +1402,12 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
}
#ifdef RD_ENABLED
- if (context_rd) {
- context_rd->window_destroy(p_window);
+ if (rendering_device) {
+ rendering_device->screen_free(p_window);
+ }
+
+ if (rendering_context) {
+ rendering_context->window_destroy(p_window);
}
#endif
#ifdef GLES3_ENABLED
@@ -1360,7 +1421,7 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window].wtctx) {
wintab_WTClose(windows[p_window].wtctx);
- windows[p_window].wtctx = 0;
+ windows[p_window].wtctx = nullptr;
}
DestroyWindow(windows[p_window].hWnd);
windows.erase(p_window);
@@ -1481,7 +1542,7 @@ Size2i DisplayServerWindows::window_get_title_size(const String &p_title, Window
return size;
}
- HDC hdc = GetDCEx(wd.hWnd, NULL, DCX_WINDOW);
+ HDC hdc = GetDCEx(wd.hWnd, nullptr, DCX_WINDOW);
if (hdc) {
Char16String s = p_title.utf16();
SIZE text_size;
@@ -1499,8 +1560,8 @@ Size2i DisplayServerWindows::window_get_title_size(const String &p_title, Window
ClientToScreen(wd.hWnd, (POINT *)&rect.right);
if (win81p_PhysicalToLogicalPointForPerMonitorDPI) {
- win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.left);
- win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.right);
+ win81p_PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.left);
+ win81p_PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.right);
}
size.x += (rect.right - rect.left);
@@ -1577,8 +1638,7 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi
Size2i wsize = window_get_size(p_window);
wpos += srect.position;
- wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - wsize.width / 3);
- wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3);
+ wpos = wpos.clamp(srect.position, srect.position + srect.size - wsize / 3);
window_set_position(wpos, p_window);
}
}
@@ -1777,8 +1837,8 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
wd.height = h;
#if defined(RD_ENABLED)
- if (context_rd) {
- context_rd->window_resize(p_window, w, h);
+ if (rendering_context) {
+ rendering_context->window_set_size(p_window, w, h);
}
#endif
#if defined(GLES3_ENABLED)
@@ -1931,7 +1991,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
if (restore_mouse_trails > 1) {
- SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0);
+ SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);
restore_mouse_trails = 0;
}
}
@@ -1988,7 +2048,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
// Save number of trails so we can restore when exiting, then turn off mouse trails
SystemParametersInfoA(SPI_GETMOUSETRAILS, 0, &restore_mouse_trails, 0);
if (restore_mouse_trails > 1) {
- SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, 0, 0);
+ SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, nullptr, 0);
}
}
}
@@ -2243,10 +2303,10 @@ void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p
if (p_active) {
wd.ime_active = true;
ImmAssociateContext(wd.hWnd, wd.im_himc);
- CreateCaret(wd.hWnd, NULL, 1, 1);
+ CreateCaret(wd.hWnd, nullptr, 1, 1);
window_set_ime_position(wd.im_position, p_window);
} else {
- ImmAssociateContext(wd.hWnd, (HIMC)0);
+ ImmAssociateContext(wd.hWnd, (HIMC) nullptr);
DestroyCaret();
wd.ime_active = false;
}
@@ -2261,7 +2321,7 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI
wd.im_position = p_pos;
HIMC himc = ImmGetContext(wd.hWnd);
- if (himc == (HIMC)0) {
+ if (himc == (HIMC) nullptr) {
return;
}
@@ -2337,38 +2397,10 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
cursors_cache.erase(p_shape);
}
- Ref<Texture2D> texture = p_cursor;
- ERR_FAIL_COND(!texture.is_valid());
- Ref<AtlasTexture> atlas_texture = p_cursor;
- Size2 texture_size;
Rect2 atlas_rect;
-
- 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 {
- texture_size.width = texture->get_width();
- texture_size.height = texture->get_height();
- }
-
- 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);
-
- 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.");
- }
+ Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect);
+ ERR_FAIL_COND(image.is_null());
+ Vector2i texture_size = image->get_size();
UINT image_size = texture_size.width * texture_size.height;
@@ -2397,7 +2429,7 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
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()) {
+ if (atlas_rect.has_area()) {
column_index = MIN(column_index, atlas_rect.size.width - 1);
row_index = MIN(row_index, atlas_rect.size.height - 1);
}
@@ -2463,6 +2495,298 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
AllowSetForegroundWindow(pid);
}
+static HRESULT CALLBACK win32_task_dialog_callback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) {
+ if (msg == TDN_CREATED) {
+ // To match the input text dialog.
+ SendMessageW(hwnd, WM_SETICON, ICON_BIG, 0);
+ SendMessageW(hwnd, WM_SETICON, ICON_SMALL, 0);
+ }
+
+ return 0;
+}
+
+Error DisplayServerWindows::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
+
+ TASKDIALOGCONFIG config;
+ ZeroMemory(&config, sizeof(TASKDIALOGCONFIG));
+ config.cbSize = sizeof(TASKDIALOGCONFIG);
+
+ Char16String title = p_title.utf16();
+ Char16String message = p_description.utf16();
+ List<Char16String> buttons;
+ for (String s : p_buttons) {
+ buttons.push_back(s.utf16());
+ }
+
+ config.pszWindowTitle = (LPCWSTR)(title.get_data());
+ config.pszContent = (LPCWSTR)(message.get_data());
+
+ const int button_count = MIN(buttons.size(), 8);
+ config.cButtons = button_count;
+
+ // No dynamic stack array size :(
+ TASKDIALOG_BUTTON *tbuttons = button_count != 0 ? (TASKDIALOG_BUTTON *)alloca(sizeof(TASKDIALOG_BUTTON) * button_count) : nullptr;
+ if (tbuttons) {
+ for (int i = 0; i < button_count; i++) {
+ tbuttons[i].nButtonID = i;
+ tbuttons[i].pszButtonText = (LPCWSTR)(buttons[i].get_data());
+ }
+ }
+ config.pButtons = tbuttons;
+ config.pfCallback = win32_task_dialog_callback;
+
+ Error result = FAILED;
+ HMODULE comctl = LoadLibraryW(L"comctl32.dll");
+ if (comctl) {
+ typedef HRESULT(WINAPI * TaskDialogIndirectPtr)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);
+
+ TaskDialogIndirectPtr task_dialog_indirect = (TaskDialogIndirectPtr)GetProcAddress(comctl, "TaskDialogIndirect");
+ int button_pressed;
+
+ if (task_dialog_indirect && SUCCEEDED(task_dialog_indirect(&config, &button_pressed, nullptr, nullptr))) {
+ if (!p_callback.is_null()) {
+ Variant button = button_pressed;
+ const Variant *args[1] = { &button };
+ Variant ret;
+ Callable::CallError ce;
+ p_callback.callp(args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute dialog callback: %s.", Variant::get_callable_error_text(p_callback, args, 1, ce)));
+ }
+ }
+
+ result = OK;
+ }
+ FreeLibrary(comctl);
+ } else {
+ ERR_PRINT("Unable to create native dialog.");
+ }
+
+ return result;
+}
+
+struct Win32InputTextDialogInit {
+ const char16_t *title;
+ const char16_t *description;
+ const char16_t *partial;
+ const Callable &callback;
+};
+
+static int scale_with_dpi(int p_pos, int p_dpi) {
+ return IsProcessDPIAware() ? (p_pos * p_dpi / 96) : p_pos;
+}
+
+static INT_PTR input_text_dialog_init(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
+ Win32InputTextDialogInit init = *(Win32InputTextDialogInit *)lParam;
+ SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)&init.callback); // Set dialog callback.
+
+ SetWindowTextW(hWnd, (LPCWSTR)init.title);
+
+ const int dpi = DisplayServerWindows::get_singleton()->screen_get_dpi();
+
+ const int margin = scale_with_dpi(7, dpi);
+ const SIZE dlg_size = { scale_with_dpi(300, dpi), scale_with_dpi(50, dpi) };
+
+ int str_len = lstrlenW((LPCWSTR)init.description);
+ SIZE str_size = { dlg_size.cx, 0 };
+ if (str_len > 0) {
+ HDC hdc = GetDC(nullptr);
+ RECT trect = { margin, margin, margin + dlg_size.cx, margin + dlg_size.cy };
+ SelectObject(hdc, (HFONT)SendMessageW(hWnd, WM_GETFONT, 0, 0));
+
+ // `+ margin` adds some space between the static text and the edit field.
+ // Don't scale this with DPI because DPI is already handled by DrawText.
+ str_size.cy = DrawTextW(hdc, (LPCWSTR)init.description, str_len, &trect, DT_LEFT | DT_WORDBREAK | DT_CALCRECT) + margin;
+
+ ReleaseDC(nullptr, hdc);
+ }
+
+ RECT crect, wrect;
+ GetClientRect(hWnd, &crect);
+ GetWindowRect(hWnd, &wrect);
+ int sw = GetSystemMetrics(SM_CXSCREEN);
+ int sh = GetSystemMetrics(SM_CYSCREEN);
+ int new_width = dlg_size.cx + margin * 2 + wrect.right - wrect.left - crect.right;
+ int new_height = dlg_size.cy + margin * 2 + wrect.bottom - wrect.top - crect.bottom + str_size.cy;
+
+ MoveWindow(hWnd, (sw - new_width) / 2, (sh - new_height) / 2, new_width, new_height, true);
+
+ HWND ok_button = GetDlgItem(hWnd, 1);
+ MoveWindow(ok_button,
+ dlg_size.cx + margin - scale_with_dpi(65, dpi),
+ dlg_size.cy + str_size.cy + margin - scale_with_dpi(20, dpi),
+ scale_with_dpi(65, dpi), scale_with_dpi(20, dpi), true);
+
+ HWND description = GetDlgItem(hWnd, 3);
+ MoveWindow(description, margin, margin, dlg_size.cx, str_size.cy, true);
+ SetWindowTextW(description, (LPCWSTR)init.description);
+
+ HWND text_edit = GetDlgItem(hWnd, 2);
+ MoveWindow(text_edit, margin, str_size.cy + margin, dlg_size.cx, scale_with_dpi(20, dpi), true);
+ SetWindowTextW(text_edit, (LPCWSTR)init.partial);
+
+ return TRUE;
+}
+
+static INT_PTR input_text_dialog_cmd_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
+ if (LOWORD(wParam) == 1) {
+ HWND text_edit = GetDlgItem(hWnd, 2);
+ ERR_FAIL_NULL_V(text_edit, false);
+
+ Char16String text;
+ text.resize(GetWindowTextLengthW(text_edit) + 1);
+ GetWindowTextW(text_edit, (LPWSTR)text.get_data(), text.size());
+
+ const Callable *callback = (const Callable *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
+ if (callback && callback->is_valid()) {
+ Variant v_result = String((const wchar_t *)text.get_data());
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[1] = { &v_result };
+
+ callback->callp(args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute input dialog callback: %s.", Variant::get_callable_error_text(*callback, args, 1, ce)));
+ }
+ }
+
+ return EndDialog(hWnd, 0);
+ }
+
+ return false;
+}
+
+static INT_PTR CALLBACK input_text_dialog_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
+ switch (code) {
+ case WM_INITDIALOG:
+ return input_text_dialog_init(hWnd, code, wParam, lParam);
+
+ case WM_COMMAND:
+ return input_text_dialog_cmd_proc(hWnd, code, wParam, lParam);
+
+ default:
+ return FALSE;
+ }
+}
+
+Error DisplayServerWindows::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
+#pragma pack(push, 1)
+
+ // NOTE: Use default/placeholder coordinates here. Windows uses its own coordinate system
+ // specifically for dialogs which relies on font sizes instead of pixels.
+ const struct {
+ WORD dlgVer; // must be 1
+ WORD signature; // must be 0xFFFF
+ DWORD helpID;
+ DWORD exStyle;
+ DWORD style;
+ WORD cDlgItems;
+ short x;
+ short y;
+ short cx;
+ short cy;
+ WCHAR menu[1]; // must be 0
+ WCHAR windowClass[7]; // must be "#32770" -- the default window class for dialogs
+ WCHAR title[1]; // must be 0
+ WORD pointsize;
+ WORD weight;
+ BYTE italic;
+ BYTE charset;
+ WCHAR font[13]; // must be "MS Shell Dlg"
+ } template_base = {
+ 1, 0xFFFF, 0, 0,
+ DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU,
+ 3, 0, 0, 20, 20, L"", L"#32770", L"", 8, FW_NORMAL, 0, DEFAULT_CHARSET, L"MS Shell Dlg"
+ };
+
+ const struct {
+ DWORD helpID;
+ DWORD exStyle;
+ DWORD style;
+ short x;
+ short y;
+ short cx;
+ short cy;
+ DWORD id;
+ WCHAR windowClass[7]; // must be "Button"
+ WCHAR title[3]; // must be "OK"
+ WORD extraCount;
+ } ok_button = {
+ 0, 0, WS_VISIBLE | BS_DEFPUSHBUTTON, 0, 0, 50, 14, 1, WC_BUTTONW, L"OK", 0
+ };
+ const struct {
+ DWORD helpID;
+ DWORD exStyle;
+ DWORD style;
+ short x;
+ short y;
+ short cx;
+ short cy;
+ DWORD id;
+ WCHAR windowClass[5]; // must be "Edit"
+ WCHAR title[1]; // must be 0
+ WORD extraCount;
+ } text_field = {
+ 0, 0, WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 0, 0, 250, 14, 2, WC_EDITW, L"", 0
+ };
+ const struct {
+ DWORD helpID;
+ DWORD exStyle;
+ DWORD style;
+ short x;
+ short y;
+ short cx;
+ short cy;
+ DWORD id;
+ WCHAR windowClass[7]; // must be "Static"
+ WCHAR title[1]; // must be 0
+ WORD extraCount;
+ } static_text = {
+ 0, 0, WS_VISIBLE, 0, 0, 250, 14, 3, WC_STATICW, L"", 0
+ };
+
+#pragma pack(pop)
+
+ // Dialog template
+ const size_t data_size = sizeof(template_base) + (sizeof(template_base) % 4) +
+ sizeof(ok_button) + (sizeof(ok_button) % 4) +
+ sizeof(text_field) + (sizeof(text_field) % 4) +
+ sizeof(static_text) + (sizeof(static_text) % 4);
+
+ void *data_template = memalloc(data_size);
+ ERR_FAIL_NULL_V_MSG(data_template, FAILED, "Unable to allocate memory for the dialog template.");
+ ZeroMemory(data_template, data_size);
+
+ char *current_block = (char *)data_template;
+ CopyMemory(current_block, &template_base, sizeof(template_base));
+ current_block += sizeof(template_base) + (sizeof(template_base) % 4);
+ CopyMemory(current_block, &ok_button, sizeof(ok_button));
+ current_block += sizeof(ok_button) + (sizeof(ok_button) % 4);
+ CopyMemory(current_block, &text_field, sizeof(text_field));
+ current_block += sizeof(text_field) + (sizeof(text_field) % 4);
+ CopyMemory(current_block, &static_text, sizeof(static_text));
+
+ Char16String title16 = p_title.utf16();
+ Char16String description16 = p_description.utf16();
+ Char16String partial16 = p_partial.utf16();
+
+ Win32InputTextDialogInit init = {
+ title16.get_data(), description16.get_data(), partial16.get_data(), p_callback
+ };
+
+ // No modal dialogs for specific windows? Assume main window here.
+ INT_PTR ret = DialogBoxIndirectParamW(hInstance, (LPDLGTEMPLATEW)data_template, nullptr, (DLGPROC)input_text_dialog_proc, (LPARAM)(&init));
+
+ Error result = ret != -1 ? OK : FAILED;
+ memfree(data_template);
+
+ if (result == FAILED) {
+ ERR_PRINT("Unable to create native dialog.");
+ }
+ return result;
+}
+
int DisplayServerWindows::keyboard_get_layout_count() const {
return GetKeyboardLayoutList(0, nullptr);
}
@@ -2828,11 +3152,177 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
}
}
+DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const Ref<Image> &p_icon, const String &p_tooltip, const Callable &p_callback) {
+ HICON hicon = nullptr;
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) {
+ Ref<Image> img = p_icon;
+ if (img != icon) {
+ img = img->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
+ }
+
+ int w = img->get_width();
+ int h = img->get_height();
+
+ // Create temporary bitmap buffer.
+ int icon_len = 40 + h * w * 4;
+ Vector<BYTE> 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 = img->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 = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
+ }
+
+ IndicatorData idat;
+ idat.callback = p_callback;
+
+ NOTIFYICONDATAW ndat;
+ ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
+ ndat.cbSize = sizeof(NOTIFYICONDATAW);
+ ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
+ ndat.uID = indicator_id_counter;
+ ndat.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
+ ndat.uCallbackMessage = WM_INDICATOR_CALLBACK_MESSAGE;
+ ndat.hIcon = hicon;
+ memcpy(ndat.szTip, p_tooltip.utf16().ptr(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));
+ ndat.uVersion = NOTIFYICON_VERSION;
+
+ Shell_NotifyIconW(NIM_ADD, &ndat);
+ Shell_NotifyIconW(NIM_SETVERSION, &ndat);
+
+ IndicatorID iid = indicator_id_counter++;
+ indicators[iid] = idat;
+
+ return iid;
+}
+
+void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref<Image> &p_icon) {
+ ERR_FAIL_COND(!indicators.has(p_id));
+
+ HICON hicon = nullptr;
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) {
+ Ref<Image> img = p_icon;
+ if (img != icon) {
+ img = img->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
+ }
+
+ int w = img->get_width();
+ int h = img->get_height();
+
+ // Create temporary bitmap buffer.
+ int icon_len = 40 + h * w * 4;
+ Vector<BYTE> 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 = img->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 = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
+ }
+
+ NOTIFYICONDATAW ndat;
+ ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
+ ndat.cbSize = sizeof(NOTIFYICONDATAW);
+ ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
+ ndat.uID = p_id;
+ ndat.uFlags = NIF_ICON;
+ ndat.hIcon = hicon;
+ ndat.uVersion = NOTIFYICON_VERSION;
+
+ Shell_NotifyIconW(NIM_MODIFY, &ndat);
+}
+
+void DisplayServerWindows::status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) {
+ ERR_FAIL_COND(!indicators.has(p_id));
+
+ NOTIFYICONDATAW ndat;
+ ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
+ ndat.cbSize = sizeof(NOTIFYICONDATAW);
+ ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
+ ndat.uID = p_id;
+ ndat.uFlags = NIF_TIP;
+ memcpy(ndat.szTip, p_tooltip.utf16().ptr(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));
+ ndat.uVersion = NOTIFYICON_VERSION;
+
+ Shell_NotifyIconW(NIM_MODIFY, &ndat);
+}
+
+void DisplayServerWindows::status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) {
+ ERR_FAIL_COND(!indicators.has(p_id));
+
+ indicators[p_id].callback = p_callback;
+}
+
+void DisplayServerWindows::delete_status_indicator(IndicatorID p_id) {
+ ERR_FAIL_COND(!indicators.has(p_id));
+
+ NOTIFYICONDATAW ndat;
+ ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
+ ndat.cbSize = sizeof(NOTIFYICONDATAW);
+ ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
+ ndat.uID = p_id;
+ ndat.uVersion = NOTIFYICON_VERSION;
+
+ Shell_NotifyIconW(NIM_DELETE, &ndat);
+ indicators.erase(p_id);
+}
+
void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
- if (context_rd) {
- context_rd->set_vsync_mode(p_window, p_vsync_mode);
+ if (rendering_context) {
+ rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
}
#endif
@@ -2849,8 +3339,8 @@ void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsyn
DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
- if (context_rd) {
- return context_rd->get_vsync_mode(p_window);
+ if (rendering_context) {
+ return rendering_context->window_get_vsync_mode(p_window);
}
#endif
@@ -2872,9 +3362,9 @@ void DisplayServerWindows::set_context(Context p_context) {
#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)
+#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))
+#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) {
if (touch_state.has(idx) == p_pressed) {
@@ -2913,6 +3403,7 @@ void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y,
event->set_index(idx);
event->set_position(Vector2(p_x, p_y));
event->set_relative(Vector2(p_x, p_y) - curr->get());
+ event->set_relative_screen_position(event->get_relative());
Input::get_singleton()->parse_input_event(event);
@@ -3173,6 +3664,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Process window messages.
switch (uMsg) {
+ case WM_MENUCOMMAND: {
+ native_menu->_menu_activate(HMENU(lParam), (int)wParam);
+ } break;
case WM_CREATE: {
if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
@@ -3215,59 +3709,18 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
return MA_NOACTIVATE; // Do not activate, but process mouse messages.
}
} break;
- case WM_SETFOCUS: {
- windows[window_id].window_has_focus = true;
- last_focused_window = window_id;
-
- // Restore mouse mode.
- _set_mouse_mode_impl(mouse_mode);
-
- if (!app_focused) {
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
- }
- app_focused = true;
- }
- } break;
- case WM_KILLFOCUS: {
- 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 (const KeyValue<int, Vector2> &E : touch_state) {
- _touch_event(window_id, false, E.value.x, E.value.y, E.key);
- }
- touch_state.clear();
-
- bool self_steal = false;
- HWND new_hwnd = (HWND)wParam;
- if (IsWindow(new_hwnd)) {
- self_steal = true;
+ case WM_ACTIVATEAPP: {
+ bool new_app_focused = (bool)wParam;
+ if (new_app_focused == app_focused) {
+ break;
}
-
- if (!self_steal) {
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
- }
- app_focused = false;
+ app_focused = new_app_focused;
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(app_focused ? MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN : MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
}
} break;
- case WM_ACTIVATE: { // Watch for window activate message.
- if (!windows[window_id].window_focused) {
- _process_activate_event(window_id, wParam, lParam);
- } else {
- windows[window_id].saved_wparam = wParam;
- windows[window_id].saved_lparam = lParam;
-
- // Run a timer to prevent event catching warning if the focused window is closing.
- windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
- }
- if (wParam != WA_INACTIVE) {
- track_mouse_leave_event(hWnd);
- }
+ case WM_ACTIVATE: {
+ _process_activate_event(window_id, wParam, lParam);
return 0; // Return to the message loop.
} break;
case WM_GETMINMAXINFO: {
@@ -3309,13 +3762,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_PAINT: {
Main::force_redraw();
} break;
- case WM_SETTINGCHANGE: {
+ case WM_SETTINGCHANGE:
+ case WM_SYSCOLORCHANGE: {
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, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
}
+ if (system_theme_changed.is_valid()) {
+ system_theme_changed.call();
+ }
} break;
case WM_THEMECHANGED: {
if (is_dark_mode_supported() && dark_title_available) {
@@ -3336,11 +3793,32 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
} break;
+ case WM_INDICATOR_CALLBACK_MESSAGE: {
+ if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN || lParam == WM_MBUTTONDOWN || lParam == WM_XBUTTONDOWN) {
+ IndicatorID iid = (IndicatorID)wParam;
+ MouseButton mb = MouseButton::LEFT;
+ if (lParam == WM_RBUTTONDOWN) {
+ mb = MouseButton::RIGHT;
+ } else if (lParam == WM_MBUTTONDOWN) {
+ mb = MouseButton::MIDDLE;
+ } else if (lParam == WM_XBUTTONDOWN) {
+ mb = MouseButton::MB_XBUTTON1;
+ }
+ if (indicators.has(iid)) {
+ if (indicators[iid].callback.is_valid()) {
+ Variant v_button = mb;
+ Variant v_pos = mouse_get_position();
+ Variant *v_args[2] = { &v_button, &v_pos };
+ Variant ret;
+ Callable::CallError ce;
+ indicators[iid].callback.callp((const Variant **)&v_args, 2, ret, ce);
+ }
+ }
+ return 0;
+ }
+ } break;
case WM_CLOSE: // Did we receive a close message?
{
- if (windows[window_id].focus_timer_id != 0U) {
- KillTimer(windows[window_id].hWnd, windows[window_id].focus_timer_id);
- }
_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
return 0; // Jump back.
@@ -3425,6 +3903,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_position(c);
mm->set_global_position(c);
mm->set_velocity(Vector2(0, 0));
+ mm->set_screen_velocity(Vector2(0, 0));
if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));
@@ -3449,6 +3928,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
old_x = coords.x;
old_y = coords.y;
}
+ mm->set_relative_screen_position(mm->get_relative());
if ((windows[window_id].window_has_focus || windows[window_id].is_popup) && mm->get_relative() != Vector2()) {
Input::get_singleton()->parse_input_event(mm);
@@ -3536,6 +4016,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
+ mm->set_screen_velocity(mm->get_velocity());
if (old_invalid) {
old_x = mm->get_position().x;
@@ -3544,6 +4025,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
+ mm->set_relative_screen_position(mm->get_relative());
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) {
@@ -3683,6 +4165,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
+ mm->set_screen_velocity(mm->get_velocity());
if (old_invalid) {
old_x = mm->get_position().x;
@@ -3691,6 +4174,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
+ mm->set_relative_screen_position(mm->get_relative());
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) {
@@ -3802,6 +4286,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
+ mm->set_screen_velocity(mm->get_velocity());
if (old_invalid) {
old_x = mm->get_position().x;
@@ -3810,6 +4295,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
+ mm->set_relative_screen_position(mm->get_relative());
old_x = mm->get_position().x;
old_y = mm->get_position().y;
@@ -4046,9 +4532,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
rect_changed = true;
}
#if defined(RD_ENABLED)
- if (context_rd && window.context_created) {
+ if (rendering_context && window.context_created) {
// Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.
- context_rd->window_resize(window_id, window.width, window.height);
+ rendering_context->window_set_size(window_id, window.width, window.height);
}
#endif
}
@@ -4091,10 +4577,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if (!Main::is_iterating()) {
Main::iteration();
}
- } else if (wParam == windows[window_id].focus_timer_id) {
- _process_activate_event(window_id, windows[window_id].saved_wparam, windows[window_id].saved_lparam);
- KillTimer(windows[window_id].hWnd, wParam);
- windows[window_id].focus_timer_id = 0U;
}
} break;
case WM_SYSKEYUP:
@@ -4300,20 +4782,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam) {
if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
- _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_IN);
- windows[p_window_id].window_focused = true;
+ last_focused_window = p_window_id;
alt_mem = false;
control_mem = false;
shift_mem = false;
gr_mem = false;
-
- // Restore mouse mode.
_set_mouse_mode_impl(mouse_mode);
+ if (!IsIconic(windows[p_window_id].hWnd)) {
+ SetFocus(windows[p_window_id].hWnd);
+ }
+ windows[p_window_id].window_focused = true;
+ _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_IN);
} else { // WM_INACTIVE.
Input::get_singleton()->release_pressed_events();
- _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT);
- windows[p_window_id].window_focused = false;
+ track_mouse_leave_event(windows[p_window_id].hWnd);
+ // Release capture unconditionally because it can be set due to dragging, in addition to captured mode.
+ ReleaseCapture();
alt_mem = false;
+ windows[p_window_id].window_focused = false;
+ _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT);
}
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window_id].wtctx) {
@@ -4481,7 +4968,7 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const
if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) {
wintab_WTEnable(wd.wtctx, false);
wintab_WTClose(wd.wtctx);
- wd.wtctx = 0;
+ wd.wtctx = nullptr;
}
if ((p_new_driver == "wintab") && wintab_available) {
wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);
@@ -4542,8 +5029,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
Rect2i srect = screen_get_usable_rect(rq_screen);
Point2i wpos = p_rect.position;
if (srect != Rect2i()) {
- wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - p_rect.size.width / 3);
- wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - p_rect.size.height / 3);
+ wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);
}
WindowRect.left = wpos.x;
@@ -4570,8 +5056,6 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
dwExStyle,
L"Engine", L"",
dwStyle,
- // (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2,
- // (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2,
WindowRect.left,
WindowRect.top,
WindowRect.right - WindowRect.left,
@@ -4604,13 +5088,13 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
}
#ifdef RD_ENABLED
- if (context_rd) {
+ if (rendering_context) {
union {
#ifdef VULKAN_ENABLED
- VulkanContextWindows::WindowPlatformData vulkan;
+ RenderingContextDriverVulkanWindows::WindowPlatformData vulkan;
#endif
#ifdef D3D12_ENABLED
- D3D12Context::WindowPlatformData d3d12;
+ RenderingContextDriverD3D12::WindowPlatformData d3d12;
#endif
} wpd;
#ifdef VULKAN_ENABLED
@@ -4624,12 +5108,16 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
wpd.d3d12.window = wd.hWnd;
}
#endif
- if (context_rd->window_create(id, p_vsync_mode, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, &wpd) != OK) {
- memdelete(context_rd);
- context_rd = nullptr;
+ if (rendering_context->window_create(id, &wpd) != OK) {
+ ERR_PRINT(vformat("Failed to create %s window.", rendering_driver));
+ memdelete(rendering_context);
+ rendering_context = nullptr;
windows.erase(id);
- ERR_FAIL_V_MSG(INVALID_WINDOW_ID, vformat("Failed to create %s Window.", context_rd->get_api_name()));
+ return INVALID_WINDOW_ID;
}
+
+ rendering_context->window_set_size(id, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top);
+ rendering_context->window_set_vsync_mode(id, p_vsync_mode);
wd.context_created = true;
}
#endif
@@ -4685,7 +5173,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
print_verbose("WinTab context creation failed.");
}
} else {
- wd.wtctx = 0;
+ wd.wtctx = nullptr;
}
if (p_mode == WINDOW_MODE_MAXIMIZED) {
@@ -4702,9 +5190,36 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
wd.last_pressure_update = 0;
wd.last_tilt = Vector2();
+ IPropertyStore *prop_store;
+ HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);
+ if (hr == S_OK) {
+ PROPVARIANT val;
+ String appname;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ appname = "Godot.GodotEditor." + String(VERSION_BRANCH);
+ } else {
+ String name = GLOBAL_GET("application/config/name");
+ String version = GLOBAL_GET("application/config/version");
+ if (version.is_empty()) {
+ version = "0";
+ }
+ String clean_app_name = name.to_pascal_case();
+ for (int i = 0; i < clean_app_name.length(); i++) {
+ if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {
+ clean_app_name[i] = '_';
+ }
+ }
+ clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");
+ appname = "Godot." + clean_app_name + "." + version;
+ }
+ InitPropVariantFromString((PCWSTR)appname.utf16().get_data(), &val);
+ prop_store->SetValue(PKEY_AppUserModel_ID, val);
+ prop_store->Release();
+ }
+
// IME.
wd.im_himc = ImmGetContext(wd.hWnd);
- ImmAssociateContext(wd.hWnd, (HIMC)0);
+ ImmAssociateContext(wd.hWnd, (HIMC) nullptr);
wd.im_position = Vector2();
@@ -4752,6 +5267,68 @@ GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr;
LogicalToPhysicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_LogicalToPhysicalPointForPerMonitorDPI = nullptr;
PhysicalToLogicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_PhysicalToLogicalPointForPerMonitorDPI = nullptr;
+Vector2i _get_device_ids(const String &p_device_name) {
+ if (p_device_name.is_empty()) {
+ return Vector2i();
+ }
+
+ REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID
+ REFIID uuid = IID_IWbemLocator; // Interface UUID
+ IWbemLocator *wbemLocator = nullptr; // to get the services
+ IWbemServices *wbemServices = nullptr; // to get the class
+ IEnumWbemClassObject *iter = nullptr;
+ IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.
+
+ HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);
+ if (hr != S_OK) {
+ return Vector2i();
+ }
+ BSTR resource_name = SysAllocString(L"root\\CIMV2");
+ hr = wbemLocator->ConnectServer(resource_name, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemServices);
+ SysFreeString(resource_name);
+
+ SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices`
+ if (hr != S_OK) {
+ SAFE_RELEASE(wbemServices)
+ return Vector2i();
+ }
+
+ Vector2i ids;
+
+ const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", p_device_name);
+ BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data());
+ BSTR query_lang = SysAllocString(L"WQL");
+ hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, nullptr, &iter);
+ SysFreeString(query_lang);
+ SysFreeString(query);
+ if (hr == S_OK) {
+ ULONG resultCount;
+ hr = iter->Next(5000, 1, pnpSDriverObject, &resultCount); // Get exactly 1. Wait max 5 seconds.
+
+ if (hr == S_OK && resultCount > 0) {
+ VARIANT did;
+ VariantInit(&did);
+ BSTR object_name = SysAllocString(L"DeviceID");
+ hr = pnpSDriverObject[0]->Get(object_name, 0, &did, nullptr, nullptr);
+ SysFreeString(object_name);
+ if (hr == S_OK) {
+ String device_id = String(V_BSTR(&did));
+ ids.x = device_id.get_slice("&", 0).lstrip("PCI\\VEN_").hex_to_int();
+ ids.y = device_id.get_slice("&", 1).lstrip("DEV_").hex_to_int();
+ }
+
+ for (ULONG i = 0; i < resultCount; i++) {
+ SAFE_RELEASE(pnpSDriverObject[i])
+ }
+ }
+ }
+
+ SAFE_RELEASE(wbemServices)
+ SAFE_RELEASE(iter)
+
+ return ids;
+}
+
typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_DPI_UNAWARE = 0,
SHC_PROCESS_SYSTEM_DPI_AWARE = 1,
@@ -4775,6 +5352,19 @@ Color DisplayServerWindows::get_accent_color() const {
return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);
}
+Color DisplayServerWindows::get_base_color() const {
+ if (!ux_theme_available) {
+ return Color(0, 0, 0, 0);
+ }
+
+ int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(ShouldAppsUseDarkMode() ? L"ImmersiveDarkChromeMediumLow" : L"ImmersiveLightChromeMediumLow"), false, 0);
+ return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);
+}
+
+void DisplayServerWindows::set_system_theme_change_callback(const Callable &p_callable) {
+ system_theme_changed = p_callable;
+}
+
int DisplayServerWindows::tablet_get_driver_count() const {
return tablet_drivers.size();
}
@@ -4833,6 +5423,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (tts_enabled) {
tts = memnew(TTS_Windows);
}
+ native_menu = memnew(NativeMenuWindows);
// Enforce default keep screen on value.
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
@@ -4858,6 +5449,32 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));
GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));
GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
+ if (os_ver.dwBuildNumber >= 17763) {
+ AllowDarkModeForAppPtr AllowDarkModeForApp = nullptr;
+ SetPreferredAppModePtr SetPreferredAppMode = nullptr;
+ FlushMenuThemesPtr FlushMenuThemes = nullptr;
+ if (os_ver.dwBuildNumber < 18362) {
+ AllowDarkModeForApp = (AllowDarkModeForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));
+ } else {
+ SetPreferredAppMode = (SetPreferredAppModePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));
+ FlushMenuThemes = (FlushMenuThemesPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));
+ }
+ RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyState = (RefreshImmersiveColorPolicyStatePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(104));
+ if (ShouldAppsUseDarkMode) {
+ bool dark_mode = ShouldAppsUseDarkMode();
+ if (SetPreferredAppMode) {
+ SetPreferredAppMode(dark_mode ? APPMODE_ALLOWDARK : APPMODE_DEFAULT);
+ } else if (AllowDarkModeForApp) {
+ AllowDarkModeForApp(dark_mode);
+ }
+ if (RefreshImmersiveColorPolicyState) {
+ RefreshImmersiveColorPolicyState();
+ }
+ if (FlushMenuThemes) {
+ FlushMenuThemes();
+ }
+ }
+ }
ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
if (os_ver.dwBuildNumber >= 18363) {
@@ -4916,6 +5533,23 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
}
+ HMODULE comctl32 = LoadLibraryW(L"comctl32.dll");
+ if (comctl32) {
+ typedef BOOL(WINAPI * InitCommonControlsExPtr)(_In_ const INITCOMMONCONTROLSEX *picce);
+ InitCommonControlsExPtr init_common_controls_ex = (InitCommonControlsExPtr)GetProcAddress(comctl32, "InitCommonControlsEx");
+
+ // Fails if the incorrect version was loaded. Probably not a big enough deal to print an error about.
+ if (init_common_controls_ex) {
+ INITCOMMONCONTROLSEX icc = {};
+ icc.dwICC = ICC_STANDARD_CLASSES;
+ icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ if (!init_common_controls_ex(&icc)) {
+ WARN_PRINT("Unable to initialize Windows common controls. Native dialogs may not work properly.");
+ }
+ }
+ FreeLibrary(comctl32);
+ }
+
memset(&wc, 0, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_OWNDC | CS_DBLCLKS;
@@ -4940,19 +5574,19 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
#if defined(RD_ENABLED)
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
- context_rd = memnew(VulkanContextWindows);
+ rendering_context = memnew(RenderingContextDriverVulkanWindows);
}
#endif
#if defined(D3D12_ENABLED)
if (rendering_driver == "d3d12") {
- context_rd = memnew(D3D12Context);
+ rendering_context = memnew(RenderingContextDriverD3D12);
}
#endif
- if (context_rd) {
- if (context_rd->initialize() != OK) {
- memdelete(context_rd);
- context_rd = nullptr;
+ if (rendering_context) {
+ if (rendering_context->initialize() != OK) {
+ memdelete(rendering_context);
+ rendering_context = nullptr;
r_error = ERR_UNAVAILABLE;
return;
}
@@ -4966,19 +5600,29 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (rendering_driver == "opengl3") {
rendering_driver = "opengl3_angle";
}
-#else
+#elif defined(EGL_STATIC)
bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_angle");
if (fallback && (rendering_driver == "opengl3")) {
Dictionary gl_info = detect_wgl();
bool force_angle = false;
+ Vector2i device_id = _get_device_ids(gl_info["name"]);
Array device_list = GLOBAL_GET("rendering/gl_compatibility/force_angle_on_devices");
for (int i = 0; i < device_list.size(); i++) {
const Dictionary &device = device_list[i];
- if (device.has("vendor") && device.has("name") && gl_info["vendor"].operator String().to_upper().contains(device["vendor"].operator String().to_upper()) && (device["name"] == "*" || gl_info["name"].operator String().to_upper().contains(device["name"].operator String().to_upper()))) {
- force_angle = true;
- break;
+ if (device.has("vendor") && device.has("name")) {
+ const String &vendor = device["vendor"];
+ const String &name = device["name"];
+ if (device_id != Vector2i() && vendor.begins_with("0x") && name.begins_with("0x") && device_id.x == vendor.lstrip("0x").hex_to_int() && device_id.y == name.lstrip("0x").hex_to_int()) {
+ // Check vendor/device IDs.
+ force_angle = true;
+ break;
+ } else if (gl_info["vendor"].operator String().to_upper().contains(vendor.to_upper()) && (name == "*" || gl_info["name"].operator String().to_upper().contains(name.to_upper()))) {
+ // Check vendor/device names.
+ force_angle = true;
+ break;
+ }
}
}
@@ -5015,6 +5659,26 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
#endif
+ String appname;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ appname = "Godot.GodotEditor." + String(VERSION_BRANCH);
+ } else {
+ String name = GLOBAL_GET("application/config/name");
+ String version = GLOBAL_GET("application/config/version");
+ if (version.is_empty()) {
+ version = "0";
+ }
+ String clean_app_name = name.to_pascal_case();
+ for (int i = 0; i < clean_app_name.length(); i++) {
+ if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {
+ clean_app_name[i] = '_';
+ }
+ }
+ clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");
+ appname = "Godot." + clean_app_name + "." + version;
+ }
+ SetCurrentProcessExplicitAppUserModelID((PCWSTR)appname.utf16().get_data());
+
mouse_monitor = SetWindowsHookEx(WH_MOUSE, ::MouseProc, nullptr, GetCurrentThreadId());
Point2i window_position;
@@ -5042,9 +5706,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
show_window(MAIN_WINDOW_ID);
#if defined(RD_ENABLED)
- if (context_rd) {
+ if (rendering_context) {
rendering_device = memnew(RenderingDevice);
- rendering_device->initialize(context_rd);
+ rendering_device->initialize(rendering_context, MAIN_WINDOW_ID);
+ rendering_device->screen_create(MAIN_WINDOW_ID);
RendererCompositorRD::make_current();
}
@@ -5138,6 +5803,18 @@ DisplayServerWindows::~DisplayServerWindows() {
cursors_cache.clear();
+ // Destroy all status indicators.
+ for (HashMap<IndicatorID, IndicatorData>::Iterator E = indicators.begin(); E; ++E) {
+ NOTIFYICONDATAW ndat;
+ ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
+ ndat.cbSize = sizeof(NOTIFYICONDATAW);
+ ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
+ ndat.uID = E->key;
+ ndat.uVersion = NOTIFYICON_VERSION;
+
+ Shell_NotifyIconW(NIM_DELETE, &ndat);
+ }
+
if (mouse_monitor) {
UnhookWindowsHookEx(mouse_monitor);
}
@@ -5149,6 +5826,11 @@ DisplayServerWindows::~DisplayServerWindows() {
// Close power request handle.
screen_set_keep_on(false);
+ if (native_menu) {
+ memdelete(native_menu);
+ native_menu = nullptr;
+ }
+
#ifdef GLES3_ENABLED
// destroy windows .. NYI?
// FIXME wglDeleteContext is never called
@@ -5156,32 +5838,35 @@ DisplayServerWindows::~DisplayServerWindows() {
if (windows.has(MAIN_WINDOW_ID)) {
#ifdef RD_ENABLED
- if (context_rd) {
- context_rd->window_destroy(MAIN_WINDOW_ID);
+ if (rendering_device) {
+ rendering_device->screen_free(MAIN_WINDOW_ID);
+ }
+
+ if (rendering_context) {
+ rendering_context->window_destroy(MAIN_WINDOW_ID);
}
#endif
if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) {
wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);
- windows[MAIN_WINDOW_ID].wtctx = 0;
+ windows[MAIN_WINDOW_ID].wtctx = nullptr;
}
DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
}
#ifdef RD_ENABLED
if (rendering_device) {
- rendering_device->finalize();
memdelete(rendering_device);
rendering_device = nullptr;
}
- if (context_rd) {
- memdelete(context_rd);
- context_rd = nullptr;
+ if (rendering_context) {
+ memdelete(rendering_context);
+ rendering_context = nullptr;
}
#endif
if (restore_mouse_trails > 1) {
- SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0);
+ SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);
}
#ifdef GLES3_ENABLED
if (gl_manager_angle) {