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.cpp1151
1 files changed, 986 insertions, 165 deletions
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 41b51f1173..72c07c3337 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -31,6 +31,7 @@
#include "display_server_windows.h"
#include "os_windows.h"
+#include "wgl_detect_version.h"
#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
@@ -38,6 +39,12 @@
#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
@@ -46,11 +53,18 @@
#include <dwmapi.h>
#include <shlwapi.h>
#include <shobjidl.h>
+#include <wbemcli.h>
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
+#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
+#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
@@ -96,6 +110,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_KEEP_SCREEN_ON:
case FEATURE_TEXT_TO_SPEECH:
case FEATURE_SCREEN_CAPTURE:
+ case FEATURE_STATUS_INDICATOR:
return true;
default:
return false;
@@ -164,81 +179,243 @@ DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() con
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;
+ RAWINPUTDEVICE rid[2] = {};
+ rid[0].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
+ rid[0].usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
rid[0].dwFlags = 0;
+ rid[1].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
+ rid[1].usUsage = 0x06; // HID_USAGE_GENERIC_KEYBOARD
+ rid[1].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;
+ rid[1].hwndTarget = windows[p_target_window].hWnd;
} else {
// Follow the keyboard focus
rid[0].hwndTarget = 0;
+ rid[1].hwndTarget = 0;
}
- if (RegisterRawInputDevices(rid, 1, sizeof(rid[0])) == FALSE) {
+ if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) {
// Registration failed.
use_raw_input = false;
}
}
bool DisplayServerWindows::tts_is_speaking() const {
- ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->is_speaking();
}
bool DisplayServerWindows::tts_is_paused() const {
- ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->is_paused();
}
TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const {
- ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_V_MSG(tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->get_voices();
}
void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
- ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
}
void DisplayServerWindows::tts_pause() {
- ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->pause();
}
void DisplayServerWindows::tts_resume() {
- ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->resume();
}
void DisplayServerWindows::tts_stop() {
- ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->stop();
}
+// Silence warning due to a COM API weirdness.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+class FileDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents {
+ LONG ref_count = 1;
+ int ctl_id = 1;
+
+ HashMap<int, String> ctls;
+ Dictionary selected;
+ String root;
+
+public:
+ // IUnknown methods
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) {
+ static const QITAB qit[] = {
+#ifdef __MINGW32__
+ { &__uuidof(IFileDialogEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogEvents, FileDialogEventHandler)) },
+ { &__uuidof(IFileDialogControlEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogControlEvents, FileDialogEventHandler)) },
+#else
+ QITABENT(FileDialogEventHandler, IFileDialogEvents),
+ QITABENT(FileDialogEventHandler, IFileDialogControlEvents),
+#endif
+ { 0, 0 },
+ };
+ return QISearch(this, qit, riid, ppv);
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef() {
+ return InterlockedIncrement(&ref_count);
+ }
+
+ ULONG STDMETHODCALLTYPE Release() {
+ long ref = InterlockedDecrement(&ref_count);
+ if (!ref) {
+ delete this;
+ }
+ return ref;
+ }
+
+ // IFileDialogEvents methods
+ HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *) { return S_OK; };
+ HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialog *) { return S_OK; };
+
+ HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog *p_pfd, IShellItem *p_item) {
+ if (root.is_empty()) {
+ return S_OK;
+ }
+
+ LPWSTR lpw_path = nullptr;
+ p_item->GetDisplayName(SIGDN_FILESYSPATH, &lpw_path);
+ if (!lpw_path) {
+ return S_FALSE;
+ }
+ String path = String::utf16((const char16_t *)lpw_path).simplify_path();
+ if (!path.begins_with(root.simplify_path())) {
+ return S_FALSE;
+ }
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE OnHelp(IFileDialog *) { return S_OK; };
+ HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialog *) { return S_OK; };
+ HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; };
+ HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog *pfd) { return S_OK; };
+ HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; };
+
+ // IFileDialogControlEvents methods
+ HRESULT STDMETHODCALLTYPE OnItemSelected(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, DWORD p_item_idx) {
+ if (ctls.has(p_ctl_id)) {
+ selected[ctls[p_ctl_id]] = (int)p_item_idx;
+ }
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; };
+ HRESULT STDMETHODCALLTYPE OnCheckButtonToggled(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, BOOL p_checked) {
+ if (ctls.has(p_ctl_id)) {
+ selected[ctls[p_ctl_id]] = (bool)p_checked;
+ }
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; };
+
+ Dictionary get_selected() {
+ return selected;
+ }
+
+ void set_root(const String &p_root) {
+ root = p_root;
+ }
+
+ void add_option(IFileDialogCustomize *p_pfdc, const String &p_name, const Vector<String> &p_options, int p_default) {
+ int gid = ctl_id++;
+ int cid = ctl_id++;
+
+ if (p_options.size() == 0) {
+ // Add check box.
+ p_pfdc->StartVisualGroup(gid, L"");
+ p_pfdc->AddCheckButton(cid, (LPCWSTR)p_name.utf16().get_data(), p_default);
+ p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);
+ p_pfdc->EndVisualGroup();
+ selected[p_name] = (bool)p_default;
+ } else {
+ // Add combo box.
+ p_pfdc->StartVisualGroup(gid, (LPCWSTR)p_name.utf16().get_data());
+ p_pfdc->AddComboBox(cid);
+ p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);
+ for (int i = 0; i < p_options.size(); i++) {
+ p_pfdc->AddControlItem(cid, i, (LPCWSTR)p_options[i].utf16().get_data());
+ }
+ p_pfdc->SetSelectedControlItem(cid, p_default);
+ p_pfdc->EndVisualGroup();
+ selected[p_name] = p_default;
+ }
+ ctls[cid] = p_name;
+ }
+
+ virtual ~FileDialogEventHandler(){};
+};
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ return _file_dialog_with_options_show(p_title, p_current_directory, String(), p_filename, p_show_hidden, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);
+}
+
+Error DisplayServerWindows::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) {
+ return _file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, true);
+}
+
+Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);
+
Vector<Char16String> filter_names;
Vector<Char16String> filter_exts;
for (const String &E : p_filters) {
Vector<String> tokens = E.split(";");
- if (tokens.size() == 2) {
- filter_exts.push_back(tokens[0].strip_edges().utf16());
- filter_names.push_back(tokens[1].strip_edges().utf16());
- } else if (tokens.size() == 1) {
- filter_exts.push_back(tokens[0].strip_edges().utf16());
- filter_names.push_back(tokens[0].strip_edges().utf16());
+ if (tokens.size() >= 1) {
+ String flt = tokens[0].strip_edges();
+ int filter_slice_count = flt.get_slice_count(",");
+ Vector<String> exts;
+ for (int j = 0; j < filter_slice_count; j++) {
+ String str = (flt.get_slice(",", j).strip_edges());
+ if (!str.is_empty()) {
+ exts.push_back(str);
+ }
+ }
+ if (!exts.is_empty()) {
+ String str = String(";").join(exts);
+ filter_exts.push_back(str.utf16());
+ if (tokens.size() == 2) {
+ filter_names.push_back(tokens[1].strip_edges().utf16());
+ } else {
+ filter_names.push_back(str.utf16());
+ }
+ }
}
}
+ if (filter_names.is_empty()) {
+ filter_exts.push_back(String("*.*").utf16());
+ filter_names.push_back(RTR("All Files").utf16());
+ }
Vector<COMDLG_FILTERSPEC> filters;
for (int i = 0; i < filter_names.size(); i++) {
filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });
}
+ WindowID prev_focus = last_focused_window;
+
HRESULT hr = S_OK;
IFileDialog *pfd = nullptr;
if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) {
@@ -247,6 +424,31 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd);
}
if (SUCCEEDED(hr)) {
+ IFileDialogEvents *pfde = nullptr;
+ FileDialogEventHandler *event_handler = new FileDialogEventHandler();
+ hr = event_handler->QueryInterface(IID_PPV_ARGS(&pfde));
+
+ DWORD cookie = 0;
+ hr = pfd->Advise(pfde, &cookie);
+
+ IFileDialogCustomize *pfdc = nullptr;
+ hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));
+
+ for (int i = 0; i < p_options.size(); i++) {
+ const Dictionary &item = p_options[i];
+ if (!item.has("name") || !item.has("values") || !item.has("default")) {
+ continue;
+ }
+ const String &name = item["name"];
+ const Vector<String> &options = item["values"];
+ int default_idx = item["default"];
+
+ event_handler->add_option(pfdc, name, options, default_idx);
+ }
+ event_handler->set_root(p_root);
+
+ pfdc->Release();
+
DWORD flags;
pfd->GetOptions(&flags);
if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) {
@@ -284,6 +486,19 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
}
hr = pfd->Show(windows[window_id].hWnd);
+ pfd->Unadvise(cookie);
+
+ Dictionary options = event_handler->get_selected();
+
+ pfde->Release();
+ event_handler->Release();
+
+ UINT index = 0;
+ pfd->GetFileTypeIndex(&index);
+ if (index > 0) {
+ index = index - 1;
+ }
+
if (SUCCEEDED(hr)) {
Vector<String> file_names;
@@ -321,24 +536,67 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
}
}
if (!p_callback.is_null()) {
- Variant v_status = true;
- Variant v_files = file_names;
- Variant *v_args[2] = { &v_status, &v_files };
- Variant ret;
- Callable::CallError ce;
- p_callback.callp((const Variant **)&v_args, 2, ret, ce);
+ if (p_options_in_cb) {
+ Variant v_result = true;
+ Variant v_files = file_names;
+ Variant v_index = index;
+ Variant v_opt = options;
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
+
+ p_callback.callp(args, 4, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 4, ce)));
+ }
+ } else {
+ Variant v_result = true;
+ Variant v_files = file_names;
+ Variant v_index = index;
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &v_result, &v_files, &v_index };
+
+ p_callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 3, ce)));
+ }
+ }
}
} else {
if (!p_callback.is_null()) {
- Variant v_status = false;
- Variant v_files = Vector<String>();
- Variant *v_args[2] = { &v_status, &v_files };
- Variant ret;
- Callable::CallError ce;
- p_callback.callp((const Variant **)&v_args, 2, ret, ce);
+ if (p_options_in_cb) {
+ Variant v_result = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = index;
+ Variant v_opt = options;
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
+
+ p_callback.callp(args, 4, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 4, ce)));
+ }
+ } else {
+ Variant v_result = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = index;
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &v_result, &v_files, &v_index };
+
+ p_callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 3, ce)));
+ }
+ }
}
}
pfd->Release();
+ if (prev_focus != INVALID_WINDOW_ID) {
+ callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
+ }
return OK;
} else {
@@ -413,7 +671,7 @@ void DisplayServerWindows::clipboard_set(const String &p_text) {
Char16String utf16 = text.utf16();
HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR));
- ERR_FAIL_COND_MSG(mem == nullptr, "Unable to allocate memory for clipboard contents.");
+ ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");
LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);
memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR));
@@ -424,7 +682,7 @@ void DisplayServerWindows::clipboard_set(const String &p_text) {
// Set the CF_TEXT version (not needed?).
CharString utf8 = text.utf8();
mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);
- ERR_FAIL_COND_MSG(mem == nullptr, "Unable to allocate memory for clipboard contents.");
+ ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");
LPTSTR ptr = (LPTSTR)GlobalLock(mem);
memcpy(ptr, utf8.get_data(), utf8.length());
@@ -512,8 +770,7 @@ Ref<Image> DisplayServerWindows::clipboard_get_image() const {
pba.append(rgbquad->rgbReserved);
}
}
- image.instantiate();
- image->create_from_data(info->biWidth, info->biHeight, false, Image::Format::FORMAT_RGBA8, pba);
+ image = Image::create_from_data(info->biWidth, info->biHeight, false, Image::Format::FORMAT_RGBA8, pba);
GlobalUnlock(mem);
}
@@ -649,6 +906,8 @@ typedef struct {
} EnumRectData;
typedef struct {
+ Vector<DISPLAYCONFIG_PATH_INFO> paths;
+ Vector<DISPLAYCONFIG_MODE_INFO> modes;
int count;
int screen;
float rate;
@@ -700,12 +959,30 @@ static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonit
minfo.cbSize = sizeof(minfo);
GetMonitorInfoW(hMonitor, &minfo);
- DEVMODEW dm;
- memset(&dm, 0, sizeof(dm));
- dm.dmSize = sizeof(dm);
- EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);
+ bool found = false;
+ for (const DISPLAYCONFIG_PATH_INFO &path : data->paths) {
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name;
+ memset(&source_name, 0, sizeof(source_name));
+ source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+ source_name.header.size = sizeof(source_name);
+ source_name.header.adapterId = path.sourceInfo.adapterId;
+ source_name.header.id = path.sourceInfo.id;
+ if (DisplayConfigGetDeviceInfo(&source_name.header) == ERROR_SUCCESS) {
+ if (wcscmp(minfo.szDevice, source_name.viewGdiDeviceName) == 0 && path.targetInfo.refreshRate.Numerator != 0 && path.targetInfo.refreshRate.Denominator != 0) {
+ data->rate = (double)path.targetInfo.refreshRate.Numerator / (double)path.targetInfo.refreshRate.Denominator;
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ DEVMODEW dm;
+ memset(&dm, 0, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+ EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);
- data->rate = dm.dmDisplayFrequency;
+ data->rate = dm.dmDisplayFrequency;
+ }
}
data->count++;
@@ -894,7 +1171,19 @@ float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
- EnumRefreshRateData data = { 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK };
+ EnumRefreshRateData data = { Vector<DISPLAYCONFIG_PATH_INFO>(), Vector<DISPLAYCONFIG_MODE_INFO>(), 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK };
+
+ uint32_t path_count = 0;
+ uint32_t mode_count = 0;
+ if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) {
+ data.paths.resize(path_count);
+ data.modes.resize(mode_count);
+ if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, data.paths.ptrw(), &mode_count, data.modes.ptrw(), nullptr) != ERROR_SUCCESS) {
+ data.paths.clear();
+ data.modes.clear();
+ }
+ }
+
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data);
return data.rate;
}
@@ -1011,6 +1300,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;
}
@@ -1064,14 +1358,21 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
window_set_transient(p_window, INVALID_WINDOW_ID);
}
-#ifdef VULKAN_ENABLED
- if (context_vulkan) {
- context_vulkan->window_destroy(p_window);
+#ifdef RD_ENABLED
+ if (rendering_device) {
+ rendering_device->screen_free(p_window);
+ }
+
+ if (rendering_context) {
+ rendering_context->window_destroy(p_window);
}
#endif
#ifdef GLES3_ENABLED
- if (gl_manager) {
- gl_manager->window_destroy(p_window);
+ if (gl_manager_angle) {
+ gl_manager_angle->window_destroy(p_window);
+ }
+ if (gl_manager_native) {
+ gl_manager_native->window_destroy(p_window);
}
#endif
@@ -1089,8 +1390,11 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) {
#if defined(GLES3_ENABLED)
- if (gl_manager) {
- gl_manager->window_make_current(p_window_id);
+ if (gl_manager_angle) {
+ gl_manager_angle->window_make_current(p_window_id);
+ }
+ if (gl_manager_native) {
+ gl_manager_native->window_make_current(p_window_id);
}
#endif
}
@@ -1106,14 +1410,18 @@ int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type,
}
#if defined(GLES3_ENABLED)
case WINDOW_VIEW: {
- if (gl_manager) {
- return (int64_t)gl_manager->get_hdc(p_window);
+ if (gl_manager_native) {
+ return (int64_t)gl_manager_native->get_hdc(p_window);
+ } else {
+ return (int64_t)GetDC(windows[p_window].hWnd);
}
- return 0;
}
case OPENGL_CONTEXT: {
- if (gl_manager) {
- return (int64_t)gl_manager->get_hglrc(p_window);
+ if (gl_manager_native) {
+ return (int64_t)gl_manager_native->get_hglrc(p_window);
+ }
+ if (gl_manager_angle) {
+ return (int64_t)gl_manager_angle->get_context(p_window);
}
return 0;
}
@@ -1180,6 +1488,51 @@ void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_wi
SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data()));
}
+Size2i DisplayServerWindows::window_get_title_size(const String &p_title, WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ Size2i size;
+ ERR_FAIL_COND_V(!windows.has(p_window), size);
+
+ const WindowData &wd = windows[p_window];
+ if (wd.fullscreen || wd.minimized || wd.borderless) {
+ return size;
+ }
+
+ HDC hdc = GetDCEx(wd.hWnd, NULL, DCX_WINDOW);
+ if (hdc) {
+ Char16String s = p_title.utf16();
+ SIZE text_size;
+ if (GetTextExtentPoint32W(hdc, (LPCWSTR)(s.get_data()), s.length(), &text_size)) {
+ size.x = text_size.cx;
+ size.y = text_size.cy;
+ }
+
+ ReleaseDC(wd.hWnd, hdc);
+ }
+ RECT rect;
+ if (DwmGetWindowAttribute(wd.hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rect, sizeof(RECT)) == S_OK) {
+ if (rect.right - rect.left > 0) {
+ ClientToScreen(wd.hWnd, (POINT *)&rect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&rect.right);
+
+ if (win81p_PhysicalToLogicalPointForPerMonitorDPI) {
+ win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.left);
+ win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.right);
+ }
+
+ size.x += (rect.right - rect.left);
+ size.y = MAX(size.y, rect.bottom - rect.top);
+ }
+ }
+ if (icon.is_valid()) {
+ size.x += 32;
+ } else {
+ size.x += 16;
+ }
+ return size;
+}
+
void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -1441,14 +1794,17 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
wd.width = w;
wd.height = h;
-#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- context_vulkan->window_resize(p_window, w, h);
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ rendering_context->window_set_size(p_window, w, h);
}
#endif
#if defined(GLES3_ENABLED)
- if (gl_manager) {
- gl_manager->window_resize(p_window, w, h);
+ if (gl_manager_native) {
+ gl_manager_native->window_resize(p_window, w, h);
+ }
+ if (gl_manager_angle) {
+ gl_manager_angle->window_resize(p_window, w, h);
}
#endif
@@ -1511,7 +1867,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre
if (p_fullscreen || p_borderless) {
r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past.
- if (p_fullscreen && p_multiwindow_fs) {
+ if ((p_fullscreen && p_multiwindow_fs) || p_maximized) {
r_style |= WS_BORDER; // Allows child windows to be displayed on top of full screen.
}
} else {
@@ -1596,7 +1952,9 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0);
restore_mouse_trails = 0;
}
- } else if (p_mode == WINDOW_MODE_WINDOWED) {
+ }
+
+ if (p_mode == WINDOW_MODE_WINDOWED) {
ShowWindow(wd.hWnd, SW_RESTORE);
wd.maximized = false;
wd.minimized = false;
@@ -1818,6 +2176,10 @@ bool DisplayServerWindows::window_is_focused(WindowID p_window) const {
return wd.window_focused;
}
+DisplayServerWindows::WindowID DisplayServerWindows::get_focused_window() const {
+ return last_focused_window;
+}
+
bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
_THREAD_SAFE_METHOD_
@@ -2318,8 +2680,11 @@ void DisplayServerWindows::make_rendering_thread() {
void DisplayServerWindows::swap_buffers() {
#if defined(GLES3_ENABLED)
- if (gl_manager) {
- gl_manager->swap_buffers();
+ if (gl_manager_angle) {
+ gl_manager_angle->swap_buffers();
+ }
+ if (gl_manager_native) {
+ gl_manager_native->swap_buffers();
}
#endif
}
@@ -2390,7 +2755,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
f->seek(pos);
f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big);
HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000);
- ERR_FAIL_COND_MSG(!icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
+ ERR_FAIL_NULL_MSG(icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
// Read the small icon.
DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;
@@ -2400,7 +2765,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
f->seek(pos);
f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small);
HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000);
- ERR_FAIL_COND_MSG(!icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
+ ERR_FAIL_NULL_MSG(icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
// Online tradition says to be sure last error is cleared and set the small icon first.
int err = 0;
@@ -2465,7 +2830,7 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
}
HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
- ERR_FAIL_COND(!hicon);
+ ERR_FAIL_NULL(hicon);
icon = img;
@@ -2481,35 +2846,206 @@ 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(VULKAN_ENABLED)
- if (context_vulkan) {
- context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
}
#endif
#if defined(GLES3_ENABLED)
- if (gl_manager) {
- gl_manager->set_use_vsync(p_window, p_vsync_mode != DisplayServer::VSYNC_DISABLED);
+ if (gl_manager_native) {
+ gl_manager_native->set_use_vsync(p_window, p_vsync_mode != DisplayServer::VSYNC_DISABLED);
+ }
+ if (gl_manager_angle) {
+ gl_manager_angle->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
}
#endif
}
DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {
_THREAD_SAFE_METHOD_
-#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- return context_vulkan->get_vsync_mode(p_window);
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ return rendering_context->window_get_vsync_mode(p_window);
}
#endif
#if defined(GLES3_ENABLED)
- if (gl_manager) {
- return gl_manager->is_using_vsync(p_window) ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
+ if (gl_manager_native) {
+ return gl_manager_native->is_using_vsync(p_window) ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
+ }
+ if (gl_manager_angle) {
+ return gl_manager_angle->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
}
#endif
-
return DisplayServer::VSYNC_ENABLED;
}
@@ -2561,6 +3097,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);
@@ -2568,12 +3105,9 @@ void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y,
}
void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent p_event) {
- if (!wd.event_callback.is_null()) {
+ if (wd.event_callback.is_valid()) {
Variant event = int(p_event);
- Variant *eventp = &event;
- Variant ret;
- Callable::CallError ce;
- wd.event_callback.callp((const Variant **)&eventp, 1, ret, ce);
+ wd.event_callback.call(event);
}
}
@@ -2586,12 +3120,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
if (in_dispatch_input_event) {
return;
}
-
in_dispatch_input_event = true;
- Variant ev = p_event;
- Variant *evp = &ev;
- Variant ret;
- Callable::CallError ce;
{
List<WindowID>::Element *E = popup_list.back();
@@ -2600,7 +3129,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
if (windows.has(E->get())) {
Callable callable = windows[E->get()].input_event_callback;
if (callable.is_valid()) {
- callable.callp((const Variant **)&evp, 1, ret, ce);
+ callable.call(p_event);
}
}
in_dispatch_input_event = false;
@@ -2614,7 +3143,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.callp((const Variant **)&evp, 1, ret, ce);
+ callable.call(p_event);
}
}
} else {
@@ -2622,7 +3151,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.callp((const Variant **)&evp, 1, ret, ce);
+ callable.call(p_event);
}
}
}
@@ -2829,15 +3358,45 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Process window messages.
switch (uMsg) {
+ case WM_CREATE: {
+ 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));
+ SendMessageW(windows[window_id].hWnd, WM_PAINT, 0, 0);
+ }
+ } break;
+ case WM_NCPAINT: {
+ if (RenderingServer::get_singleton() && (windows[window_id].borderless || (windows[window_id].fullscreen && windows[window_id].multiwindow_fs))) {
+ Color color = RenderingServer::get_singleton()->get_default_clear_color();
+ HDC hdc = GetWindowDC(hWnd);
+ if (hdc) {
+ HPEN pen = CreatePen(PS_SOLID, 1, RGB(color.r * 255.f, color.g * 255.f, color.b * 255.f));
+ if (pen) {
+ HGDIOBJ prev_pen = SelectObject(hdc, pen);
+ HGDIOBJ prev_brush = SelectObject(hdc, GetStockObject(NULL_BRUSH));
+
+ RECT rc;
+ GetWindowRect(hWnd, &rc);
+ OffsetRect(&rc, -rc.left, -rc.top);
+ Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
+
+ SelectObject(hdc, prev_pen);
+ SelectObject(hdc, prev_brush);
+ DeleteObject(pen);
+ }
+ ReleaseDC(hWnd, hdc);
+ }
+ return 0;
+ }
+ } break;
case WM_NCHITTEST: {
if (windows[window_id].mpass) {
return HTTRANSPARENT;
}
} break;
case WM_MOUSEACTIVATE: {
- if (windows[window_id].no_focus) {
- return MA_NOACTIVATEANDEAT; // Do not activate, and discard mouse messages.
- } else if (windows[window_id].is_popup) {
+ if (windows[window_id].no_focus || windows[window_id].is_popup) {
return MA_NOACTIVATE; // Do not activate, but process mouse messages.
}
} break;
@@ -2939,18 +3498,22 @@ 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, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
+ ::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) {
BOOL value = is_dark_mode();
- ::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
+ ::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));
}
} break;
case WM_SYSCOMMAND: // Intercept system commands.
@@ -2966,6 +3529,30 @@ 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) {
@@ -2989,7 +3576,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;
case WM_INPUT: {
- if (mouse_mode != MOUSE_MODE_CAPTURED || !use_raw_input) {
+ if (!use_raw_input) {
break;
}
@@ -3007,7 +3594,32 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
RAWINPUT *raw = (RAWINPUT *)lpb;
- if (raw->header.dwType == RIM_TYPEMOUSE) {
+ if (raw->header.dwType == RIM_TYPEKEYBOARD) {
+ if (raw->data.keyboard.VKey == VK_SHIFT) {
+ // If multiple Shifts are held down at the same time,
+ // Windows natively only sends a KEYUP for the last one to be released.
+ if (raw->data.keyboard.Flags & RI_KEY_BREAK) {
+ if (GetAsyncKeyState(VK_SHIFT) < 0) {
+ // A Shift is released, but another Shift is still held
+ ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
+
+ KeyEvent ke;
+ ke.shift = false;
+ ke.alt = alt_mem;
+ ke.control = control_mem;
+ ke.meta = meta_mem;
+ ke.uMsg = WM_KEYUP;
+ ke.window_id = window_id;
+
+ ke.wParam = VK_SHIFT;
+ // data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.
+ // Bit 30 -> key was previously down, bit 31 -> key is being released.
+ ke.lParam = raw->data.keyboard.MakeCode << 16 | 1 << 30 | 1 << 31;
+ key_event_buffer[key_event_pos++] = ke;
+ }
+ }
+ }
+ } else if (mouse_mode == MOUSE_MODE_CAPTURED && raw->header.dwType == RIM_TYPEMOUSE) {
Ref<InputEventMouseMotion> mm;
mm.instantiate();
@@ -3030,6 +3642,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));
@@ -3054,6 +3667,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);
@@ -3141,6 +3755,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;
@@ -3149,6 +3764,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) {
@@ -3288,6 +3904,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;
@@ -3296,6 +3913,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) {
@@ -3407,6 +4025,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;
@@ -3415,6 +4034,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;
@@ -3650,10 +4270,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
rect_changed = true;
}
-#if defined(VULKAN_ENABLED)
- if (context_vulkan && window.context_created) {
+#if defined(RD_ENABLED)
+ 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_vulkan->window_resize(window_id, window.width, window.height);
+ rendering_context->window_set_size(window_id, window.width, window.height);
}
#endif
}
@@ -3665,11 +4285,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if (rect_changed) {
if (!window.rect_changed_callback.is_null()) {
- Variant size = Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height);
- const Variant *args[] = { &size };
- Variant ret;
- Callable::CallError ce;
- window.rect_changed_callback.callp(args, 1, ret, ce);
+ window.rect_changed_callback.call(Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height));
}
// Update cursor clip region after window rect has changed.
@@ -3685,7 +4301,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Return here to prevent WM_MOVE and WM_SIZE from being sent
// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks
return 0;
-
} break;
case WM_ENTERSIZEMOVE: {
@@ -3886,11 +4501,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
if (files.size() && !windows[window_id].drop_files_callback.is_null()) {
- Variant v = files;
- Variant *vp = &v;
- Variant ret;
- Callable::CallError ce;
- windows[window_id].drop_files_callback.callp((const Variant **)&vp, 1, ret, ce);
+ windows[window_id].drop_files_callback.call(files);
}
} break;
default: {
@@ -4021,6 +4632,7 @@ void DisplayServerWindows::_process_key_events() {
}
Key key_label = keycode;
Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));
+ KeyLocation location = KeyMappingWindows::get_location((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));
static BYTE keyboard_state[256];
memset(keyboard_state, 0, 256);
@@ -4047,6 +4659,7 @@ void DisplayServerWindows::_process_key_events() {
}
k->set_keycode(keycode);
k->set_physical_keycode(physical_keycode);
+ k->set_location(location);
k->set_key_label(key_label);
if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {
@@ -4212,26 +4825,59 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
- ::DwmSetWindowAttribute(wd.hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
+ ::DwmSetWindowAttribute(wd.hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
+#ifdef RD_ENABLED
+ if (rendering_context) {
+ union {
+#ifdef VULKAN_ENABLED
+ RenderingContextDriverVulkanWindows::WindowPlatformData vulkan;
+#endif
+#ifdef D3D12_ENABLED
+ RenderingContextDriverD3D12::WindowPlatformData d3d12;
+#endif
+ } wpd;
#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) != OK) {
- memdelete(context_vulkan);
- context_vulkan = nullptr;
+ if (rendering_driver == "vulkan") {
+ wpd.vulkan.window = wd.hWnd;
+ wpd.vulkan.instance = hInstance;
+ }
+#endif
+#ifdef D3D12_ENABLED
+ if (rendering_driver == "d3d12") {
+ wpd.d3d12.window = wd.hWnd;
+ }
+#endif
+ 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, "Failed to create Vulkan Window.");
+ 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
#ifdef GLES3_ENABLED
- if (gl_manager) {
- if (gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) {
- memdelete(gl_manager);
- gl_manager = nullptr;
+ if (gl_manager_native) {
+ if (gl_manager_native->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) {
+ memdelete(gl_manager_native);
+ gl_manager_native = nullptr;
+ windows.erase(id);
+ ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");
+ }
+ window_set_vsync_mode(p_vsync_mode, id);
+ }
+
+ if (gl_manager_angle) {
+ if (gl_manager_angle->window_create(id, nullptr, wd.hWnd, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) {
+ memdelete(gl_manager_angle);
+ gl_manager_angle = nullptr;
windows.erase(id);
ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");
}
@@ -4321,6 +4967,7 @@ WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
// UXTheme API.
bool DisplayServerWindows::dark_title_available = false;
+bool DisplayServerWindows::use_legacy_dark_mode_before_20H1 = false;
bool DisplayServerWindows::ux_theme_available = false;
ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;
GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;
@@ -4332,6 +4979,69 @@ bool DisplayServerWindows::winink_available = false;
GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
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 = NULL; // to get the services
+ IWbemServices *wbemServices = NULL; // to get the class
+ IEnumWbemClassObject *iter = NULL;
+ IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.
+
+ HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);
+ if (hr != S_OK) {
+ return Vector2i();
+ }
+ BSTR resource_name = SysAllocString(L"root\\CIMV2");
+ hr = wbemLocator->ConnectServer(resource_name, NULL, NULL, NULL, 0, NULL, NULL, &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, NULL, &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, NULL, NULL);
+ 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,
@@ -4356,6 +5066,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();
}
@@ -4441,11 +5164,30 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
- if (os_ver.dwBuildNumber >= 22000) {
+ if (os_ver.dwBuildNumber >= 18363) {
dark_title_available = true;
+ if (os_ver.dwBuildNumber < 19041) {
+ use_legacy_dark_mode_before_20H1 = true;
+ }
}
}
+ // Note: Windows Ink API for pen input, available on Windows 8+ only.
+ // Note: DPI conversion API, available on Windows 8.1+ only.
+ HMODULE user32_lib = LoadLibraryW(L"user32.dll");
+ if (user32_lib) {
+ win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
+ win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
+ win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI");
+ win81p_PhysicalToLogicalPointForPerMonitorDPI = (PhysicalToLogicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "PhysicalToLogicalPointForPerMonitorDPI");
+
+ winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo;
+ }
+
+ if (winink_available) {
+ tablet_drivers.push_back("winink");
+ }
+
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
if (wintab_lib) {
@@ -4462,20 +5204,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
tablet_drivers.push_back("wintab");
}
- // Note: Windows Ink API for pen input, available on Windows 8+ only.
- // Note: DPI conversion API, available on Windows 8.1+ only.
- HMODULE user32_lib = LoadLibraryW(L"user32.dll");
- if (user32_lib) {
- win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
- win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
- win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI");
-
- winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo;
- }
-
- if (winink_available) {
- tablet_drivers.push_back("winink");
- }
+ tablet_drivers.push_back("dummy");
if (OS::get_singleton()->is_hidpi_allowed()) {
HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
@@ -4512,33 +5241,91 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
_register_raw_input_devices(INVALID_WINDOW_ID);
+#if defined(RD_ENABLED)
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
- context_vulkan = memnew(VulkanContextWindows);
- if (context_vulkan->initialize() != OK) {
- memdelete(context_vulkan);
- context_vulkan = nullptr;
+ rendering_context = memnew(RenderingContextDriverVulkanWindows);
+ }
+#endif
+#if defined(D3D12_ENABLED)
+ if (rendering_driver == "d3d12") {
+ rendering_context = memnew(RenderingContextDriverD3D12);
+ }
+#endif
+
+ if (rendering_context) {
+ if (rendering_context->initialize() != OK) {
+ memdelete(rendering_context);
+ rendering_context = nullptr;
r_error = ERR_UNAVAILABLE;
return;
}
}
#endif
- // Init context and rendering device
+// Init context and rendering device
#if defined(GLES3_ENABLED)
+#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
+ // There's no native OpenGL drivers on Windows for ARM, switch to ANGLE over DX.
+ if (rendering_driver == "opengl3") {
+ rendering_driver = "opengl3_angle";
+ }
+#else
+ 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")) {
+ 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;
+ }
+ }
+ }
+
+ if (force_angle || (gl_info["version"].operator int() < 30003)) {
+ WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE.");
+ rendering_driver = "opengl3_angle";
+ }
+ }
+#endif
+
if (rendering_driver == "opengl3") {
- GLManager_Windows::ContextType opengl_api_type = GLManager_Windows::GLES_3_0_COMPATIBLE;
+ gl_manager_native = memnew(GLManagerNative_Windows);
- gl_manager = memnew(GLManager_Windows(opengl_api_type));
+ if (gl_manager_native->initialize() != OK) {
+ memdelete(gl_manager_native);
+ gl_manager_native = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
- if (gl_manager->initialize() != OK) {
- memdelete(gl_manager);
- gl_manager = nullptr;
+ RasterizerGLES3::make_current(true);
+ }
+ if (rendering_driver == "opengl3_angle") {
+ gl_manager_angle = memnew(GLManagerANGLE_Windows);
+
+ if (gl_manager_angle->initialize() != OK) {
+ memdelete(gl_manager_angle);
+ gl_manager_angle = nullptr;
r_error = ERR_UNAVAILABLE;
return;
}
- RasterizerGLES3::make_current();
+ RasterizerGLES3::make_current(false);
}
#endif
@@ -4551,7 +5338,8 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (p_screen == SCREEN_OF_MAIN_WINDOW) {
p_screen = SCREEN_PRIMARY;
}
- window_position = screen_get_position(p_screen) + (screen_get_size(p_screen) - p_resolution) / 2;
+ Rect2i scr_rect = screen_get_usable_rect(p_screen);
+ window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2;
}
WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution));
@@ -4567,11 +5355,11 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
show_window(MAIN_WINDOW_ID);
-#if defined(VULKAN_ENABLED)
-
- if (rendering_driver == "vulkan") {
- rendering_device_vulkan = memnew(RenderingDeviceVulkan);
- rendering_device_vulkan->initialize(context_vulkan);
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ rendering_device = memnew(RenderingDevice);
+ rendering_device->initialize(rendering_context, MAIN_WINDOW_ID);
+ rendering_device->screen_create(MAIN_WINDOW_ID);
RendererCompositorRD::make_current();
}
@@ -4610,8 +5398,12 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
#ifdef VULKAN_ENABLED
drivers.push_back("vulkan");
#endif
+#ifdef D3D12_ENABLED
+ drivers.push_back("d3d12");
+#endif
#ifdef GLES3_ENABLED
drivers.push_back("opengl3");
+ drivers.push_back("opengl3_angle");
#endif
return drivers;
@@ -4630,6 +5422,16 @@ DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_drive
"If you have recently updated your video card drivers, try rebooting.",
executable_name),
"Unable to initialize Vulkan video driver");
+ } else if (p_rendering_driver == "d3d12") {
+ String executable_name = OS::get_singleton()->get_executable_path().get_file();
+ OS::get_singleton()->alert(
+ vformat("Your video card drivers seem not to support the required DirectX 12 version.\n\n"
+ "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"
+ "If you have recently updated your video card drivers, try rebooting.",
+ executable_name),
+ "Unable to initialize DirectX 12 video driver");
} else {
OS::get_singleton()->alert(
"Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
@@ -4651,6 +5453,18 @@ DisplayServerWindows::~DisplayServerWindows() {
cursors_cache.clear();
+ // Destroy all status indicators.
+ for (HashMap<IndicatorID, IndicatorData>::Iterator E = indicators.begin(); 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);
}
@@ -4668,9 +5482,13 @@ DisplayServerWindows::~DisplayServerWindows() {
#endif
if (windows.has(MAIN_WINDOW_ID)) {
-#ifdef VULKAN_ENABLED
- if (context_vulkan) {
- context_vulkan->window_destroy(MAIN_WINDOW_ID);
+#ifdef RD_ENABLED
+ 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) {
@@ -4680,16 +5498,15 @@ DisplayServerWindows::~DisplayServerWindows() {
DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
}
-#if defined(VULKAN_ENABLED)
- if (rendering_device_vulkan) {
- rendering_device_vulkan->finalize();
- memdelete(rendering_device_vulkan);
- rendering_device_vulkan = nullptr;
+#ifdef RD_ENABLED
+ if (rendering_device) {
+ memdelete(rendering_device);
+ rendering_device = nullptr;
}
- if (context_vulkan) {
- memdelete(context_vulkan);
- context_vulkan = nullptr;
+ if (rendering_context) {
+ memdelete(rendering_context);
+ rendering_context = nullptr;
}
#endif
@@ -4697,9 +5514,13 @@ DisplayServerWindows::~DisplayServerWindows() {
SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0);
}
#ifdef GLES3_ENABLED
- if (gl_manager) {
- memdelete(gl_manager);
- gl_manager = nullptr;
+ if (gl_manager_angle) {
+ memdelete(gl_manager_angle);
+ gl_manager_angle = nullptr;
+ }
+ if (gl_manager_native) {
+ memdelete(gl_manager_native);
+ gl_manager_native = nullptr;
}
#endif
if (tts) {