Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/platform/windows/display_server_windows.cpp
11352 views
1
/**************************************************************************/
2
/* display_server_windows.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "display_server_windows.h"
32
33
#include "drop_target_windows.h"
34
#include "os_windows.h"
35
#include "scene/main/window.h"
36
#include "wgl_detect_version.h"
37
38
#include "core/config/project_settings.h"
39
#include "core/io/file_access.h"
40
#include "core/io/marshalls.h"
41
#include "core/io/xml_parser.h"
42
#include "core/os/main_loop.h"
43
#include "core/version.h"
44
#include "drivers/png/png_driver_common.h"
45
#include "main/main.h"
46
#include "scene/resources/texture.h"
47
48
#ifdef SDL_ENABLED
49
#include "drivers/sdl/joypad_sdl.h"
50
#endif
51
52
#include "servers/rendering/dummy/rasterizer_dummy.h"
53
54
#if defined(VULKAN_ENABLED)
55
#include "rendering_context_driver_vulkan_windows.h"
56
#endif
57
#if defined(D3D12_ENABLED)
58
#include "drivers/d3d12/rendering_context_driver_d3d12.h"
59
#endif
60
#if defined(GLES3_ENABLED)
61
#include "drivers/gles3/rasterizer_gles3.h"
62
#endif
63
64
#if defined(ACCESSKIT_ENABLED)
65
#include "drivers/accesskit/accessibility_driver_accesskit.h"
66
#endif
67
68
#include <avrt.h>
69
#include <dwmapi.h>
70
#include <propkey.h>
71
#include <propvarutil.h>
72
#include <shellapi.h>
73
#include <shellscalingapi.h>
74
#include <shlwapi.h>
75
#include <shobjidl.h>
76
#include <wbemcli.h>
77
78
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
79
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
80
#endif
81
82
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
83
#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
84
#endif
85
86
#ifndef DWMWA_WINDOW_CORNER_PREFERENCE
87
#define DWMWA_WINDOW_CORNER_PREFERENCE 33
88
#endif
89
90
#ifndef DWMWCP_DEFAULT
91
#define DWMWCP_DEFAULT 0
92
#endif
93
94
#ifndef DWMWCP_DONOTROUND
95
#define DWMWCP_DONOTROUND 1
96
#endif
97
98
#define WM_INDICATOR_CALLBACK_MESSAGE (WM_USER + 1)
99
100
int constexpr FS_TRANSP_BORDER = 2;
101
102
static String format_error_message(DWORD id) {
103
LPWSTR messageBuffer = nullptr;
104
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
105
nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);
106
107
String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size);
108
109
LocalFree(messageBuffer);
110
111
return msg;
112
}
113
114
static void track_mouse_leave_event(HWND hWnd) {
115
TRACKMOUSEEVENT tme;
116
tme.cbSize = sizeof(TRACKMOUSEEVENT);
117
tme.dwFlags = TME_LEAVE;
118
tme.hwndTrack = hWnd;
119
tme.dwHoverTime = HOVER_DEFAULT;
120
TrackMouseEvent(&tme);
121
}
122
123
bool DisplayServerWindows::has_feature(Feature p_feature) const {
124
switch (p_feature) {
125
#ifndef DISABLE_DEPRECATED
126
case FEATURE_GLOBAL_MENU: {
127
return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));
128
} break;
129
#endif
130
case FEATURE_SUBWINDOWS:
131
case FEATURE_TOUCHSCREEN:
132
case FEATURE_MOUSE:
133
case FEATURE_MOUSE_WARP:
134
case FEATURE_CLIPBOARD:
135
case FEATURE_CURSOR_SHAPE:
136
case FEATURE_CUSTOM_CURSOR_SHAPE:
137
case FEATURE_IME:
138
case FEATURE_WINDOW_TRANSPARENCY:
139
case FEATURE_HIDPI:
140
case FEATURE_ICON:
141
case FEATURE_NATIVE_ICON:
142
case FEATURE_NATIVE_DIALOG:
143
case FEATURE_NATIVE_DIALOG_INPUT:
144
case FEATURE_NATIVE_DIALOG_FILE:
145
case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
146
//case FEATURE_NATIVE_DIALOG_FILE_MIME:
147
case FEATURE_SWAP_BUFFERS:
148
case FEATURE_KEEP_SCREEN_ON:
149
case FEATURE_TEXT_TO_SPEECH:
150
case FEATURE_SCREEN_CAPTURE:
151
case FEATURE_STATUS_INDICATOR:
152
case FEATURE_WINDOW_EMBEDDING:
153
case FEATURE_WINDOW_DRAG:
154
return true;
155
case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:
156
return (os_ver.dwBuildNumber >= 19041); // Fully supported on Windows 10 Vibranium R1 (2004)+ only, captured as black rect on older versions.
157
case FEATURE_EMOJI_AND_SYMBOL_PICKER:
158
return (os_ver.dwBuildNumber >= 17134); // Windows 10 Redstone 4 (1803)+ only.
159
#ifdef ACCESSKIT_ENABLED
160
case FEATURE_ACCESSIBILITY_SCREEN_READER: {
161
return (accessibility_driver != nullptr);
162
} break;
163
#endif
164
default:
165
return false;
166
}
167
}
168
169
String DisplayServerWindows::get_name() const {
170
return "Windows";
171
}
172
173
void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
174
if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
175
// Hide cursor before moving.
176
if (hCursor == nullptr) {
177
hCursor = SetCursor(nullptr);
178
} else {
179
SetCursor(nullptr);
180
}
181
}
182
183
if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
184
// Mouse is grabbed (captured or confined).
185
WindowID window_id = _get_focused_window_or_popup();
186
if (!windows.has(window_id)) {
187
window_id = MAIN_WINDOW_ID;
188
}
189
190
WindowData &wd = windows[window_id];
191
192
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
193
194
RECT clipRect;
195
GetClientRect(wd.hWnd, &clipRect);
196
clipRect.right -= off_x;
197
ClientToScreen(wd.hWnd, (POINT *)&clipRect.left);
198
ClientToScreen(wd.hWnd, (POINT *)&clipRect.right);
199
ClipCursor(&clipRect);
200
if (p_mode == MOUSE_MODE_CAPTURED) {
201
center = window_get_size() / 2;
202
POINT pos = { (int)center.x, (int)center.y };
203
ClientToScreen(wd.hWnd, &pos);
204
SetCursorPos(pos.x, pos.y);
205
SetCapture(wd.hWnd);
206
207
_register_raw_input_devices(window_id);
208
}
209
} else {
210
// Mouse is free to move around (not captured or confined).
211
// When the user is moving a window, it's important to not ReleaseCapture because it will cause
212
// the window movement to stop and if the user tries to move the Windows when it's not activated,
213
// it will prevent the window movement. It's probably impossible to move the Window while it's captured anyway.
214
if (!_has_moving_window()) {
215
ReleaseCapture();
216
}
217
ClipCursor(nullptr);
218
219
_register_raw_input_devices(INVALID_WINDOW_ID);
220
}
221
222
if (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED) {
223
// Show cursor.
224
CursorShape c = cursor_shape;
225
cursor_shape = CURSOR_MAX;
226
cursor_set_shape(c);
227
}
228
}
229
230
DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() const {
231
const List<WindowID>::Element *E = popup_list.back();
232
if (E) {
233
return E->get();
234
}
235
236
return last_focused_window;
237
}
238
239
bool DisplayServerWindows::_has_moving_window() const {
240
for (const KeyValue<WindowID, WindowData> &E : windows) {
241
if (E.value.move_timer_id) {
242
return true;
243
}
244
}
245
return false;
246
}
247
248
void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) {
249
use_raw_input = true;
250
251
RAWINPUTDEVICE rid[2] = {};
252
rid[0].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
253
rid[0].usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
254
rid[0].dwFlags = 0;
255
256
rid[1].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
257
rid[1].usUsage = 0x06; // HID_USAGE_GENERIC_KEYBOARD
258
rid[1].dwFlags = 0;
259
260
if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) {
261
// Follow the defined window
262
rid[0].hwndTarget = windows[p_target_window].hWnd;
263
rid[1].hwndTarget = windows[p_target_window].hWnd;
264
} else {
265
// Follow the keyboard focus
266
rid[0].hwndTarget = nullptr;
267
rid[1].hwndTarget = nullptr;
268
}
269
270
if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) {
271
// Registration failed.
272
use_raw_input = false;
273
}
274
}
275
276
void DisplayServerWindows::initialize_tts() const {
277
const_cast<DisplayServerWindows *>(this)->tts = memnew(TTS_Windows);
278
}
279
280
bool DisplayServerWindows::tts_is_speaking() const {
281
if (unlikely(!tts)) {
282
initialize_tts();
283
}
284
ERR_FAIL_NULL_V(tts, false);
285
return tts->is_speaking();
286
}
287
288
bool DisplayServerWindows::tts_is_paused() const {
289
if (unlikely(!tts)) {
290
initialize_tts();
291
}
292
ERR_FAIL_NULL_V(tts, false);
293
return tts->is_paused();
294
}
295
296
TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const {
297
if (unlikely(!tts)) {
298
initialize_tts();
299
}
300
ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());
301
return tts->get_voices();
302
}
303
304
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) {
305
if (unlikely(!tts)) {
306
initialize_tts();
307
}
308
ERR_FAIL_NULL(tts);
309
tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
310
}
311
312
void DisplayServerWindows::tts_pause() {
313
if (unlikely(!tts)) {
314
initialize_tts();
315
}
316
ERR_FAIL_NULL(tts);
317
tts->pause();
318
}
319
320
void DisplayServerWindows::tts_resume() {
321
if (unlikely(!tts)) {
322
initialize_tts();
323
}
324
ERR_FAIL_NULL(tts);
325
tts->resume();
326
}
327
328
void DisplayServerWindows::tts_stop() {
329
if (unlikely(!tts)) {
330
initialize_tts();
331
}
332
ERR_FAIL_NULL(tts);
333
tts->stop();
334
}
335
336
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, WindowID p_window_id) {
337
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, p_window_id);
338
}
339
340
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, WindowID p_window_id) {
341
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, p_window_id);
342
}
343
344
GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Wnon-virtual-dtor") // Silence warning due to a COM API weirdness.
345
346
class FileDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents {
347
LONG ref_count = 1;
348
int ctl_id = 1;
349
350
HashMap<int, String> ctls;
351
Dictionary selected;
352
String root;
353
354
public:
355
// IUnknown methods
356
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) {
357
static const QITAB qit[] = {
358
#ifdef __MINGW32__
359
{ &__uuidof(IFileDialogEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogEvents, FileDialogEventHandler)) },
360
{ &__uuidof(IFileDialogControlEvents), static_cast<decltype(qit[0].dwOffset)>(OFFSETOFCLASS(IFileDialogControlEvents, FileDialogEventHandler)) },
361
#else
362
QITABENT(FileDialogEventHandler, IFileDialogEvents),
363
QITABENT(FileDialogEventHandler, IFileDialogControlEvents),
364
#endif
365
{ nullptr, 0 },
366
};
367
return QISearch(this, qit, riid, ppv);
368
}
369
370
ULONG STDMETHODCALLTYPE AddRef() {
371
return InterlockedIncrement(&ref_count);
372
}
373
374
ULONG STDMETHODCALLTYPE Release() {
375
long ref = InterlockedDecrement(&ref_count);
376
if (!ref) {
377
delete this;
378
}
379
return ref;
380
}
381
382
// IFileDialogEvents methods
383
HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *) { return S_OK; }
384
HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialog *) { return S_OK; }
385
386
HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialog *p_pfd, IShellItem *p_item) {
387
if (root.is_empty()) {
388
return S_OK;
389
}
390
391
LPWSTR lpw_path = nullptr;
392
p_item->GetDisplayName(SIGDN_FILESYSPATH, &lpw_path);
393
if (!lpw_path) {
394
return S_FALSE;
395
}
396
String path = String::utf16((const char16_t *)lpw_path).replace_char('\\', '/').trim_prefix(R"(\\?\)").simplify_path();
397
if (!path.begins_with(root.simplify_path())) {
398
return S_FALSE;
399
}
400
return S_OK;
401
}
402
403
HRESULT STDMETHODCALLTYPE OnHelp(IFileDialog *) { return S_OK; }
404
HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialog *) { return S_OK; }
405
HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; }
406
HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialog *pfd) { return S_OK; }
407
HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; }
408
409
// IFileDialogControlEvents methods
410
HRESULT STDMETHODCALLTYPE OnItemSelected(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, DWORD p_item_idx) {
411
if (ctls.has(p_ctl_id)) {
412
selected[ctls[p_ctl_id]] = (int)p_item_idx;
413
}
414
return S_OK;
415
}
416
417
HRESULT STDMETHODCALLTYPE OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; }
418
HRESULT STDMETHODCALLTYPE OnCheckButtonToggled(IFileDialogCustomize *p_pfdc, DWORD p_ctl_id, BOOL p_checked) {
419
if (ctls.has(p_ctl_id)) {
420
selected[ctls[p_ctl_id]] = (bool)p_checked;
421
}
422
return S_OK;
423
}
424
HRESULT STDMETHODCALLTYPE OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; }
425
426
Dictionary get_selected() {
427
return selected;
428
}
429
430
void set_root(const String &p_root) {
431
root = p_root;
432
}
433
434
void add_option(IFileDialogCustomize *p_pfdc, const String &p_name, const Vector<String> &p_options, int p_default) {
435
int gid = ctl_id++;
436
int cid = ctl_id++;
437
438
if (p_options.is_empty()) {
439
// Add check box.
440
p_pfdc->StartVisualGroup(gid, L"");
441
p_pfdc->AddCheckButton(cid, (LPCWSTR)p_name.utf16().get_data(), p_default);
442
p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);
443
p_pfdc->EndVisualGroup();
444
selected[p_name] = (bool)p_default;
445
} else {
446
// Add combo box.
447
p_pfdc->StartVisualGroup(gid, (LPCWSTR)p_name.utf16().get_data());
448
p_pfdc->AddComboBox(cid);
449
p_pfdc->SetControlState(cid, CDCS_VISIBLE | CDCS_ENABLED);
450
for (int i = 0; i < p_options.size(); i++) {
451
p_pfdc->AddControlItem(cid, i, (LPCWSTR)p_options[i].utf16().get_data());
452
}
453
p_pfdc->SetSelectedControlItem(cid, p_default);
454
p_pfdc->EndVisualGroup();
455
selected[p_name] = p_default;
456
}
457
ctls[cid] = p_name;
458
}
459
460
virtual ~FileDialogEventHandler() {}
461
};
462
463
GODOT_GCC_WARNING_POP
464
465
LRESULT CALLBACK WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
466
DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());
467
if (ds_win) {
468
return ds_win->WndProcFileDialog(hWnd, uMsg, wParam, lParam);
469
} else {
470
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
471
}
472
}
473
474
LRESULT DisplayServerWindows::WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
475
MutexLock lock(file_dialog_mutex);
476
if (file_dialog_wnd.has(hWnd)) {
477
if (file_dialog_wnd[hWnd]->close_requested.is_set()) {
478
IPropertyStore *prop_store;
479
HRESULT hr = SHGetPropertyStoreForWindow(hWnd, IID_IPropertyStore, (void **)&prop_store);
480
if (hr == S_OK) {
481
PROPVARIANT val;
482
PropVariantInit(&val);
483
prop_store->SetValue(PKEY_AppUserModel_ID, val);
484
prop_store->Release();
485
}
486
DestroyWindow(hWnd);
487
file_dialog_wnd.erase(hWnd);
488
}
489
}
490
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
491
}
492
493
void DisplayServerWindows::_thread_fd_monitor(void *p_ud) {
494
DisplayServerWindows *ds = static_cast<DisplayServerWindows *>(get_singleton());
495
FileDialogData *fd = (FileDialogData *)p_ud;
496
497
if (fd->mode < 0 && fd->mode >= DisplayServer::FILE_DIALOG_MODE_SAVE_MAX) {
498
fd->finished.set();
499
return;
500
}
501
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
502
503
int64_t x = fd->wrect.position.x;
504
int64_t y = fd->wrect.position.y;
505
int64_t w = fd->wrect.size.x;
506
int64_t h = fd->wrect.size.y;
507
508
WNDCLASSW wc = {};
509
wc.lpfnWndProc = (WNDPROC)::WndProcFileDialog;
510
wc.hInstance = GetModuleHandle(nullptr);
511
wc.lpszClassName = L"Engine File Dialog";
512
RegisterClassW(&wc);
513
514
HWND hwnd_dialog = CreateWindowExW(WS_EX_APPWINDOW, L"Engine File Dialog", L"", WS_OVERLAPPEDWINDOW, x, y, w, h, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
515
if (hwnd_dialog) {
516
{
517
MutexLock lock(ds->file_dialog_mutex);
518
ds->file_dialog_wnd[hwnd_dialog] = fd;
519
}
520
521
HICON mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_SMALL, 0);
522
if (mainwindow_icon) {
523
SendMessage(hwnd_dialog, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);
524
}
525
mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_BIG, 0);
526
if (mainwindow_icon) {
527
SendMessage(hwnd_dialog, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);
528
}
529
IPropertyStore *prop_store;
530
HRESULT hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);
531
if (hr == S_OK) {
532
PROPVARIANT val;
533
InitPropVariantFromString((PCWSTR)fd->appid.utf16().get_data(), &val);
534
prop_store->SetValue(PKEY_AppUserModel_ID, val);
535
prop_store->Release();
536
}
537
}
538
539
SetCurrentProcessExplicitAppUserModelID((PCWSTR)fd->appid.utf16().get_data());
540
541
Vector<Char16String> filter_names;
542
Vector<Char16String> filter_exts;
543
for (const String &E : fd->filters) {
544
Vector<String> tokens = E.split(";");
545
if (tokens.size() >= 1) {
546
String flt = tokens[0].strip_edges();
547
int filter_slice_count = flt.get_slice_count(",");
548
Vector<String> exts;
549
for (int j = 0; j < filter_slice_count; j++) {
550
String str = (flt.get_slicec(',', j).strip_edges());
551
if (!str.is_empty()) {
552
exts.push_back(str);
553
}
554
}
555
if (!exts.is_empty()) {
556
String str = String(";").join(exts);
557
filter_exts.push_back(str.utf16());
558
if (tokens.size() == 2) {
559
filter_names.push_back(tokens[1].strip_edges().utf16());
560
} else {
561
filter_names.push_back(str.utf16());
562
}
563
}
564
}
565
}
566
if (filter_names.is_empty()) {
567
filter_exts.push_back(String("*.*").utf16());
568
filter_names.push_back((RTR("All Files") + " (*.*)").utf16());
569
}
570
571
Vector<COMDLG_FILTERSPEC> filters;
572
for (int i = 0; i < filter_names.size(); i++) {
573
filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });
574
}
575
576
HRESULT hr = S_OK;
577
IFileDialog *pfd = nullptr;
578
if (fd->mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
579
hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd);
580
} else {
581
hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd);
582
}
583
if (SUCCEEDED(hr)) {
584
IFileDialogEvents *pfde = nullptr;
585
FileDialogEventHandler *event_handler = new FileDialogEventHandler();
586
hr = event_handler->QueryInterface(IID_PPV_ARGS(&pfde));
587
588
DWORD cookie = 0;
589
hr = pfd->Advise(pfde, &cookie);
590
591
IFileDialogCustomize *pfdc = nullptr;
592
hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));
593
594
for (int i = 0; i < fd->options.size(); i++) {
595
const Dictionary &item = fd->options[i];
596
if (!item.has("name") || !item.has("values") || !item.has("default")) {
597
continue;
598
}
599
event_handler->add_option(pfdc, item["name"], item["values"], item["default"]);
600
}
601
event_handler->set_root(fd->root);
602
603
pfdc->Release();
604
605
DWORD flags;
606
pfd->GetOptions(&flags);
607
if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {
608
flags |= FOS_ALLOWMULTISELECT;
609
}
610
if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR) {
611
flags |= FOS_PICKFOLDERS;
612
}
613
if (fd->show_hidden) {
614
flags |= FOS_FORCESHOWHIDDEN;
615
}
616
pfd->SetOptions(flags | FOS_FORCEFILESYSTEM);
617
pfd->SetTitle((LPCWSTR)fd->title.utf16().get_data());
618
619
String dir = ProjectSettings::get_singleton()->globalize_path(fd->current_directory);
620
if (dir == ".") {
621
dir = OS::get_singleton()->get_executable_path().get_base_dir();
622
}
623
if (dir.is_relative_path() || dir == ".") {
624
Char16String current_dir_name;
625
size_t str_len = GetCurrentDirectoryW(0, nullptr);
626
current_dir_name.resize_uninitialized(str_len + 1);
627
GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw());
628
if (dir == ".") {
629
dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/');
630
} else {
631
dir = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/').path_join(dir);
632
}
633
}
634
dir = dir.simplify_path();
635
dir = dir.trim_prefix(R"(\\?\)").replace_char('/', '\\');
636
637
IShellItem *shellitem = nullptr;
638
hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem);
639
if (SUCCEEDED(hr)) {
640
pfd->SetDefaultFolder(shellitem);
641
pfd->SetFolder(shellitem);
642
}
643
644
pfd->SetFileName((LPCWSTR)fd->filename.utf16().get_data());
645
pfd->SetFileTypes(filters.size(), filters.ptr());
646
pfd->SetFileTypeIndex(0);
647
648
hr = pfd->Show(hwnd_dialog);
649
pfd->Unadvise(cookie);
650
651
Dictionary options = event_handler->get_selected();
652
653
pfde->Release();
654
event_handler->Release();
655
656
UINT index = 0;
657
pfd->GetFileTypeIndex(&index);
658
if (index > 0) {
659
index = index - 1;
660
}
661
662
if (SUCCEEDED(hr)) {
663
Vector<String> file_names;
664
665
if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {
666
IShellItemArray *results;
667
hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results);
668
if (SUCCEEDED(hr)) {
669
DWORD count = 0;
670
results->GetCount(&count);
671
for (DWORD i = 0; i < count; i++) {
672
IShellItem *result;
673
results->GetItemAt(i, &result);
674
675
PWSTR file_path = nullptr;
676
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
677
if (SUCCEEDED(hr)) {
678
file_names.push_back(String::utf16((const char16_t *)file_path).replace_char('\\', '/').trim_prefix(R"(\\?\)"));
679
CoTaskMemFree(file_path);
680
}
681
result->Release();
682
}
683
results->Release();
684
}
685
} else {
686
IShellItem *result;
687
hr = pfd->GetResult(&result);
688
if (SUCCEEDED(hr)) {
689
PWSTR file_path = nullptr;
690
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
691
if (SUCCEEDED(hr)) {
692
file_names.push_back(String::utf16((const char16_t *)file_path).replace_char('\\', '/').trim_prefix(R"(\\?\)"));
693
CoTaskMemFree(file_path);
694
}
695
result->Release();
696
}
697
}
698
if (fd->callback.is_valid()) {
699
MutexLock lock(ds->file_dialog_mutex);
700
FileDialogCallback cb;
701
cb.callback = fd->callback;
702
cb.status = true;
703
cb.files = file_names;
704
cb.index = index;
705
cb.options = options;
706
cb.opt_in_cb = fd->options_in_cb;
707
ds->pending_cbs.push_back(cb);
708
}
709
} else {
710
if (fd->callback.is_valid()) {
711
MutexLock lock(ds->file_dialog_mutex);
712
FileDialogCallback cb;
713
cb.callback = fd->callback;
714
cb.status = false;
715
cb.files = Vector<String>();
716
cb.index = index;
717
cb.options = options;
718
cb.opt_in_cb = fd->options_in_cb;
719
ds->pending_cbs.push_back(cb);
720
}
721
}
722
pfd->Release();
723
} else {
724
if (fd->callback.is_valid()) {
725
MutexLock lock(ds->file_dialog_mutex);
726
FileDialogCallback cb;
727
cb.callback = fd->callback;
728
cb.status = false;
729
cb.files = Vector<String>();
730
cb.index = 0;
731
cb.options = Dictionary();
732
cb.opt_in_cb = fd->options_in_cb;
733
ds->pending_cbs.push_back(cb);
734
}
735
}
736
{
737
MutexLock lock(ds->file_dialog_mutex);
738
if (hwnd_dialog && ds->file_dialog_wnd.has(hwnd_dialog)) {
739
IPropertyStore *prop_store;
740
hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);
741
if (hr == S_OK) {
742
PROPVARIANT val;
743
PropVariantInit(&val);
744
prop_store->SetValue(PKEY_AppUserModel_ID, val);
745
prop_store->Release();
746
}
747
DestroyWindow(hwnd_dialog);
748
ds->file_dialog_wnd.erase(hwnd_dialog);
749
}
750
}
751
UnregisterClassW(L"Engine File Dialog", GetModuleHandle(nullptr));
752
CoUninitialize();
753
754
fd->finished.set();
755
756
if (fd->window_id != INVALID_WINDOW_ID) {
757
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd->window_id);
758
}
759
}
760
761
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, WindowID p_window_id) {
762
_THREAD_SAFE_METHOD_
763
764
ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);
765
766
String appname;
767
if (Engine::get_singleton()->is_editor_hint()) {
768
appname = "Godot.GodotEditor." + String(GODOT_VERSION_BRANCH);
769
} else {
770
String name = GLOBAL_GET("application/config/name");
771
String version = GLOBAL_GET("application/config/version");
772
if (version.is_empty()) {
773
version = "0";
774
}
775
String clean_app_name = name.to_pascal_case();
776
for (int i = 0; i < clean_app_name.length(); i++) {
777
if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {
778
clean_app_name[i] = '_';
779
}
780
}
781
clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");
782
appname = "Godot." + clean_app_name + "." + version;
783
}
784
785
FileDialogData *fd = memnew(FileDialogData);
786
if (windows.has(p_window_id) && !windows[p_window_id].is_popup) {
787
fd->hwnd_owner = windows[p_window_id].hWnd;
788
RECT crect;
789
GetWindowRect(fd->hwnd_owner, &crect);
790
fd->wrect = Rect2i(crect.left, crect.top, crect.right - crect.left, crect.bottom - crect.top);
791
} else {
792
fd->hwnd_owner = nullptr;
793
fd->wrect = Rect2i(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT);
794
}
795
fd->appid = appname;
796
fd->title = p_title;
797
fd->current_directory = p_current_directory;
798
fd->root = p_root;
799
fd->filename = p_filename;
800
fd->show_hidden = p_show_hidden;
801
fd->mode = p_mode;
802
fd->window_id = p_window_id;
803
fd->filters = p_filters;
804
fd->options = p_options;
805
fd->callback = p_callback;
806
fd->options_in_cb = p_options_in_cb;
807
fd->finished.clear();
808
fd->close_requested.clear();
809
810
fd->listener_thread.start(DisplayServerWindows::_thread_fd_monitor, fd);
811
812
file_dialogs.push_back(fd);
813
814
return OK;
815
}
816
817
void DisplayServerWindows::process_file_dialog_callbacks() {
818
MutexLock lock(file_dialog_mutex);
819
while (!pending_cbs.is_empty()) {
820
FileDialogCallback cb = pending_cbs.front()->get();
821
pending_cbs.pop_front();
822
823
if (cb.opt_in_cb) {
824
Variant ret;
825
Callable::CallError ce;
826
const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options };
827
828
cb.callback.callp(args, 4, ret, ce);
829
if (ce.error != Callable::CallError::CALL_OK) {
830
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce)));
831
}
832
} else {
833
Variant ret;
834
Callable::CallError ce;
835
const Variant *args[3] = { &cb.status, &cb.files, &cb.index };
836
837
cb.callback.callp(args, 3, ret, ce);
838
if (ce.error != Callable::CallError::CALL_OK) {
839
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce)));
840
}
841
}
842
}
843
}
844
845
void DisplayServerWindows::beep() const {
846
MessageBeep(MB_OK);
847
}
848
849
void DisplayServerWindows::_mouse_update_mode() {
850
_THREAD_SAFE_METHOD_
851
852
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
853
? mouse_mode_override
854
: mouse_mode_base;
855
856
if (mouse_mode == wanted_mouse_mode) {
857
// Already in the same mode; do nothing.
858
return;
859
}
860
861
mouse_mode = wanted_mouse_mode;
862
863
_set_mouse_mode_impl(wanted_mouse_mode);
864
}
865
866
void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
867
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
868
if (p_mode == mouse_mode_base) {
869
return;
870
}
871
mouse_mode_base = p_mode;
872
_mouse_update_mode();
873
}
874
875
DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {
876
return mouse_mode;
877
}
878
879
void DisplayServerWindows::mouse_set_mode_override(MouseMode p_mode) {
880
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
881
if (p_mode == mouse_mode_override) {
882
return;
883
}
884
mouse_mode_override = p_mode;
885
_mouse_update_mode();
886
}
887
888
DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode_override() const {
889
return mouse_mode_override;
890
}
891
892
void DisplayServerWindows::mouse_set_mode_override_enabled(bool p_override_enabled) {
893
if (p_override_enabled == mouse_mode_override_enabled) {
894
return;
895
}
896
mouse_mode_override_enabled = p_override_enabled;
897
_mouse_update_mode();
898
}
899
900
bool DisplayServerWindows::mouse_is_mode_override_enabled() const {
901
return mouse_mode_override_enabled;
902
}
903
904
void DisplayServerWindows::warp_mouse(const Point2i &p_position) {
905
_THREAD_SAFE_METHOD_
906
907
WindowID window_id = _get_focused_window_or_popup();
908
909
if (!windows.has(window_id)) {
910
return; // No focused window?
911
}
912
913
if (mouse_mode == MOUSE_MODE_CAPTURED) {
914
old_x = p_position.x;
915
old_y = p_position.y;
916
} else {
917
POINT p;
918
p.x = p_position.x;
919
p.y = p_position.y;
920
ClientToScreen(windows[window_id].hWnd, &p);
921
922
SetCursorPos(p.x, p.y);
923
}
924
}
925
926
Point2i DisplayServerWindows::mouse_get_position() const {
927
POINT p;
928
GetCursorPos(&p);
929
return Point2i(p.x, p.y) - _get_screens_origin();
930
}
931
932
BitField<MouseButtonMask> DisplayServerWindows::mouse_get_button_state() const {
933
BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;
934
935
if (GetKeyState(VK_LBUTTON) & (1 << 15)) {
936
last_button_state.set_flag(MouseButtonMask::LEFT);
937
}
938
if (GetKeyState(VK_RBUTTON) & (1 << 15)) {
939
last_button_state.set_flag(MouseButtonMask::RIGHT);
940
}
941
if (GetKeyState(VK_MBUTTON) & (1 << 15)) {
942
last_button_state.set_flag(MouseButtonMask::MIDDLE);
943
}
944
if (GetKeyState(VK_XBUTTON1) & (1 << 15)) {
945
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);
946
}
947
if (GetKeyState(VK_XBUTTON2) & (1 << 15)) {
948
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);
949
}
950
951
return last_button_state;
952
}
953
954
void DisplayServerWindows::clipboard_set(const String &p_text) {
955
_THREAD_SAFE_METHOD_
956
957
if (!windows.has(MAIN_WINDOW_ID)) {
958
return;
959
}
960
961
// Convert LF line endings to CRLF in clipboard content.
962
// Otherwise, line endings won't be visible when pasted in other software.
963
String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // Avoid \r\r\n.
964
965
if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {
966
ERR_FAIL_MSG("Unable to open clipboard.");
967
}
968
EmptyClipboard();
969
970
Char16String utf16 = text.utf16();
971
HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR));
972
ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");
973
974
LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);
975
memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR));
976
GlobalUnlock(mem);
977
978
SetClipboardData(CF_UNICODETEXT, mem);
979
980
// Set the CF_TEXT version (not needed?).
981
CharString utf8 = text.utf8();
982
mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);
983
ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents.");
984
985
LPTSTR ptr = (LPTSTR)GlobalLock(mem);
986
memcpy(ptr, utf8.get_data(), utf8.length());
987
ptr[utf8.length()] = 0;
988
GlobalUnlock(mem);
989
990
SetClipboardData(CF_TEXT, mem);
991
992
CloseClipboard();
993
}
994
995
String DisplayServerWindows::clipboard_get() const {
996
_THREAD_SAFE_METHOD_
997
998
if (!windows.has(MAIN_WINDOW_ID)) {
999
return String();
1000
}
1001
1002
String ret;
1003
if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {
1004
ERR_FAIL_V_MSG("", "Unable to open clipboard.");
1005
}
1006
1007
if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
1008
HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
1009
if (mem != nullptr) {
1010
LPWSTR ptr = (LPWSTR)GlobalLock(mem);
1011
if (ptr != nullptr) {
1012
ret = String::utf16((const char16_t *)ptr);
1013
GlobalUnlock(mem);
1014
}
1015
}
1016
1017
} else if (IsClipboardFormatAvailable(CF_TEXT)) {
1018
HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
1019
if (mem != nullptr) {
1020
LPTSTR ptr = (LPTSTR)GlobalLock(mem);
1021
if (ptr != nullptr) {
1022
ret.append_utf8((const char *)ptr);
1023
GlobalUnlock(mem);
1024
}
1025
}
1026
}
1027
1028
CloseClipboard();
1029
1030
return ret;
1031
}
1032
1033
Ref<Image> DisplayServerWindows::clipboard_get_image() const {
1034
Ref<Image> image;
1035
if (!windows.has(last_focused_window)) {
1036
return image; // No focused window?
1037
}
1038
if (!OpenClipboard(windows[last_focused_window].hWnd)) {
1039
ERR_FAIL_V_MSG(image, "Unable to open clipboard.");
1040
}
1041
UINT png_format = RegisterClipboardFormatA("PNG");
1042
if (png_format && IsClipboardFormatAvailable(png_format)) {
1043
HANDLE png_handle = GetClipboardData(png_format);
1044
if (png_handle) {
1045
size_t png_size = GlobalSize(png_handle);
1046
uint8_t *png_data = (uint8_t *)GlobalLock(png_handle);
1047
image.instantiate();
1048
1049
PNGDriverCommon::png_to_image(png_data, png_size, false, image);
1050
1051
GlobalUnlock(png_handle);
1052
}
1053
} else if (IsClipboardFormatAvailable(CF_DIB)) {
1054
HGLOBAL mem = GetClipboardData(CF_DIB);
1055
if (mem != nullptr) {
1056
BITMAPINFO *ptr = static_cast<BITMAPINFO *>(GlobalLock(mem));
1057
1058
if (ptr != nullptr) {
1059
BITMAPINFOHEADER *info = &ptr->bmiHeader;
1060
void *dib_bits = (void *)(ptr->bmiColors);
1061
1062
// Draw DIB image to temporary DC surface and read it back as BGRA8.
1063
HDC dc = GetDC(nullptr);
1064
if (dc) {
1065
HDC hdc = CreateCompatibleDC(dc);
1066
if (hdc) {
1067
HBITMAP hbm = CreateCompatibleBitmap(dc, info->biWidth, std::abs(info->biHeight));
1068
if (hbm) {
1069
SelectObject(hdc, hbm);
1070
SetDIBitsToDevice(hdc, 0, 0, info->biWidth, std::abs(info->biHeight), 0, 0, 0, std::abs(info->biHeight), dib_bits, ptr, DIB_RGB_COLORS);
1071
1072
BITMAPINFO bmp_info = {};
1073
bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
1074
bmp_info.bmiHeader.biWidth = info->biWidth;
1075
bmp_info.bmiHeader.biHeight = -std::abs(info->biHeight);
1076
bmp_info.bmiHeader.biPlanes = 1;
1077
bmp_info.bmiHeader.biBitCount = 32;
1078
bmp_info.bmiHeader.biCompression = BI_RGB;
1079
1080
Vector<uint8_t> img_data;
1081
img_data.resize(info->biWidth * std::abs(info->biHeight) * 4);
1082
GetDIBits(hdc, hbm, 0, std::abs(info->biHeight), img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);
1083
1084
uint8_t *wr = (uint8_t *)img_data.ptrw();
1085
for (int i = 0; i < info->biWidth * std::abs(info->biHeight); i++) {
1086
SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.
1087
if (info->biBitCount != 32) {
1088
wr[i * 4 + 3] = 255; // Set A to solid if it's not in the source image.
1089
}
1090
}
1091
image = Image::create_from_data(info->biWidth, std::abs(info->biHeight), false, Image::Format::FORMAT_RGBA8, img_data);
1092
1093
DeleteObject(hbm);
1094
}
1095
DeleteDC(hdc);
1096
}
1097
ReleaseDC(nullptr, dc);
1098
}
1099
GlobalUnlock(mem);
1100
}
1101
}
1102
}
1103
CloseClipboard();
1104
1105
return image;
1106
}
1107
1108
bool DisplayServerWindows::clipboard_has() const {
1109
return (IsClipboardFormatAvailable(CF_TEXT) ||
1110
IsClipboardFormatAvailable(CF_UNICODETEXT) ||
1111
IsClipboardFormatAvailable(CF_OEMTEXT));
1112
}
1113
1114
bool DisplayServerWindows::clipboard_has_image() const {
1115
UINT png_format = RegisterClipboardFormatA("PNG");
1116
return ((png_format && IsClipboardFormatAvailable(png_format)) || IsClipboardFormatAvailable(CF_DIB));
1117
}
1118
1119
typedef struct {
1120
int count;
1121
int screen;
1122
HMONITOR monitor;
1123
} EnumScreenData;
1124
1125
static BOOL CALLBACK _MonitorEnumProcPrim(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1126
EnumScreenData *data = (EnumScreenData *)dwData;
1127
if ((lprcMonitor->left == 0) && (lprcMonitor->top == 0)) {
1128
data->screen = data->count;
1129
return FALSE;
1130
}
1131
1132
data->count++;
1133
return TRUE;
1134
}
1135
1136
static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1137
EnumScreenData *data = (EnumScreenData *)dwData;
1138
if (data->monitor == hMonitor) {
1139
data->screen = data->count;
1140
}
1141
1142
data->count++;
1143
return TRUE;
1144
}
1145
1146
static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1147
int *data = (int *)dwData;
1148
(*data)++;
1149
return TRUE;
1150
}
1151
1152
int DisplayServerWindows::get_screen_count() const {
1153
_THREAD_SAFE_METHOD_
1154
1155
int data = 0;
1156
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcCount, (LPARAM)&data);
1157
return data;
1158
}
1159
1160
int DisplayServerWindows::get_primary_screen() const {
1161
EnumScreenData data = { 0, 0, nullptr };
1162
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPrim, (LPARAM)&data);
1163
return data.screen;
1164
}
1165
1166
int DisplayServerWindows::get_keyboard_focus_screen() const {
1167
HWND hwnd = GetForegroundWindow();
1168
if (hwnd) {
1169
EnumScreenData data = { 0, 0, MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) };
1170
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);
1171
return data.screen;
1172
} else {
1173
return get_primary_screen();
1174
}
1175
}
1176
1177
typedef struct {
1178
int count;
1179
int screen;
1180
Point2 pos;
1181
} EnumPosData;
1182
1183
static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1184
EnumPosData *data = (EnumPosData *)dwData;
1185
if (data->count == data->screen) {
1186
data->pos.x = lprcMonitor->left;
1187
data->pos.y = lprcMonitor->top;
1188
}
1189
1190
data->count++;
1191
return TRUE;
1192
}
1193
1194
static BOOL CALLBACK _MonitorEnumProcOrigin(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1195
EnumPosData *data = (EnumPosData *)dwData;
1196
data->pos = data->pos.min(Point2(lprcMonitor->left, lprcMonitor->top));
1197
1198
return TRUE;
1199
}
1200
1201
Point2i DisplayServerWindows::_get_screens_origin() const {
1202
_THREAD_SAFE_METHOD_
1203
1204
EnumPosData data = { 0, 0, Point2() };
1205
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcOrigin, (LPARAM)&data);
1206
return data.pos;
1207
}
1208
1209
Point2i DisplayServerWindows::screen_get_position(int p_screen) const {
1210
_THREAD_SAFE_METHOD_
1211
1212
p_screen = _get_screen_index(p_screen);
1213
int screen_count = get_screen_count();
1214
ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());
1215
1216
EnumPosData data = { 0, p_screen, Point2() };
1217
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPos, (LPARAM)&data);
1218
return data.pos - _get_screens_origin();
1219
}
1220
1221
typedef struct {
1222
int count;
1223
int screen;
1224
Size2 size;
1225
} EnumSizeData;
1226
1227
typedef struct {
1228
int count;
1229
int screen;
1230
Rect2i rect;
1231
} EnumRectData;
1232
1233
typedef struct {
1234
Vector<DISPLAYCONFIG_PATH_INFO> paths;
1235
Vector<DISPLAYCONFIG_MODE_INFO> modes;
1236
int count;
1237
int screen;
1238
float rate;
1239
} EnumRefreshRateData;
1240
1241
static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1242
EnumSizeData *data = (EnumSizeData *)dwData;
1243
if (data->count == data->screen) {
1244
data->size.x = lprcMonitor->right - lprcMonitor->left;
1245
data->size.y = lprcMonitor->bottom - lprcMonitor->top;
1246
}
1247
1248
data->count++;
1249
return TRUE;
1250
}
1251
1252
Size2i DisplayServerWindows::screen_get_size(int p_screen) const {
1253
_THREAD_SAFE_METHOD_
1254
1255
p_screen = _get_screen_index(p_screen);
1256
int screen_count = get_screen_count();
1257
ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());
1258
1259
EnumSizeData data = { 0, p_screen, Size2() };
1260
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcSize, (LPARAM)&data);
1261
return data.size;
1262
}
1263
1264
static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1265
EnumRectData *data = (EnumRectData *)dwData;
1266
if (data->count == data->screen) {
1267
MONITORINFO minfo;
1268
memset(&minfo, 0, sizeof(MONITORINFO));
1269
minfo.cbSize = sizeof(MONITORINFO);
1270
GetMonitorInfoA(hMonitor, &minfo);
1271
1272
data->rect.position.x = minfo.rcWork.left;
1273
data->rect.position.y = minfo.rcWork.top;
1274
data->rect.size.x = minfo.rcWork.right - minfo.rcWork.left;
1275
data->rect.size.y = minfo.rcWork.bottom - minfo.rcWork.top;
1276
}
1277
1278
data->count++;
1279
return TRUE;
1280
}
1281
1282
static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1283
EnumRefreshRateData *data = (EnumRefreshRateData *)dwData;
1284
if (data->count == data->screen) {
1285
MONITORINFOEXW minfo;
1286
memset(&minfo, 0, sizeof(minfo));
1287
minfo.cbSize = sizeof(minfo);
1288
GetMonitorInfoW(hMonitor, &minfo);
1289
1290
bool found = false;
1291
for (const DISPLAYCONFIG_PATH_INFO &path : data->paths) {
1292
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name;
1293
memset(&source_name, 0, sizeof(source_name));
1294
source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
1295
source_name.header.size = sizeof(source_name);
1296
source_name.header.adapterId = path.sourceInfo.adapterId;
1297
source_name.header.id = path.sourceInfo.id;
1298
if (DisplayConfigGetDeviceInfo(&source_name.header) == ERROR_SUCCESS) {
1299
if (wcscmp(minfo.szDevice, source_name.viewGdiDeviceName) == 0 && path.targetInfo.refreshRate.Numerator != 0 && path.targetInfo.refreshRate.Denominator != 0) {
1300
data->rate = (double)path.targetInfo.refreshRate.Numerator / (double)path.targetInfo.refreshRate.Denominator;
1301
found = true;
1302
break;
1303
}
1304
}
1305
}
1306
if (!found) {
1307
DEVMODEW dm;
1308
memset(&dm, 0, sizeof(dm));
1309
dm.dmSize = sizeof(dm);
1310
EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);
1311
1312
data->rate = dm.dmDisplayFrequency;
1313
}
1314
}
1315
1316
data->count++;
1317
return TRUE;
1318
}
1319
1320
Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const {
1321
_THREAD_SAFE_METHOD_
1322
1323
p_screen = _get_screen_index(p_screen);
1324
int screen_count = get_screen_count();
1325
ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());
1326
1327
EnumRectData data = { 0, p_screen, Rect2i() };
1328
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcUsableSize, (LPARAM)&data);
1329
data.rect.position -= _get_screens_origin();
1330
return data.rect;
1331
}
1332
1333
typedef struct {
1334
int count;
1335
int screen;
1336
int dpi;
1337
} EnumDpiData;
1338
1339
static int QueryDpiForMonitor(HMONITOR hmon, MONITOR_DPI_TYPE dpiType = MDT_DEFAULT) {
1340
int dpiX = 96, dpiY = 96;
1341
1342
UINT x = 0, y = 0;
1343
if (hmon) {
1344
HRESULT hr = GetDpiForMonitor(hmon, dpiType, &x, &y);
1345
if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
1346
dpiX = (int)x;
1347
dpiY = (int)y;
1348
}
1349
} else {
1350
static int overallX = 0, overallY = 0;
1351
if (overallX <= 0 || overallY <= 0) {
1352
HDC hdc = GetDC(nullptr);
1353
if (hdc) {
1354
overallX = GetDeviceCaps(hdc, LOGPIXELSX);
1355
overallY = GetDeviceCaps(hdc, LOGPIXELSY);
1356
ReleaseDC(nullptr, hdc);
1357
}
1358
}
1359
if (overallX > 0 && overallY > 0) {
1360
dpiX = overallX;
1361
dpiY = overallY;
1362
}
1363
}
1364
1365
return (dpiX + dpiY) / 2;
1366
}
1367
1368
static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
1369
EnumDpiData *data = (EnumDpiData *)dwData;
1370
if (data->count == data->screen) {
1371
data->dpi = QueryDpiForMonitor(hMonitor);
1372
}
1373
1374
data->count++;
1375
return TRUE;
1376
}
1377
1378
int DisplayServerWindows::screen_get_dpi(int p_screen) const {
1379
_THREAD_SAFE_METHOD_
1380
1381
p_screen = _get_screen_index(p_screen);
1382
int screen_count = get_screen_count();
1383
ERR_FAIL_INDEX_V(p_screen, screen_count, 72);
1384
1385
EnumDpiData data = { 0, p_screen, 72 };
1386
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);
1387
return data.dpi;
1388
}
1389
1390
Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const {
1391
Point2i pos = p_position + _get_screens_origin();
1392
1393
POINT p;
1394
p.x = pos.x;
1395
p.y = pos.y;
1396
LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p);
1397
1398
HDC dc = GetDC(nullptr);
1399
if (dc) {
1400
COLORREF col = GetPixel(dc, p.x, p.y);
1401
if (col != CLR_INVALID) {
1402
ReleaseDC(nullptr, dc);
1403
return Color(float(col & 0x000000FF) / 255.0f, float((col & 0x0000FF00) >> 8) / 255.0f, float((col & 0x00FF0000) >> 16) / 255.0f, 1.0f);
1404
}
1405
ReleaseDC(nullptr, dc);
1406
}
1407
1408
return Color();
1409
}
1410
1411
Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {
1412
p_screen = _get_screen_index(p_screen);
1413
int screen_count = get_screen_count();
1414
ERR_FAIL_INDEX_V(p_screen, screen_count, Ref<Image>());
1415
1416
Point2i pos = screen_get_position(p_screen) + _get_screens_origin();
1417
Size2i size = screen_get_size(p_screen);
1418
1419
POINT p1;
1420
p1.x = pos.x;
1421
p1.y = pos.y;
1422
1423
POINT p2;
1424
p2.x = pos.x + size.x;
1425
p2.y = pos.y + size.y;
1426
LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p1);
1427
LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p2);
1428
1429
Ref<Image> img;
1430
HDC dc = GetDC(nullptr);
1431
if (dc) {
1432
HDC hdc = CreateCompatibleDC(dc);
1433
int width = p2.x - p1.x;
1434
int height = p2.y - p1.y;
1435
if (hdc) {
1436
HBITMAP hbm = CreateCompatibleBitmap(dc, width, height);
1437
if (hbm) {
1438
SelectObject(hdc, hbm);
1439
BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY);
1440
1441
BITMAPINFO bmp_info = {};
1442
bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
1443
bmp_info.bmiHeader.biWidth = width;
1444
bmp_info.bmiHeader.biHeight = -height;
1445
bmp_info.bmiHeader.biPlanes = 1;
1446
bmp_info.bmiHeader.biBitCount = 32;
1447
bmp_info.bmiHeader.biCompression = BI_RGB;
1448
1449
Vector<uint8_t> img_data;
1450
img_data.resize(width * height * 4);
1451
GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);
1452
1453
uint8_t *wr = (uint8_t *)img_data.ptrw();
1454
for (int i = 0; i < width * height; i++) {
1455
SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.
1456
}
1457
img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);
1458
1459
DeleteObject(hbm);
1460
}
1461
DeleteDC(hdc);
1462
}
1463
ReleaseDC(nullptr, dc);
1464
}
1465
1466
return img;
1467
}
1468
1469
Ref<Image> DisplayServerWindows::screen_get_image_rect(const Rect2i &p_rect) const {
1470
Point2i pos = p_rect.position + _get_screens_origin();
1471
Size2i size = p_rect.size;
1472
1473
POINT p1;
1474
p1.x = pos.x;
1475
p1.y = pos.y;
1476
1477
POINT p2;
1478
p2.x = pos.x + size.x;
1479
p2.y = pos.y + size.y;
1480
LogicalToPhysicalPointForPerMonitorDPI(0, &p1);
1481
LogicalToPhysicalPointForPerMonitorDPI(0, &p2);
1482
1483
Ref<Image> img;
1484
HDC dc = GetDC(0);
1485
if (dc) {
1486
HDC hdc = CreateCompatibleDC(dc);
1487
int width = p2.x - p1.x;
1488
int height = p2.y - p1.y;
1489
if (hdc) {
1490
HBITMAP hbm = CreateCompatibleBitmap(dc, width, height);
1491
if (hbm) {
1492
SelectObject(hdc, hbm);
1493
BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY);
1494
1495
BITMAPINFO bmp_info = {};
1496
bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
1497
bmp_info.bmiHeader.biWidth = width;
1498
bmp_info.bmiHeader.biHeight = -height;
1499
bmp_info.bmiHeader.biPlanes = 1;
1500
bmp_info.bmiHeader.biBitCount = 32;
1501
bmp_info.bmiHeader.biCompression = BI_RGB;
1502
1503
Vector<uint8_t> img_data;
1504
img_data.resize(width * height * 4);
1505
GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);
1506
1507
uint8_t *wr = (uint8_t *)img_data.ptrw();
1508
for (int i = 0; i < width * height; i++) {
1509
SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.
1510
}
1511
img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);
1512
1513
DeleteObject(hbm);
1514
}
1515
DeleteDC(hdc);
1516
}
1517
ReleaseDC(NULL, dc);
1518
}
1519
1520
return img;
1521
}
1522
1523
float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {
1524
_THREAD_SAFE_METHOD_
1525
1526
p_screen = _get_screen_index(p_screen);
1527
int screen_count = get_screen_count();
1528
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);
1529
1530
EnumRefreshRateData data = { Vector<DISPLAYCONFIG_PATH_INFO>(), Vector<DISPLAYCONFIG_MODE_INFO>(), 0, p_screen, SCREEN_REFRESH_RATE_FALLBACK };
1531
1532
uint32_t path_count = 0;
1533
uint32_t mode_count = 0;
1534
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) {
1535
data.paths.resize(path_count);
1536
data.modes.resize(mode_count);
1537
if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, data.paths.ptrw(), &mode_count, data.modes.ptrw(), nullptr) != ERROR_SUCCESS) {
1538
data.paths.clear();
1539
data.modes.clear();
1540
}
1541
}
1542
1543
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data);
1544
return data.rate;
1545
}
1546
1547
void DisplayServerWindows::screen_set_keep_on(bool p_enable) {
1548
if (keep_screen_on == p_enable) {
1549
return;
1550
}
1551
1552
if (p_enable) {
1553
const String reason = "Godot Engine running with display/window/energy_saving/keep_screen_on = true";
1554
Char16String reason_utf16 = reason.utf16();
1555
REASON_CONTEXT context;
1556
context.Version = POWER_REQUEST_CONTEXT_VERSION;
1557
context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
1558
context.Reason.SimpleReasonString = (LPWSTR)(reason_utf16.ptrw());
1559
power_request = PowerCreateRequest(&context);
1560
if (power_request == INVALID_HANDLE_VALUE) {
1561
print_error("Failed to enable screen_keep_on.");
1562
return;
1563
}
1564
if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired) == 0) {
1565
print_error("Failed to request system sleep override.");
1566
return;
1567
}
1568
if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired) == 0) {
1569
print_error("Failed to request display timeout override.");
1570
return;
1571
}
1572
} else {
1573
PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired);
1574
PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired);
1575
CloseHandle(power_request);
1576
power_request = nullptr;
1577
}
1578
1579
keep_screen_on = p_enable;
1580
}
1581
1582
bool DisplayServerWindows::screen_is_kept_on() const {
1583
return keep_screen_on;
1584
}
1585
1586
Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {
1587
_THREAD_SAFE_METHOD_
1588
1589
Vector<DisplayServer::WindowID> ret;
1590
for (const KeyValue<WindowID, WindowData> &E : windows) {
1591
ret.push_back(E.key);
1592
}
1593
return ret;
1594
}
1595
1596
DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(const Point2i &p_position) const {
1597
Point2i offset = _get_screens_origin();
1598
POINT p;
1599
p.x = p_position.x + offset.x;
1600
p.y = p_position.y + offset.y;
1601
HWND hwnd = WindowFromPoint(p);
1602
for (const KeyValue<WindowID, WindowData> &E : windows) {
1603
if (E.value.hWnd == hwnd) {
1604
return E.key;
1605
}
1606
}
1607
1608
return INVALID_WINDOW_ID;
1609
}
1610
1611
DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent) {
1612
_THREAD_SAFE_METHOD_
1613
1614
WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_exclusive, p_transient_parent, NULL);
1615
ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window.");
1616
1617
WindowData &wd = windows[window_id];
1618
1619
if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {
1620
wd.resizable = false;
1621
}
1622
if (p_flags & WINDOW_FLAG_MINIMIZE_DISABLED_BIT) {
1623
wd.no_min_btn = true;
1624
}
1625
if (p_flags & WINDOW_FLAG_MAXIMIZE_DISABLED_BIT) {
1626
wd.no_max_btn = true;
1627
}
1628
if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
1629
wd.borderless = true;
1630
}
1631
if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
1632
wd.always_on_top = true;
1633
}
1634
if (p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT) {
1635
wd.sharp_corners = true;
1636
}
1637
if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
1638
wd.no_focus = true;
1639
}
1640
if (p_flags & WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT) {
1641
wd.mpass = true;
1642
}
1643
if (p_flags & WINDOW_FLAG_EXCLUDE_FROM_CAPTURE_BIT) {
1644
wd.hide_from_capture = true;
1645
if (os_ver.dwBuildNumber >= 19041) {
1646
SetWindowDisplayAffinity(wd.hWnd, WDA_EXCLUDEFROMCAPTURE);
1647
} else {
1648
SetWindowDisplayAffinity(wd.hWnd, WDA_MONITOR);
1649
}
1650
}
1651
if (p_flags & WINDOW_FLAG_POPUP_BIT) {
1652
wd.is_popup = true;
1653
}
1654
if (p_flags & WINDOW_FLAG_TRANSPARENT_BIT) {
1655
if (OS::get_singleton()->is_layered_allowed()) {
1656
DWM_BLURBEHIND bb;
1657
ZeroMemory(&bb, sizeof(bb));
1658
HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
1659
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
1660
bb.hRgnBlur = hRgn;
1661
bb.fEnable = TRUE;
1662
DwmEnableBlurBehindWindow(wd.hWnd, &bb);
1663
}
1664
1665
wd.layered_window = true;
1666
}
1667
1668
// Inherit icons from MAIN_WINDOW for all sub windows.
1669
HICON mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_SMALL, 0);
1670
if (mainwindow_icon) {
1671
SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);
1672
}
1673
mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_BIG, 0);
1674
if (mainwindow_icon) {
1675
SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);
1676
}
1677
#ifdef RD_ENABLED
1678
if (rendering_device) {
1679
rendering_device->screen_create(window_id);
1680
}
1681
#endif
1682
return window_id;
1683
}
1684
1685
bool DisplayServerWindows::_is_always_on_top_recursive(WindowID p_window) const {
1686
ERR_FAIL_COND_V(!windows.has(p_window), false);
1687
1688
const WindowData &wd = windows[p_window];
1689
if (wd.always_on_top) {
1690
return true;
1691
}
1692
1693
if (wd.transient_parent != INVALID_WINDOW_ID) {
1694
return _is_always_on_top_recursive(wd.transient_parent);
1695
}
1696
1697
return false;
1698
}
1699
1700
void DisplayServerWindows::show_window(WindowID p_id) {
1701
ERR_FAIL_COND(!windows.has(p_id));
1702
1703
WindowData &wd = windows[p_id];
1704
popup_open(p_id);
1705
1706
if (p_id != MAIN_WINDOW_ID) {
1707
_update_window_style(p_id);
1708
}
1709
wd.initialized = true;
1710
1711
if (wd.maximized) {
1712
ShowWindow(wd.hWnd, SW_SHOWMAXIMIZED);
1713
SetForegroundWindow(wd.hWnd); // Slightly higher priority.
1714
SetFocus(wd.hWnd); // Set keyboard focus.
1715
} else if (wd.minimized) {
1716
ShowWindow(wd.hWnd, SW_SHOWMINIMIZED);
1717
} else if (wd.no_focus) {
1718
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
1719
ShowWindow(wd.hWnd, SW_SHOWNA);
1720
} else if (wd.is_popup) {
1721
ShowWindow(wd.hWnd, SW_SHOWNA);
1722
SetFocus(wd.hWnd); // Set keyboard focus.
1723
} else {
1724
ShowWindow(wd.hWnd, SW_SHOW);
1725
SetForegroundWindow(wd.hWnd); // Slightly higher priority.
1726
SetFocus(wd.hWnd); // Set keyboard focus.
1727
}
1728
if (_is_always_on_top_recursive(p_id)) {
1729
SetWindowPos(wd.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));
1730
}
1731
}
1732
1733
void DisplayServerWindows::delete_sub_window(WindowID p_window) {
1734
_THREAD_SAFE_METHOD_
1735
1736
ERR_FAIL_COND(!windows.has(p_window));
1737
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted.");
1738
1739
popup_close(p_window);
1740
1741
WindowData &wd = windows[p_window];
1742
1743
IPropertyStore *prop_store;
1744
HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);
1745
if (hr == S_OK) {
1746
PROPVARIANT val;
1747
PropVariantInit(&val);
1748
prop_store->SetValue(PKEY_AppUserModel_ID, val);
1749
prop_store->Release();
1750
}
1751
1752
while (wd.transient_children.size()) {
1753
window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);
1754
}
1755
1756
if (wd.transient_parent != INVALID_WINDOW_ID) {
1757
window_set_transient(p_window, INVALID_WINDOW_ID);
1758
}
1759
1760
#ifdef RD_ENABLED
1761
if (rendering_device) {
1762
rendering_device->screen_free(p_window);
1763
}
1764
1765
if (rendering_context) {
1766
rendering_context->window_destroy(p_window);
1767
}
1768
#endif
1769
#ifdef GLES3_ENABLED
1770
if (gl_manager_angle) {
1771
gl_manager_angle->window_destroy(p_window);
1772
}
1773
if (gl_manager_native) {
1774
gl_manager_native->window_destroy(p_window);
1775
}
1776
#endif
1777
1778
if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {
1779
wintab_WTClose(wd.wtctx);
1780
wd.wtctx = nullptr;
1781
}
1782
1783
if (wd.drop_target != nullptr) {
1784
RevokeDragDrop(wd.hWnd);
1785
wd.drop_target->Release();
1786
}
1787
1788
DestroyWindow(wd.hWnd);
1789
windows.erase(p_window);
1790
1791
if (last_focused_window == p_window) {
1792
last_focused_window = INVALID_WINDOW_ID;
1793
}
1794
}
1795
1796
void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) {
1797
#if defined(GLES3_ENABLED)
1798
if (gl_manager_angle) {
1799
gl_manager_angle->window_make_current(p_window_id);
1800
}
1801
if (gl_manager_native) {
1802
gl_manager_native->window_make_current(p_window_id);
1803
}
1804
#endif
1805
}
1806
1807
int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
1808
ERR_FAIL_COND_V(!windows.has(p_window), 0);
1809
switch (p_handle_type) {
1810
case DISPLAY_HANDLE: {
1811
return 0; // Not supported.
1812
}
1813
case WINDOW_HANDLE: {
1814
return (int64_t)windows[p_window].hWnd;
1815
}
1816
#if defined(GLES3_ENABLED)
1817
case WINDOW_VIEW: {
1818
if (gl_manager_native) {
1819
return (int64_t)gl_manager_native->get_hdc(p_window);
1820
} else {
1821
return (int64_t)GetDC(windows[p_window].hWnd);
1822
}
1823
}
1824
case OPENGL_CONTEXT: {
1825
if (gl_manager_native) {
1826
return (int64_t)gl_manager_native->get_hglrc(p_window);
1827
}
1828
if (gl_manager_angle) {
1829
return (int64_t)gl_manager_angle->get_context(p_window);
1830
}
1831
return 0;
1832
}
1833
case EGL_DISPLAY: {
1834
if (gl_manager_angle) {
1835
return (int64_t)gl_manager_angle->get_display(p_window);
1836
}
1837
return 0;
1838
}
1839
case EGL_CONFIG: {
1840
if (gl_manager_angle) {
1841
return (int64_t)gl_manager_angle->get_config(p_window);
1842
}
1843
return 0;
1844
}
1845
#endif
1846
default: {
1847
return 0;
1848
}
1849
}
1850
}
1851
1852
void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
1853
_THREAD_SAFE_METHOD_
1854
1855
ERR_FAIL_COND(!windows.has(p_window));
1856
windows[p_window].instance_id = p_instance;
1857
}
1858
1859
ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window) const {
1860
_THREAD_SAFE_METHOD_
1861
1862
ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
1863
return windows[p_window].instance_id;
1864
}
1865
1866
void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
1867
_THREAD_SAFE_METHOD_
1868
1869
ERR_FAIL_COND(!windows.has(p_window));
1870
windows[p_window].rect_changed_callback = p_callable;
1871
}
1872
1873
void DisplayServerWindows::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
1874
_THREAD_SAFE_METHOD_
1875
1876
ERR_FAIL_COND(!windows.has(p_window));
1877
windows[p_window].event_callback = p_callable;
1878
}
1879
1880
void DisplayServerWindows::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
1881
_THREAD_SAFE_METHOD_
1882
1883
ERR_FAIL_COND(!windows.has(p_window));
1884
windows[p_window].input_event_callback = p_callable;
1885
}
1886
1887
void DisplayServerWindows::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
1888
_THREAD_SAFE_METHOD_
1889
1890
ERR_FAIL_COND(!windows.has(p_window));
1891
windows[p_window].input_text_callback = p_callable;
1892
}
1893
1894
void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
1895
_THREAD_SAFE_METHOD_
1896
1897
ERR_FAIL_COND(!windows.has(p_window));
1898
WindowData &window_data = windows[p_window];
1899
1900
window_data.drop_files_callback = p_callable;
1901
1902
if (window_data.drop_target == nullptr) {
1903
window_data.drop_target = memnew(DropTargetWindows(&window_data));
1904
ERR_FAIL_COND(RegisterDragDrop(window_data.hWnd, window_data.drop_target) != S_OK);
1905
}
1906
}
1907
1908
void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) {
1909
_THREAD_SAFE_METHOD_
1910
1911
ERR_FAIL_COND(!windows.has(p_window));
1912
SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data()));
1913
}
1914
1915
Size2i DisplayServerWindows::window_get_title_size(const String &p_title, WindowID p_window) const {
1916
_THREAD_SAFE_METHOD_
1917
1918
Size2i size;
1919
ERR_FAIL_COND_V(!windows.has(p_window), size);
1920
1921
const WindowData &wd = windows[p_window];
1922
if (wd.fullscreen || wd.minimized || wd.borderless) {
1923
return size;
1924
}
1925
1926
HDC hdc = GetDCEx(wd.hWnd, nullptr, DCX_WINDOW);
1927
if (hdc) {
1928
Char16String s = p_title.utf16();
1929
SIZE text_size;
1930
if (GetTextExtentPoint32W(hdc, (LPCWSTR)(s.get_data()), s.length(), &text_size)) {
1931
size.x = text_size.cx;
1932
size.y = text_size.cy;
1933
}
1934
1935
ReleaseDC(wd.hWnd, hdc);
1936
}
1937
RECT rect;
1938
if (DwmGetWindowAttribute(wd.hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rect, sizeof(RECT)) == S_OK) {
1939
if (rect.right - rect.left > 0) {
1940
ClientToScreen(wd.hWnd, (POINT *)&rect.left);
1941
ClientToScreen(wd.hWnd, (POINT *)&rect.right);
1942
1943
PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.left);
1944
PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.right);
1945
1946
size.x += (rect.right - rect.left);
1947
size.y = MAX(size.y, rect.bottom - rect.top);
1948
}
1949
}
1950
if (icon.is_valid()) {
1951
size.x += 32;
1952
} else {
1953
size.x += 16;
1954
}
1955
return size;
1956
}
1957
1958
void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
1959
_THREAD_SAFE_METHOD_
1960
1961
ERR_FAIL_COND(!windows.has(p_window));
1962
windows[p_window].mpath = p_region;
1963
_update_window_mouse_passthrough(p_window);
1964
}
1965
1966
void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {
1967
ERR_FAIL_COND(!windows.has(p_window));
1968
1969
const WindowData &wd = windows[p_window];
1970
bool clip_pixel = (wd.multiwindow_fs || (wd.borderless && wd.maximized));
1971
bool pass_set = (wd.mpath.size() > 0);
1972
if (!clip_pixel && !pass_set) {
1973
SetWindowRgn(wd.hWnd, nullptr, TRUE);
1974
} else {
1975
HRGN region = nullptr;
1976
if (pass_set) {
1977
Vector<POINT> points;
1978
points.resize(wd.mpath.size());
1979
POINT *points_ptr = points.ptrw();
1980
for (int i = 0; i < wd.mpath.size(); i++) {
1981
if (wd.borderless) {
1982
points_ptr[i].x = wd.mpath[i].x;
1983
points_ptr[i].y = wd.mpath[i].y;
1984
} else {
1985
points_ptr[i].x = wd.mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME);
1986
points_ptr[i].y = wd.mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);
1987
}
1988
}
1989
region = CreatePolygonRgn(points.ptr(), points.size(), ALTERNATE);
1990
} else {
1991
region = CreateRectRgn(0, 0, wd.width, wd.height);
1992
}
1993
if (clip_pixel) {
1994
HRGN region_clip = CreateRectRgn(0, 0, wd.width, wd.height);
1995
CombineRgn(region, region, region_clip, RGN_AND);
1996
DeleteObject(region_clip);
1997
}
1998
SetWindowRgn(wd.hWnd, region, FALSE);
1999
}
2000
}
2001
2002
int DisplayServerWindows::window_get_current_screen(WindowID p_window) const {
2003
_THREAD_SAFE_METHOD_
2004
2005
ERR_FAIL_COND_V(!windows.has(p_window), INVALID_SCREEN);
2006
2007
EnumScreenData data = { 0, 0, MonitorFromWindow(windows[p_window].hWnd, MONITOR_DEFAULTTONEAREST) };
2008
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);
2009
return data.screen;
2010
}
2011
2012
void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) {
2013
_THREAD_SAFE_METHOD_
2014
2015
ERR_FAIL_COND(!windows.has(p_window));
2016
2017
p_screen = _get_screen_index(p_screen);
2018
int screen_count = get_screen_count();
2019
ERR_FAIL_INDEX(p_screen, screen_count);
2020
2021
if (window_get_current_screen(p_window) == p_screen) {
2022
return;
2023
}
2024
const WindowData &wd = windows[p_window];
2025
2026
if (wd.parent_hwnd) {
2027
print_line("Embedded window can't be moved to another screen.");
2028
return;
2029
}
2030
if (wd.fullscreen) {
2031
Point2 pos = screen_get_position(p_screen) + _get_screens_origin();
2032
Size2 size = screen_get_size(p_screen);
2033
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
2034
2035
MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);
2036
} else if (wd.maximized) {
2037
Point2 pos = screen_get_position(p_screen) + _get_screens_origin();
2038
Size2 size = screen_get_size(p_screen);
2039
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
2040
2041
ShowWindow(wd.hWnd, SW_RESTORE);
2042
MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);
2043
ShowWindow(wd.hWnd, SW_MAXIMIZE);
2044
} else {
2045
Rect2i srect = screen_get_usable_rect(p_screen);
2046
Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
2047
Size2i wsize = window_get_size(p_window);
2048
wpos += srect.position;
2049
2050
wpos = wpos.clamp(srect.position, srect.position + srect.size - wsize / 3);
2051
window_set_position(wpos, p_window);
2052
}
2053
}
2054
2055
Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {
2056
_THREAD_SAFE_METHOD_
2057
2058
ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
2059
const WindowData &wd = windows[p_window];
2060
2061
if (wd.minimized) {
2062
return wd.last_pos;
2063
}
2064
2065
POINT point;
2066
point.x = 0;
2067
point.y = 0;
2068
2069
ClientToScreen(wd.hWnd, &point);
2070
2071
return Point2i(point.x, point.y) - _get_screens_origin();
2072
}
2073
2074
Point2i DisplayServerWindows::window_get_position_with_decorations(WindowID p_window) const {
2075
_THREAD_SAFE_METHOD_
2076
2077
ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
2078
const WindowData &wd = windows[p_window];
2079
2080
if (wd.minimized) {
2081
return wd.last_pos;
2082
}
2083
2084
RECT r;
2085
if (GetWindowRect(wd.hWnd, &r)) {
2086
return Point2i(r.left, r.top) - _get_screens_origin();
2087
}
2088
2089
return Point2i();
2090
}
2091
2092
void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) {
2093
ERR_FAIL_COND(!windows.has(p_window));
2094
2095
POINT mouse_pos;
2096
if (GetCursorPos(&mouse_pos) && ScreenToClient(windows[p_window].hWnd, &mouse_pos)) {
2097
if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= windows[p_window].width && mouse_pos.y <= windows[p_window].height) {
2098
old_x = mouse_pos.x;
2099
old_y = mouse_pos.y;
2100
old_invalid = false;
2101
Input::get_singleton()->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y));
2102
}
2103
}
2104
}
2105
2106
void DisplayServerWindows::window_set_position(const Point2i &p_position, WindowID p_window) {
2107
_THREAD_SAFE_METHOD_
2108
2109
ERR_FAIL_COND(!windows.has(p_window));
2110
WindowData &wd = windows[p_window];
2111
2112
if (wd.parent_hwnd) {
2113
print_line("Embedded window can't be moved.");
2114
return;
2115
}
2116
2117
if (wd.fullscreen || wd.maximized) {
2118
return;
2119
}
2120
2121
Point2i offset = _get_screens_origin();
2122
2123
RECT rc;
2124
rc.left = p_position.x + offset.x;
2125
rc.right = p_position.x + wd.width + offset.x;
2126
rc.bottom = p_position.y + wd.height + offset.y;
2127
rc.top = p_position.y + offset.y;
2128
2129
const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);
2130
const DWORD exStyle = GetWindowLongPtr(wd.hWnd, GWL_EXSTYLE);
2131
2132
AdjustWindowRectEx(&rc, style, false, exStyle);
2133
MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
2134
2135
wd.last_pos = p_position;
2136
_update_real_mouse_position(p_window);
2137
}
2138
2139
void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclusive) {
2140
_THREAD_SAFE_METHOD_
2141
ERR_FAIL_COND(!windows.has(p_window));
2142
WindowData &wd = windows[p_window];
2143
if (wd.exclusive != p_exclusive) {
2144
wd.exclusive = p_exclusive;
2145
if (wd.transient_parent != INVALID_WINDOW_ID) {
2146
if (wd.exclusive) {
2147
WindowData &wd_parent = windows[wd.transient_parent];
2148
SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);
2149
} else {
2150
SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);
2151
}
2152
}
2153
}
2154
}
2155
2156
void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) {
2157
_THREAD_SAFE_METHOD_
2158
2159
ERR_FAIL_COND(p_window == p_parent);
2160
ERR_FAIL_COND(!windows.has(p_window));
2161
2162
WindowData &wd_window = windows[p_window];
2163
2164
ERR_FAIL_COND(wd_window.transient_parent == p_parent);
2165
ERR_FAIL_COND_MSG(wd_window.always_on_top, "Windows with the 'on top' can't become transient.");
2166
2167
if (p_parent == INVALID_WINDOW_ID) {
2168
// Remove transient.
2169
2170
ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
2171
ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
2172
2173
WindowData &wd_parent = windows[wd_window.transient_parent];
2174
2175
wd_window.transient_parent = INVALID_WINDOW_ID;
2176
wd_parent.transient_children.erase(p_window);
2177
2178
if (wd_window.exclusive) {
2179
SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);
2180
}
2181
} else {
2182
ERR_FAIL_COND(!windows.has(p_parent));
2183
ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
2184
WindowData &wd_parent = windows[p_parent];
2185
2186
wd_window.transient_parent = p_parent;
2187
wd_parent.transient_children.insert(p_window);
2188
2189
if (wd_window.exclusive) {
2190
SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);
2191
}
2192
}
2193
}
2194
2195
void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_window) {
2196
_THREAD_SAFE_METHOD_
2197
2198
ERR_FAIL_COND(!windows.has(p_window));
2199
WindowData &wd = windows[p_window];
2200
2201
if (wd.parent_hwnd) {
2202
print_line("Embedded windows can't have a maximum size.");
2203
return;
2204
}
2205
2206
if ((p_size != Size2()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
2207
ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
2208
return;
2209
}
2210
wd.max_size = p_size;
2211
}
2212
2213
Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const {
2214
_THREAD_SAFE_METHOD_
2215
2216
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
2217
const WindowData &wd = windows[p_window];
2218
return wd.max_size;
2219
}
2220
2221
void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_window) {
2222
_THREAD_SAFE_METHOD_
2223
2224
ERR_FAIL_COND(!windows.has(p_window));
2225
WindowData &wd = windows[p_window];
2226
2227
if (wd.parent_hwnd) {
2228
print_line("Embedded windows can't have a minimum size.");
2229
return;
2230
}
2231
2232
if ((p_size != Size2()) && (wd.max_size != Size2()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
2233
ERR_PRINT("Minimum window size can't be larger than maximum window size!");
2234
return;
2235
}
2236
wd.min_size = p_size;
2237
}
2238
2239
Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const {
2240
_THREAD_SAFE_METHOD_
2241
2242
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
2243
const WindowData &wd = windows[p_window];
2244
return wd.min_size;
2245
}
2246
2247
void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_window) {
2248
_THREAD_SAFE_METHOD_
2249
2250
ERR_FAIL_COND(!windows.has(p_window));
2251
WindowData &wd = windows[p_window];
2252
2253
if (wd.parent_hwnd) {
2254
print_line("Embedded window can't be resized.");
2255
return;
2256
}
2257
2258
if (wd.fullscreen || wd.maximized) {
2259
return;
2260
}
2261
2262
int w = p_size.width;
2263
int h = p_size.height;
2264
RECT rect;
2265
GetWindowRect(wd.hWnd, &rect);
2266
2267
if (!wd.borderless) {
2268
RECT crect;
2269
GetClientRect(wd.hWnd, &crect);
2270
2271
w += (rect.right - rect.left) - (crect.right - crect.left);
2272
h += (rect.bottom - rect.top) - (crect.bottom - crect.top);
2273
}
2274
2275
MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE);
2276
}
2277
2278
Size2i DisplayServerWindows::window_get_size(WindowID p_window) const {
2279
_THREAD_SAFE_METHOD_
2280
2281
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
2282
const WindowData &wd = windows[p_window];
2283
2284
// GetClientRect() returns a zero rect for a minimized window, so we need to get the size in another way.
2285
if (wd.minimized) {
2286
return Size2(wd.width, wd.height);
2287
}
2288
2289
RECT r;
2290
if (GetClientRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.
2291
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
2292
return Size2(r.right - r.left - off_x, r.bottom - r.top);
2293
}
2294
return Size2();
2295
}
2296
2297
Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window) const {
2298
_THREAD_SAFE_METHOD_
2299
2300
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
2301
const WindowData &wd = windows[p_window];
2302
2303
RECT r;
2304
if (GetWindowRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.
2305
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
2306
return Size2(r.right - r.left - off_x, r.bottom - r.top);
2307
}
2308
return Size2();
2309
}
2310
2311
void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_no_min_btn, bool p_no_max_btn, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, bool p_embed_child, DWORD &r_style, DWORD &r_style_ex) {
2312
// Windows docs for window styles:
2313
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
2314
// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
2315
2316
r_style = 0;
2317
r_style_ex = WS_EX_WINDOWEDGE;
2318
if (p_main_window) {
2319
// When embedded, we don't want the window to have WS_EX_APPWINDOW because it will
2320
// show the embedded process in the taskbar and Alt-Tab.
2321
if (!p_embed_child) {
2322
r_style_ex |= WS_EX_APPWINDOW;
2323
}
2324
if (p_initialized) {
2325
r_style |= WS_VISIBLE;
2326
}
2327
}
2328
2329
if (p_embed_child) {
2330
r_style |= WS_POPUP;
2331
} else if (p_fullscreen || p_borderless) {
2332
r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past.
2333
if (p_minimized) {
2334
r_style |= WS_MINIMIZE;
2335
} else if (p_maximized) {
2336
r_style |= WS_MAXIMIZE;
2337
}
2338
if (!p_fullscreen) {
2339
r_style |= WS_SYSMENU;
2340
if (!p_no_min_btn) {
2341
r_style |= WS_MINIMIZEBOX;
2342
}
2343
if (!p_no_max_btn) {
2344
r_style |= WS_MAXIMIZEBOX;
2345
}
2346
}
2347
} else {
2348
if (p_resizable) {
2349
if (p_minimized) {
2350
r_style = WS_OVERLAPPEDWINDOW | WS_MINIMIZE;
2351
} else if (p_maximized) {
2352
r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE;
2353
} else {
2354
r_style = WS_OVERLAPPEDWINDOW;
2355
}
2356
if (p_no_min_btn) {
2357
r_style &= ~WS_MINIMIZEBOX;
2358
}
2359
if (p_no_max_btn) {
2360
r_style &= ~WS_MAXIMIZEBOX;
2361
}
2362
} else {
2363
if (p_minimized) {
2364
r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZE;
2365
} else {
2366
r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
2367
}
2368
if (!p_no_min_btn) {
2369
r_style |= WS_MINIMIZEBOX;
2370
}
2371
if (!p_no_max_btn) {
2372
r_style |= WS_MAXIMIZEBOX;
2373
}
2374
}
2375
}
2376
2377
if (p_no_activate_focus && !p_embed_child) {
2378
r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;
2379
}
2380
2381
if (!p_borderless && !p_no_activate_focus && p_initialized) {
2382
r_style |= WS_VISIBLE;
2383
}
2384
2385
r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
2386
r_style_ex |= WS_EX_ACCEPTFILES;
2387
2388
if (OS::get_singleton()->get_current_rendering_driver_name() == "d3d12") {
2389
r_style_ex |= WS_EX_NOREDIRECTIONBITMAP;
2390
}
2391
}
2392
2393
void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint) {
2394
_THREAD_SAFE_METHOD_
2395
2396
ERR_FAIL_COND(!windows.has(p_window));
2397
WindowData &wd = windows[p_window];
2398
2399
DWORD style = 0;
2400
DWORD style_ex = 0;
2401
2402
_get_window_style(p_window == MAIN_WINDOW_ID, wd.initialized, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.no_min_btn, wd.no_max_btn, wd.minimized, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, wd.parent_hwnd, style, style_ex);
2403
2404
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
2405
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
2406
2407
if (icon.is_valid()) {
2408
set_icon(icon);
2409
}
2410
2411
SetWindowPos(wd.hWnd, _is_always_on_top_recursive(p_window) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));
2412
2413
if (p_repaint) {
2414
RECT rect;
2415
GetWindowRect(wd.hWnd, &rect);
2416
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
2417
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left + off_x, rect.bottom - rect.top, TRUE);
2418
}
2419
}
2420
2421
void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) {
2422
_THREAD_SAFE_METHOD_
2423
2424
ERR_FAIL_COND(!windows.has(p_window));
2425
WindowData &wd = windows[p_window];
2426
2427
if (p_mode != WINDOW_MODE_WINDOWED && wd.parent_hwnd) {
2428
print_line("Embedded window only supports Windowed mode.");
2429
return;
2430
}
2431
2432
bool was_fullscreen = wd.fullscreen;
2433
wd.was_fullscreen_pre_min = false;
2434
2435
if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {
2436
int cs = window_get_current_screen(p_window);
2437
Rect2i full = Rect2i(screen_get_position(cs), screen_get_size(cs));
2438
Rect2i usable = screen_get_usable_rect(cs);
2439
if (full == usable) {
2440
p_mode = WINDOW_MODE_FULLSCREEN;
2441
}
2442
}
2443
2444
if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
2445
RECT rect;
2446
2447
wd.fullscreen = false;
2448
wd.multiwindow_fs = false;
2449
2450
// Restore previous maximized state.
2451
wd.maximized = wd.was_maximized_pre_fs;
2452
2453
_update_window_style(p_window, false);
2454
2455
// Restore window rect after exiting fullscreen.
2456
if (wd.pre_fs_valid) {
2457
rect = wd.pre_fs_rect;
2458
} else {
2459
rect.left = 0;
2460
rect.right = wd.width;
2461
rect.top = 0;
2462
rect.bottom = wd.height;
2463
}
2464
2465
ShowWindow(wd.hWnd, SW_RESTORE);
2466
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
2467
2468
if (restore_mouse_trails > 1) {
2469
SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);
2470
restore_mouse_trails = 0;
2471
}
2472
}
2473
2474
if ((wd.maximized || wd.was_maximized_pre_fs) && wd.borderless && p_mode != WINDOW_MODE_MINIMIZED && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
2475
RECT rect;
2476
if (wd.pre_fs_valid) {
2477
rect = wd.pre_fs_rect;
2478
} else {
2479
rect.left = 0;
2480
rect.right = wd.width;
2481
rect.top = 0;
2482
rect.bottom = wd.height;
2483
}
2484
2485
ShowWindow(wd.hWnd, SW_RESTORE);
2486
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
2487
}
2488
2489
if (p_mode == WINDOW_MODE_WINDOWED) {
2490
ShowWindow(wd.hWnd, SW_NORMAL);
2491
wd.maximized = false;
2492
wd.minimized = false;
2493
}
2494
2495
if (p_mode == WINDOW_MODE_MAXIMIZED && !wd.borderless) {
2496
ShowWindow(wd.hWnd, SW_MAXIMIZE);
2497
wd.maximized = true;
2498
wd.minimized = false;
2499
}
2500
2501
if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {
2502
if (!was_fullscreen && !(wd.maximized && wd.borderless)) {
2503
// Save non-fullscreen rect before entering fullscreen.
2504
GetWindowRect(wd.hWnd, &wd.pre_fs_rect);
2505
wd.pre_fs_valid = true;
2506
}
2507
ShowWindow(wd.hWnd, SW_NORMAL);
2508
wd.maximized = true;
2509
wd.minimized = false;
2510
2511
int cs = window_get_current_screen(p_window);
2512
Rect2i usable = screen_get_usable_rect(cs);
2513
Point2 pos = usable.position + _get_screens_origin();
2514
Size2 size = usable.size;
2515
MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
2516
}
2517
2518
if (p_mode == WINDOW_MODE_MINIMIZED) {
2519
ShowWindow(wd.hWnd, SW_MINIMIZE);
2520
wd.maximized = false;
2521
wd.minimized = true;
2522
wd.was_fullscreen_pre_min = was_fullscreen;
2523
}
2524
2525
if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
2526
wd.multiwindow_fs = false;
2527
} else if (p_mode == WINDOW_MODE_FULLSCREEN) {
2528
wd.multiwindow_fs = true;
2529
}
2530
_update_window_style(p_window, false);
2531
2532
if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) {
2533
if (wd.minimized || wd.maximized) {
2534
ShowWindow(wd.hWnd, SW_RESTORE);
2535
}
2536
2537
// Save previous maximized stare.
2538
wd.was_maximized_pre_fs = wd.maximized;
2539
2540
if (!was_fullscreen && !(wd.maximized && wd.borderless)) {
2541
// Save non-fullscreen rect before entering fullscreen.
2542
GetWindowRect(wd.hWnd, &wd.pre_fs_rect);
2543
wd.pre_fs_valid = true;
2544
}
2545
2546
int cs = window_get_current_screen(p_window);
2547
Point2 pos = screen_get_position(cs) + _get_screens_origin();
2548
Size2 size = screen_get_size(cs);
2549
2550
wd.fullscreen = true;
2551
wd.maximized = false;
2552
wd.minimized = false;
2553
2554
_update_window_style(p_window, false);
2555
2556
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
2557
MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);
2558
2559
// If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode.
2560
// Save number of trails so we can restore when exiting, then turn off mouse trails
2561
SystemParametersInfoA(SPI_GETMOUSETRAILS, 0, &restore_mouse_trails, 0);
2562
if (restore_mouse_trails > 1) {
2563
SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, nullptr, 0);
2564
}
2565
}
2566
_update_window_mouse_passthrough(p_window);
2567
}
2568
2569
DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {
2570
_THREAD_SAFE_METHOD_
2571
2572
ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
2573
const WindowData &wd = windows[p_window];
2574
2575
if (wd.fullscreen) {
2576
if (wd.multiwindow_fs) {
2577
return WINDOW_MODE_FULLSCREEN;
2578
} else {
2579
return WINDOW_MODE_EXCLUSIVE_FULLSCREEN;
2580
}
2581
} else if (wd.minimized) {
2582
return WINDOW_MODE_MINIMIZED;
2583
} else if (wd.maximized) {
2584
return WINDOW_MODE_MAXIMIZED;
2585
} else {
2586
return WINDOW_MODE_WINDOWED;
2587
}
2588
}
2589
2590
bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const {
2591
_THREAD_SAFE_METHOD_
2592
2593
ERR_FAIL_COND_V(!windows.has(p_window), false);
2594
const WindowData &wd = windows[p_window];
2595
2596
const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);
2597
return (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX;
2598
}
2599
2600
void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
2601
_THREAD_SAFE_METHOD_
2602
2603
ERR_FAIL_COND(!windows.has(p_window));
2604
WindowData &wd = windows[p_window];
2605
switch (p_flag) {
2606
case WINDOW_FLAG_MINIMIZE_DISABLED: {
2607
wd.no_min_btn = p_enabled;
2608
_update_window_style(p_window);
2609
} break;
2610
case WINDOW_FLAG_MAXIMIZE_DISABLED: {
2611
wd.no_max_btn = p_enabled;
2612
_update_window_style(p_window);
2613
} break;
2614
case WINDOW_FLAG_RESIZE_DISABLED: {
2615
if (p_enabled && wd.parent_hwnd) {
2616
print_line("Embedded window resize can't be disabled.");
2617
return;
2618
}
2619
wd.resizable = !p_enabled;
2620
_update_window_style(p_window);
2621
} break;
2622
case WINDOW_FLAG_BORDERLESS: {
2623
wd.borderless = p_enabled;
2624
_update_window_mouse_passthrough(p_window);
2625
_update_window_style(p_window);
2626
ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.
2627
} break;
2628
case WINDOW_FLAG_ALWAYS_ON_TOP: {
2629
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top.");
2630
if (p_enabled && wd.parent_hwnd) {
2631
print_line("Embedded window can't become on top.");
2632
return;
2633
}
2634
wd.always_on_top = p_enabled;
2635
_update_window_style(p_window);
2636
} break;
2637
case WINDOW_FLAG_SHARP_CORNERS: {
2638
wd.sharp_corners = p_enabled;
2639
DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;
2640
::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));
2641
_update_window_style(p_window);
2642
} break;
2643
case WINDOW_FLAG_TRANSPARENT: {
2644
if (p_enabled) {
2645
// Enable per-pixel alpha.
2646
if (OS::get_singleton()->is_layered_allowed()) {
2647
DWM_BLURBEHIND bb;
2648
ZeroMemory(&bb, sizeof(bb));
2649
HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
2650
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
2651
bb.hRgnBlur = hRgn;
2652
bb.fEnable = TRUE;
2653
DwmEnableBlurBehindWindow(wd.hWnd, &bb);
2654
}
2655
wd.layered_window = true;
2656
} else {
2657
// Disable per-pixel alpha.
2658
wd.layered_window = false;
2659
if (OS::get_singleton()->is_layered_allowed()) {
2660
DWM_BLURBEHIND bb;
2661
ZeroMemory(&bb, sizeof(bb));
2662
HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
2663
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
2664
bb.hRgnBlur = hRgn;
2665
bb.fEnable = FALSE;
2666
DwmEnableBlurBehindWindow(wd.hWnd, &bb);
2667
}
2668
}
2669
} break;
2670
case WINDOW_FLAG_NO_FOCUS: {
2671
wd.no_focus = p_enabled;
2672
_update_window_style(p_window);
2673
} break;
2674
case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
2675
wd.mpass = p_enabled;
2676
} break;
2677
case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {
2678
wd.hide_from_capture = p_enabled;
2679
if (p_enabled) {
2680
if (os_ver.dwBuildNumber >= 19041) {
2681
SetWindowDisplayAffinity(wd.hWnd, WDA_EXCLUDEFROMCAPTURE);
2682
} else {
2683
SetWindowDisplayAffinity(wd.hWnd, WDA_MONITOR);
2684
}
2685
} else {
2686
SetWindowDisplayAffinity(wd.hWnd, WDA_NONE);
2687
}
2688
} break;
2689
case WINDOW_FLAG_POPUP: {
2690
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
2691
ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
2692
if (p_enabled && wd.parent_hwnd) {
2693
print_line("Embedded window can't be popup.");
2694
return;
2695
}
2696
wd.is_popup = p_enabled;
2697
} break;
2698
default:
2699
break;
2700
}
2701
}
2702
2703
bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
2704
_THREAD_SAFE_METHOD_
2705
2706
ERR_FAIL_COND_V(!windows.has(p_window), false);
2707
const WindowData &wd = windows[p_window];
2708
switch (p_flag) {
2709
case WINDOW_FLAG_MAXIMIZE_DISABLED: {
2710
return wd.no_max_btn;
2711
} break;
2712
case WINDOW_FLAG_MINIMIZE_DISABLED: {
2713
return wd.no_min_btn;
2714
} break;
2715
case WINDOW_FLAG_RESIZE_DISABLED: {
2716
return !wd.resizable;
2717
} break;
2718
case WINDOW_FLAG_BORDERLESS: {
2719
return wd.borderless;
2720
} break;
2721
case WINDOW_FLAG_ALWAYS_ON_TOP: {
2722
return wd.always_on_top;
2723
} break;
2724
case WINDOW_FLAG_SHARP_CORNERS: {
2725
return wd.sharp_corners;
2726
} break;
2727
case WINDOW_FLAG_TRANSPARENT: {
2728
return wd.layered_window;
2729
} break;
2730
case WINDOW_FLAG_NO_FOCUS: {
2731
return wd.no_focus;
2732
} break;
2733
case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
2734
return wd.mpass;
2735
} break;
2736
case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {
2737
return wd.hide_from_capture;
2738
} break;
2739
case WINDOW_FLAG_POPUP: {
2740
return wd.is_popup;
2741
} break;
2742
default:
2743
break;
2744
}
2745
2746
return false;
2747
}
2748
2749
void DisplayServerWindows::window_request_attention(WindowID p_window) {
2750
_THREAD_SAFE_METHOD_
2751
2752
ERR_FAIL_COND(!windows.has(p_window));
2753
const WindowData &wd = windows[p_window];
2754
2755
FLASHWINFO info;
2756
info.cbSize = sizeof(FLASHWINFO);
2757
info.hwnd = wd.hWnd;
2758
info.dwFlags = FLASHW_ALL;
2759
info.dwTimeout = 0;
2760
info.uCount = 2;
2761
FlashWindowEx(&info);
2762
}
2763
2764
void DisplayServerWindows::window_move_to_foreground(WindowID p_window) {
2765
_THREAD_SAFE_METHOD_
2766
2767
ERR_FAIL_COND(!windows.has(p_window));
2768
WindowData &wd = windows[p_window];
2769
2770
if (!wd.no_focus && !wd.is_popup) {
2771
SetForegroundWindow(wd.hWnd);
2772
}
2773
}
2774
2775
bool DisplayServerWindows::window_is_focused(WindowID p_window) const {
2776
_THREAD_SAFE_METHOD_
2777
2778
ERR_FAIL_COND_V(!windows.has(p_window), false);
2779
const WindowData &wd = windows[p_window];
2780
2781
return wd.window_focused;
2782
}
2783
2784
DisplayServerWindows::WindowID DisplayServerWindows::get_focused_window() const {
2785
return last_focused_window;
2786
}
2787
2788
bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
2789
_THREAD_SAFE_METHOD_
2790
2791
ERR_FAIL_COND_V(!windows.has(p_window), false);
2792
const WindowData &wd = windows[p_window];
2793
return !wd.minimized;
2794
}
2795
2796
bool DisplayServerWindows::can_any_window_draw() const {
2797
_THREAD_SAFE_METHOD_
2798
2799
for (const KeyValue<WindowID, WindowData> &E : windows) {
2800
if (!E.value.minimized) {
2801
return true;
2802
}
2803
}
2804
2805
return false;
2806
}
2807
2808
int DisplayServerWindows::accessibility_should_increase_contrast() const {
2809
HIGHCONTRASTA hc;
2810
hc.cbSize = sizeof(HIGHCONTRAST);
2811
if (!SystemParametersInfoA(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRAST), &hc, 0)) {
2812
return -1;
2813
}
2814
return (hc.dwFlags & HCF_HIGHCONTRASTON);
2815
}
2816
2817
int DisplayServerWindows::accessibility_should_reduce_animation() const {
2818
BOOL anim_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.
2819
if (!SystemParametersInfoA(SPI_GETCLIENTAREAANIMATION, 0, &anim_enabled, 0)) {
2820
return -1;
2821
}
2822
return (!anim_enabled);
2823
}
2824
2825
int DisplayServerWindows::accessibility_should_reduce_transparency() const {
2826
BOOL tr_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.
2827
if (!SystemParametersInfoA(SPI_GETDISABLEOVERLAPPEDCONTENT, 0, &tr_enabled, 0)) {
2828
return -1;
2829
}
2830
return tr_enabled;
2831
}
2832
2833
int DisplayServerWindows::accessibility_screen_reader_active() const {
2834
BOOL sr_enabled = false; // Note: this should be BOOL (WinAPI), not bool (C++), since SystemParametersInfoA expect variable with specific size.
2835
if (SystemParametersInfoA(SPI_GETSCREENREADER, 0, &sr_enabled, 0) && sr_enabled) {
2836
return true;
2837
}
2838
2839
static const WCHAR *narrator_mutex_name = L"NarratorRunning";
2840
HANDLE narrator_mutex = OpenMutexW(MUTEX_ALL_ACCESS, false, narrator_mutex_name);
2841
if (narrator_mutex) {
2842
CloseHandle(narrator_mutex);
2843
return true;
2844
}
2845
return false;
2846
}
2847
2848
Vector2i DisplayServerWindows::ime_get_selection() const {
2849
_THREAD_SAFE_METHOD_
2850
2851
DisplayServer::WindowID window_id = _get_focused_window_or_popup();
2852
const WindowData &wd = windows[window_id];
2853
if (!wd.ime_active) {
2854
return Vector2i();
2855
}
2856
2857
int cursor = ImmGetCompositionStringW(wd.im_himc, GCS_CURSORPOS, nullptr, 0);
2858
2859
int32_t length = ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, nullptr, 0);
2860
wchar_t *string = reinterpret_cast<wchar_t *>(memalloc(length));
2861
ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);
2862
2863
int32_t utf32_cursor = 0;
2864
for (int32_t i = 0; i < length / int32_t(sizeof(wchar_t)); i++) {
2865
if ((string[i] & 0xfffffc00) == 0xd800) {
2866
i++;
2867
}
2868
if (i < cursor) {
2869
utf32_cursor++;
2870
} else {
2871
break;
2872
}
2873
}
2874
2875
memdelete(string);
2876
2877
return Vector2i(utf32_cursor, 0);
2878
}
2879
2880
String DisplayServerWindows::ime_get_text() const {
2881
_THREAD_SAFE_METHOD_
2882
2883
DisplayServer::WindowID window_id = _get_focused_window_or_popup();
2884
const WindowData &wd = windows[window_id];
2885
if (!wd.ime_active) {
2886
return String();
2887
}
2888
2889
String ret;
2890
int32_t length = ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, nullptr, 0);
2891
wchar_t *string = reinterpret_cast<wchar_t *>(memalloc(length));
2892
ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);
2893
ret.append_utf16((char16_t *)string, length / sizeof(wchar_t));
2894
2895
memdelete(string);
2896
2897
return ret;
2898
}
2899
2900
void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p_window) {
2901
_THREAD_SAFE_METHOD_
2902
2903
ERR_FAIL_COND(!windows.has(p_window));
2904
WindowData &wd = windows[p_window];
2905
2906
if (p_active) {
2907
wd.ime_active = true;
2908
ImmAssociateContext(wd.hWnd, wd.im_himc);
2909
CreateCaret(wd.hWnd, nullptr, 1, 1);
2910
window_set_ime_position(wd.im_position, p_window);
2911
} else {
2912
ImmAssociateContext(wd.hWnd, (HIMC) nullptr);
2913
DestroyCaret();
2914
wd.ime_active = false;
2915
}
2916
}
2917
2918
void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
2919
_THREAD_SAFE_METHOD_
2920
2921
ERR_FAIL_COND(!windows.has(p_window));
2922
WindowData &wd = windows[p_window];
2923
2924
wd.im_position = p_pos;
2925
2926
HIMC himc = ImmGetContext(wd.hWnd);
2927
if (himc == (HIMC) nullptr) {
2928
return;
2929
}
2930
2931
COMPOSITIONFORM cps;
2932
cps.dwStyle = CFS_POINT;
2933
cps.ptCurrentPos.x = wd.im_position.x;
2934
cps.ptCurrentPos.y = wd.im_position.y;
2935
ImmSetCompositionWindow(himc, &cps);
2936
ImmReleaseContext(wd.hWnd, himc);
2937
}
2938
2939
void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
2940
_THREAD_SAFE_METHOD_
2941
2942
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
2943
2944
if (cursor_shape == p_shape) {
2945
return;
2946
}
2947
2948
if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
2949
cursor_shape = p_shape;
2950
return;
2951
}
2952
2953
static const LPCTSTR win_cursors[CURSOR_MAX] = {
2954
IDC_ARROW,
2955
IDC_IBEAM,
2956
IDC_HAND, // Finger.
2957
IDC_CROSS,
2958
IDC_WAIT,
2959
IDC_APPSTARTING,
2960
IDC_SIZEALL,
2961
IDC_ARROW,
2962
IDC_NO,
2963
IDC_SIZENS,
2964
IDC_SIZEWE,
2965
IDC_SIZENESW,
2966
IDC_SIZENWSE,
2967
IDC_SIZEALL,
2968
IDC_SIZENS,
2969
IDC_SIZEWE,
2970
IDC_HELP
2971
};
2972
2973
if (cursors_cache.has(p_shape)) {
2974
SetCursor(cursors[p_shape]);
2975
} else {
2976
SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
2977
}
2978
2979
cursor_shape = p_shape;
2980
}
2981
2982
DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {
2983
return cursor_shape;
2984
}
2985
2986
void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
2987
_THREAD_SAFE_METHOD_
2988
2989
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
2990
2991
if (p_cursor.is_valid()) {
2992
RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
2993
2994
if (cursor_c) {
2995
if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
2996
cursor_set_shape(p_shape);
2997
return;
2998
}
2999
3000
cursors_cache.erase(p_shape);
3001
}
3002
3003
Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
3004
ERR_FAIL_COND(image.is_null());
3005
Vector2i texture_size = image->get_size();
3006
3007
UINT image_size = texture_size.width * texture_size.height;
3008
3009
// Create the BITMAP with alpha channel.
3010
COLORREF *buffer = nullptr;
3011
3012
BITMAPV5HEADER bi;
3013
ZeroMemory(&bi, sizeof(bi));
3014
bi.bV5Size = sizeof(bi);
3015
bi.bV5Width = texture_size.width;
3016
bi.bV5Height = -texture_size.height;
3017
bi.bV5Planes = 1;
3018
bi.bV5BitCount = 32;
3019
bi.bV5Compression = BI_BITFIELDS;
3020
bi.bV5RedMask = 0x00ff0000;
3021
bi.bV5GreenMask = 0x0000ff00;
3022
bi.bV5BlueMask = 0x000000ff;
3023
bi.bV5AlphaMask = 0xff000000;
3024
3025
HDC dc = GetDC(nullptr);
3026
HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);
3027
HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr);
3028
3029
bool fully_transparent = true;
3030
for (UINT index = 0; index < image_size; index++) {
3031
int row_index = std::floor(index / texture_size.width);
3032
int column_index = index % int(texture_size.width);
3033
3034
const Color &c = image->get_pixel(column_index, row_index);
3035
fully_transparent = fully_transparent && (c.a == 0.f);
3036
3037
*(buffer + index) = c.to_argb32();
3038
}
3039
3040
// Finally, create the icon.
3041
if (cursors[p_shape]) {
3042
DestroyIcon(cursors[p_shape]);
3043
}
3044
3045
if (fully_transparent) {
3046
cursors[p_shape] = nullptr;
3047
} else {
3048
ICONINFO iconinfo;
3049
iconinfo.fIcon = FALSE;
3050
iconinfo.xHotspot = p_hotspot.x;
3051
iconinfo.yHotspot = p_hotspot.y;
3052
iconinfo.hbmMask = mask;
3053
iconinfo.hbmColor = bitmap;
3054
cursors[p_shape] = CreateIconIndirect(&iconinfo);
3055
}
3056
3057
Vector<Variant> params;
3058
params.push_back(p_cursor);
3059
params.push_back(p_hotspot);
3060
cursors_cache.insert(p_shape, params);
3061
3062
if (p_shape == cursor_shape) {
3063
if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
3064
SetCursor(cursors[p_shape]);
3065
}
3066
}
3067
3068
DeleteObject(mask);
3069
DeleteObject(bitmap);
3070
ReleaseDC(nullptr, dc);
3071
} else {
3072
// Reset to default system cursor.
3073
if (cursors[p_shape]) {
3074
DestroyIcon(cursors[p_shape]);
3075
}
3076
cursors[p_shape] = nullptr;
3077
3078
cursors_cache.erase(p_shape);
3079
3080
CursorShape c = cursor_shape;
3081
cursor_shape = CURSOR_MAX;
3082
cursor_set_shape(c);
3083
}
3084
}
3085
3086
bool DisplayServerWindows::get_swap_cancel_ok() {
3087
return true;
3088
}
3089
3090
void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
3091
_THREAD_SAFE_METHOD_
3092
3093
AllowSetForegroundWindow(pid);
3094
}
3095
3096
struct WindowEnumData {
3097
DWORD process_id;
3098
HWND parent_hWnd;
3099
HWND hWnd;
3100
};
3101
3102
static BOOL CALLBACK _enum_proc_find_window_from_process_id_callback(HWND hWnd, LPARAM lParam) {
3103
WindowEnumData &ed = *(WindowEnumData *)lParam;
3104
DWORD process_id = 0x0;
3105
3106
GetWindowThreadProcessId(hWnd, &process_id);
3107
if (ed.process_id == process_id) {
3108
if (GetParent(hWnd) != ed.parent_hWnd) {
3109
return TRUE;
3110
}
3111
3112
// Found it.
3113
ed.hWnd = hWnd;
3114
SetLastError(ERROR_SUCCESS);
3115
return FALSE;
3116
}
3117
// Continue enumeration.
3118
return TRUE;
3119
}
3120
3121
HWND DisplayServerWindows::_find_window_from_process_id(OS::ProcessID p_pid, HWND p_current_hwnd) {
3122
DWORD pid = p_pid;
3123
WindowEnumData ed = { pid, p_current_hwnd, NULL };
3124
3125
// First, check our own child, maybe it's already embedded.
3126
if (!EnumChildWindows(p_current_hwnd, _enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {
3127
if (ed.hWnd) {
3128
return ed.hWnd;
3129
}
3130
}
3131
3132
// Then check all the opened windows on the computer.
3133
if (!EnumWindows(_enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {
3134
return ed.hWnd;
3135
}
3136
3137
return NULL;
3138
}
3139
3140
Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {
3141
_THREAD_SAFE_METHOD_
3142
3143
ERR_FAIL_COND_V(!windows.has(p_window), FAILED);
3144
3145
const WindowData &wd = windows[p_window];
3146
3147
EmbeddedProcessData *ep = nullptr;
3148
if (embedded_processes.has(p_pid)) {
3149
ep = embedded_processes.get(p_pid);
3150
} else {
3151
// New process, trying to find the window.
3152
HWND handle_to_embed = _find_window_from_process_id(p_pid, wd.hWnd);
3153
if (!handle_to_embed) {
3154
return ERR_DOES_NOT_EXIST;
3155
}
3156
3157
const DWORD style = GetWindowLongPtr(handle_to_embed, GWL_STYLE);
3158
3159
ep = memnew(EmbeddedProcessData);
3160
ep->window_handle = handle_to_embed;
3161
ep->parent_window_handle = wd.hWnd;
3162
ep->is_visible = (style & WS_VISIBLE) == WS_VISIBLE;
3163
3164
embedded_processes.insert(p_pid, ep);
3165
}
3166
3167
if (p_rect.size.x <= 100 || p_rect.size.y <= 100) {
3168
p_visible = false;
3169
}
3170
3171
// In Godot, the window position is offset by the screen's origin coordinates.
3172
// We need to adjust for this when a screen is positioned in the negative space
3173
// (e.g., a screen to the left of the main screen).
3174
const Rect2i adjusted_rect = Rect2i(p_rect.position + _get_screens_origin(), p_rect.size);
3175
3176
// Use HWND_BOTTOM to prevent reordering of the embedded window over another popup.
3177
SetWindowPos(ep->window_handle, HWND_BOTTOM, adjusted_rect.position.x, adjusted_rect.position.y, adjusted_rect.size.x, adjusted_rect.size.y, SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS);
3178
3179
if (ep->is_visible != p_visible) {
3180
if (p_visible) {
3181
ShowWindow(ep->window_handle, SW_SHOWNA);
3182
} else {
3183
ShowWindow(ep->window_handle, SW_HIDE);
3184
}
3185
ep->is_visible = p_visible;
3186
}
3187
3188
if (p_grab_focus) {
3189
SetForegroundWindow(ep->window_handle);
3190
SetFocus(ep->window_handle);
3191
}
3192
3193
return OK;
3194
}
3195
3196
Error DisplayServerWindows::request_close_embedded_process(OS::ProcessID p_pid) {
3197
_THREAD_SAFE_METHOD_
3198
3199
if (!embedded_processes.has(p_pid)) {
3200
return ERR_DOES_NOT_EXIST;
3201
}
3202
3203
EmbeddedProcessData *ep = embedded_processes.get(p_pid);
3204
3205
// Send a close message to gracefully close the process.
3206
PostMessage(ep->window_handle, WM_CLOSE, 0, 0);
3207
3208
return OK;
3209
}
3210
3211
Error DisplayServerWindows::remove_embedded_process(OS::ProcessID p_pid) {
3212
_THREAD_SAFE_METHOD_
3213
3214
if (!embedded_processes.has(p_pid)) {
3215
return ERR_DOES_NOT_EXIST;
3216
}
3217
3218
EmbeddedProcessData *ep = embedded_processes.get(p_pid);
3219
3220
request_close_embedded_process(p_pid);
3221
3222
// This is a workaround to ensure the parent window correctly regains focus after the
3223
// embedded window is closed. When the embedded window is closed while it has focus,
3224
// the parent window (the editor) does not become active. It appears focused but is not truly activated.
3225
// Opening a new window and closing it forces Windows to set the focus and activation correctly.
3226
DWORD style = WS_POPUP | WS_VISIBLE;
3227
DWORD style_ex = WS_EX_TOPMOST;
3228
3229
WNDCLASSW wcTemp = {};
3230
wcTemp.lpfnWndProc = DefWindowProcW;
3231
wcTemp.hInstance = GetModuleHandle(nullptr);
3232
wcTemp.lpszClassName = L"Engine temp window";
3233
RegisterClassW(&wcTemp);
3234
3235
HWND hWnd = CreateWindowExW(
3236
style_ex,
3237
L"Engine temp window", L"",
3238
style,
3239
0,
3240
0,
3241
1,
3242
1,
3243
ep->parent_window_handle,
3244
nullptr,
3245
GetModuleHandle(nullptr),
3246
nullptr);
3247
3248
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
3249
3250
DestroyWindow(hWnd);
3251
UnregisterClassW(L"Engine temp window", GetModuleHandle(nullptr));
3252
3253
SetForegroundWindow(ep->parent_window_handle);
3254
3255
embedded_processes.erase(p_pid);
3256
memdelete(ep);
3257
3258
return OK;
3259
}
3260
3261
OS::ProcessID DisplayServerWindows::get_focused_process_id() {
3262
HWND hwnd = GetForegroundWindow();
3263
if (!hwnd) {
3264
return 0;
3265
}
3266
3267
// Get the process ID of the window.
3268
DWORD processID;
3269
GetWindowThreadProcessId(hwnd, &processID);
3270
3271
return processID;
3272
}
3273
3274
static HRESULT CALLBACK win32_task_dialog_callback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) {
3275
if (msg == TDN_CREATED) {
3276
// To match the input text dialog.
3277
SendMessageW(hwnd, WM_SETICON, ICON_BIG, 0);
3278
SendMessageW(hwnd, WM_SETICON, ICON_SMALL, 0);
3279
}
3280
3281
return 0;
3282
}
3283
3284
Error DisplayServerWindows::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
3285
_THREAD_SAFE_METHOD_
3286
3287
TASKDIALOGCONFIG config;
3288
ZeroMemory(&config, sizeof(TASKDIALOGCONFIG));
3289
config.cbSize = sizeof(TASKDIALOGCONFIG);
3290
3291
Char16String title = p_title.utf16();
3292
Char16String message = p_description.utf16();
3293
LocalVector<Char16String> buttons;
3294
for (String s : p_buttons) {
3295
buttons.push_back(s.utf16());
3296
}
3297
3298
WindowID window_id = _get_focused_window_or_popup();
3299
if (!windows.has(window_id)) {
3300
window_id = MAIN_WINDOW_ID;
3301
}
3302
3303
config.pszWindowTitle = (LPCWSTR)(title.get_data());
3304
config.pszContent = (LPCWSTR)(message.get_data());
3305
config.hwndParent = windows[window_id].hWnd;
3306
3307
const int button_count = buttons.size();
3308
config.cButtons = button_count;
3309
3310
// No dynamic stack array size :(
3311
TASKDIALOG_BUTTON *tbuttons = button_count != 0 ? (TASKDIALOG_BUTTON *)alloca(sizeof(TASKDIALOG_BUTTON) * button_count) : nullptr;
3312
if (tbuttons) {
3313
for (int i = 0; i < button_count; i++) {
3314
tbuttons[i].nButtonID = i + 100;
3315
tbuttons[i].pszButtonText = (LPCWSTR)(buttons[i].get_data());
3316
}
3317
}
3318
config.pButtons = tbuttons;
3319
config.pfCallback = win32_task_dialog_callback;
3320
3321
Error result = FAILED;
3322
HMODULE comctl = LoadLibraryW(L"comctl32.dll");
3323
if (comctl) {
3324
typedef HRESULT(WINAPI * TaskDialogIndirectPtr)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);
3325
3326
TaskDialogIndirectPtr task_dialog_indirect = (TaskDialogIndirectPtr)(void *)GetProcAddress(comctl, "TaskDialogIndirect");
3327
int button_pressed;
3328
3329
if (task_dialog_indirect && SUCCEEDED(task_dialog_indirect(&config, &button_pressed, nullptr, nullptr))) {
3330
if (p_callback.is_valid()) {
3331
Variant button = button_pressed - 100;
3332
const Variant *args[1] = { &button };
3333
Variant ret;
3334
Callable::CallError ce;
3335
p_callback.callp(args, 1, ret, ce);
3336
if (ce.error != Callable::CallError::CALL_OK) {
3337
ERR_PRINT(vformat("Failed to execute dialog callback: %s.", Variant::get_callable_error_text(p_callback, args, 1, ce)));
3338
}
3339
}
3340
3341
result = OK;
3342
}
3343
FreeLibrary(comctl);
3344
} else {
3345
ERR_PRINT("Unable to create native dialog.");
3346
}
3347
3348
return result;
3349
}
3350
3351
struct Win32InputTextDialogInit {
3352
const char16_t *title;
3353
const char16_t *description;
3354
const char16_t *partial;
3355
const Callable &callback;
3356
};
3357
3358
static int scale_with_dpi(int p_pos, int p_dpi) {
3359
return IsProcessDPIAware() ? (p_pos * p_dpi / 96) : p_pos;
3360
}
3361
3362
static INT_PTR input_text_dialog_init(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
3363
Win32InputTextDialogInit init = *(Win32InputTextDialogInit *)lParam;
3364
SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)&init.callback); // Set dialog callback.
3365
3366
SetWindowTextW(hWnd, (LPCWSTR)init.title);
3367
3368
const int dpi = DisplayServerWindows::get_singleton()->screen_get_dpi();
3369
3370
const int margin = scale_with_dpi(7, dpi);
3371
const SIZE dlg_size = { scale_with_dpi(300, dpi), scale_with_dpi(50, dpi) };
3372
3373
int str_len = lstrlenW((LPCWSTR)init.description);
3374
SIZE str_size = { dlg_size.cx, 0 };
3375
if (str_len > 0) {
3376
HDC hdc = GetDC(nullptr);
3377
RECT trect = { margin, margin, margin + dlg_size.cx, margin + dlg_size.cy };
3378
SelectObject(hdc, (HFONT)SendMessageW(hWnd, WM_GETFONT, 0, 0));
3379
3380
// `+ margin` adds some space between the static text and the edit field.
3381
// Don't scale this with DPI because DPI is already handled by DrawText.
3382
str_size.cy = DrawTextW(hdc, (LPCWSTR)init.description, str_len, &trect, DT_LEFT | DT_WORDBREAK | DT_CALCRECT) + margin;
3383
3384
ReleaseDC(nullptr, hdc);
3385
}
3386
3387
RECT crect, wrect;
3388
GetClientRect(hWnd, &crect);
3389
GetWindowRect(hWnd, &wrect);
3390
int sw = GetSystemMetrics(SM_CXSCREEN);
3391
int sh = GetSystemMetrics(SM_CYSCREEN);
3392
int new_width = dlg_size.cx + margin * 2 + wrect.right - wrect.left - crect.right;
3393
int new_height = dlg_size.cy + margin * 2 + wrect.bottom - wrect.top - crect.bottom + str_size.cy;
3394
3395
MoveWindow(hWnd, (sw - new_width) / 2, (sh - new_height) / 2, new_width, new_height, true);
3396
3397
HWND ok_button = GetDlgItem(hWnd, 1);
3398
MoveWindow(ok_button,
3399
dlg_size.cx + margin - scale_with_dpi(65, dpi),
3400
dlg_size.cy + str_size.cy + margin - scale_with_dpi(20, dpi),
3401
scale_with_dpi(65, dpi), scale_with_dpi(20, dpi), true);
3402
3403
HWND description = GetDlgItem(hWnd, 3);
3404
MoveWindow(description, margin, margin, dlg_size.cx, str_size.cy, true);
3405
SetWindowTextW(description, (LPCWSTR)init.description);
3406
3407
HWND text_edit = GetDlgItem(hWnd, 2);
3408
MoveWindow(text_edit, margin, str_size.cy + margin, dlg_size.cx, scale_with_dpi(20, dpi), true);
3409
SetWindowTextW(text_edit, (LPCWSTR)init.partial);
3410
3411
return TRUE;
3412
}
3413
3414
static INT_PTR input_text_dialog_cmd_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
3415
if (LOWORD(wParam) == 1) {
3416
HWND text_edit = GetDlgItem(hWnd, 2);
3417
ERR_FAIL_NULL_V(text_edit, false);
3418
3419
Char16String text;
3420
text.resize_uninitialized(GetWindowTextLengthW(text_edit) + 1);
3421
GetWindowTextW(text_edit, (LPWSTR)text.get_data(), text.size());
3422
3423
const Callable *callback = (const Callable *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
3424
if (callback && callback->is_valid()) {
3425
Variant v_result = String((const wchar_t *)text.get_data());
3426
Variant ret;
3427
Callable::CallError ce;
3428
const Variant *args[1] = { &v_result };
3429
3430
callback->callp(args, 1, ret, ce);
3431
if (ce.error != Callable::CallError::CALL_OK) {
3432
ERR_PRINT(vformat("Failed to execute input dialog callback: %s.", Variant::get_callable_error_text(*callback, args, 1, ce)));
3433
}
3434
}
3435
3436
return EndDialog(hWnd, 0);
3437
}
3438
3439
return false;
3440
}
3441
3442
static INT_PTR CALLBACK input_text_dialog_proc(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
3443
switch (code) {
3444
case WM_INITDIALOG:
3445
return input_text_dialog_init(hWnd, code, wParam, lParam);
3446
3447
case WM_COMMAND:
3448
return input_text_dialog_cmd_proc(hWnd, code, wParam, lParam);
3449
3450
default:
3451
return FALSE;
3452
}
3453
}
3454
3455
Error DisplayServerWindows::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
3456
#pragma pack(push, 1)
3457
3458
// NOTE: Use default/placeholder coordinates here. Windows uses its own coordinate system
3459
// specifically for dialogs which relies on font sizes instead of pixels.
3460
const struct {
3461
WORD dlgVer; // must be 1
3462
WORD signature; // must be 0xFFFF
3463
DWORD helpID;
3464
DWORD exStyle;
3465
DWORD style;
3466
WORD cDlgItems;
3467
short x;
3468
short y;
3469
short cx;
3470
short cy;
3471
WCHAR menu[1]; // must be 0
3472
WCHAR windowClass[7]; // must be "#32770" -- the default window class for dialogs
3473
WCHAR title[1]; // must be 0
3474
WORD pointsize;
3475
WORD weight;
3476
BYTE italic;
3477
BYTE charset;
3478
WCHAR font[13]; // must be "MS Shell Dlg"
3479
} template_base = {
3480
1, 0xFFFF, 0, 0,
3481
DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION,
3482
3, 0, 0, 20, 20, L"", L"#32770", L"", 8, FW_NORMAL, 0, DEFAULT_CHARSET, L"MS Shell Dlg"
3483
};
3484
3485
const struct {
3486
DWORD helpID;
3487
DWORD exStyle;
3488
DWORD style;
3489
short x;
3490
short y;
3491
short cx;
3492
short cy;
3493
DWORD id;
3494
WCHAR windowClass[7]; // must be "Button"
3495
WCHAR title[3]; // must be "OK"
3496
WORD extraCount;
3497
} ok_button = {
3498
0, 0, WS_VISIBLE | BS_DEFPUSHBUTTON, 0, 0, 50, 14, 1, WC_BUTTONW, L"OK", 0
3499
};
3500
const struct {
3501
DWORD helpID;
3502
DWORD exStyle;
3503
DWORD style;
3504
short x;
3505
short y;
3506
short cx;
3507
short cy;
3508
DWORD id;
3509
WCHAR windowClass[5]; // must be "Edit"
3510
WCHAR title[1]; // must be 0
3511
WORD extraCount;
3512
} text_field = {
3513
0, 0, WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 0, 0, 250, 14, 2, WC_EDITW, L"", 0
3514
};
3515
const struct {
3516
DWORD helpID;
3517
DWORD exStyle;
3518
DWORD style;
3519
short x;
3520
short y;
3521
short cx;
3522
short cy;
3523
DWORD id;
3524
WCHAR windowClass[7]; // must be "Static"
3525
WCHAR title[1]; // must be 0
3526
WORD extraCount;
3527
} static_text = {
3528
0, 0, WS_VISIBLE, 0, 0, 250, 14, 3, WC_STATICW, L"", 0
3529
};
3530
3531
#pragma pack(pop)
3532
3533
// Dialog template
3534
const size_t data_size = sizeof(template_base) + (sizeof(template_base) % 4) +
3535
sizeof(ok_button) + (sizeof(ok_button) % 4) +
3536
sizeof(text_field) + (sizeof(text_field) % 4) +
3537
sizeof(static_text) + (sizeof(static_text) % 4);
3538
3539
void *data_template = memalloc(data_size);
3540
ERR_FAIL_NULL_V_MSG(data_template, FAILED, "Unable to allocate memory for the dialog template.");
3541
ZeroMemory(data_template, data_size);
3542
3543
char *current_block = (char *)data_template;
3544
CopyMemory(current_block, &template_base, sizeof(template_base));
3545
current_block += sizeof(template_base) + (sizeof(template_base) % 4);
3546
CopyMemory(current_block, &ok_button, sizeof(ok_button));
3547
current_block += sizeof(ok_button) + (sizeof(ok_button) % 4);
3548
CopyMemory(current_block, &text_field, sizeof(text_field));
3549
current_block += sizeof(text_field) + (sizeof(text_field) % 4);
3550
CopyMemory(current_block, &static_text, sizeof(static_text));
3551
3552
Char16String title16 = p_title.utf16();
3553
Char16String description16 = p_description.utf16();
3554
Char16String partial16 = p_partial.utf16();
3555
3556
Win32InputTextDialogInit init = {
3557
title16.get_data(), description16.get_data(), partial16.get_data(), p_callback
3558
};
3559
3560
// No modal dialogs for specific windows? Assume main window here.
3561
INT_PTR ret = DialogBoxIndirectParamW(hInstance, (LPDLGTEMPLATEW)data_template, nullptr, (DLGPROC)input_text_dialog_proc, (LPARAM)(&init));
3562
3563
Error result = ret != -1 ? OK : FAILED;
3564
memfree(data_template);
3565
3566
if (result == FAILED) {
3567
ERR_PRINT("Unable to create native dialog.");
3568
}
3569
return result;
3570
}
3571
3572
int DisplayServerWindows::keyboard_get_layout_count() const {
3573
return GetKeyboardLayoutList(0, nullptr);
3574
}
3575
3576
int DisplayServerWindows::keyboard_get_current_layout() const {
3577
HKL cur_layout = GetKeyboardLayout(0);
3578
3579
int layout_count = GetKeyboardLayoutList(0, nullptr);
3580
HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
3581
GetKeyboardLayoutList(layout_count, layouts);
3582
3583
for (int i = 0; i < layout_count; i++) {
3584
if (cur_layout == layouts[i]) {
3585
memfree(layouts);
3586
return i;
3587
}
3588
}
3589
memfree(layouts);
3590
return -1;
3591
}
3592
3593
void DisplayServerWindows::keyboard_set_current_layout(int p_index) {
3594
int layout_count = GetKeyboardLayoutList(0, nullptr);
3595
3596
ERR_FAIL_INDEX(p_index, layout_count);
3597
3598
HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
3599
GetKeyboardLayoutList(layout_count, layouts);
3600
ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS);
3601
memfree(layouts);
3602
}
3603
3604
String DisplayServerWindows::keyboard_get_layout_language(int p_index) const {
3605
int layout_count = GetKeyboardLayoutList(0, nullptr);
3606
3607
ERR_FAIL_INDEX_V(p_index, layout_count, "");
3608
3609
HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
3610
GetKeyboardLayoutList(layout_count, layouts);
3611
3612
WCHAR buf[LOCALE_NAME_MAX_LENGTH];
3613
memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));
3614
LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);
3615
3616
memfree(layouts);
3617
3618
return String::utf16((const char16_t *)buf).substr(0, 2);
3619
}
3620
3621
Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) const {
3622
Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
3623
Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);
3624
3625
if (keycode_no_mod == Key::PRINT ||
3626
keycode_no_mod == Key::KP_ADD ||
3627
keycode_no_mod == Key::KP_5 ||
3628
(keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {
3629
return p_keycode;
3630
}
3631
3632
unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);
3633
if (scancode == 0) {
3634
return p_keycode;
3635
}
3636
3637
HKL current_layout = GetKeyboardLayout(0);
3638
UINT vk = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK, current_layout);
3639
if (vk == 0) {
3640
return p_keycode;
3641
}
3642
3643
UINT char_code = MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, current_layout) & 0x7FFF;
3644
// Unlike a similar Linux/BSD check which matches full Latin-1 range,
3645
// we limit these to ASCII to fix some layouts, including Arabic ones
3646
if (char_code >= 32 && char_code <= 127) {
3647
// Godot uses 'braces' instead of 'brackets'
3648
if (char_code == (unsigned int)Key::BRACKETLEFT || char_code == (unsigned int)Key::BRACKETRIGHT) {
3649
char_code += 32;
3650
}
3651
return (Key)(char_code | (unsigned int)modifiers);
3652
}
3653
3654
return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers);
3655
}
3656
3657
Key DisplayServerWindows::keyboard_get_label_from_physical(Key p_keycode) const {
3658
Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
3659
Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);
3660
3661
if (keycode_no_mod == Key::PRINT ||
3662
keycode_no_mod == Key::KP_ADD ||
3663
keycode_no_mod == Key::KP_5 ||
3664
(keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {
3665
return p_keycode;
3666
}
3667
3668
unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);
3669
if (scancode == 0) {
3670
return p_keycode;
3671
}
3672
3673
Key keycode = KeyMappingWindows::get_keysym(MapVirtualKey(scancode, MAPVK_VSC_TO_VK));
3674
3675
HKL current_layout = GetKeyboardLayout(0);
3676
static BYTE keyboard_state[256];
3677
memset(keyboard_state, 0, 256);
3678
wchar_t chars[256] = {};
3679
UINT extended_code = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);
3680
if (ToUnicodeEx(extended_code, scancode, keyboard_state, chars, 255, 4, current_layout) > 0) {
3681
String keysym = String::utf16((char16_t *)chars, 255);
3682
if (!keysym.is_empty()) {
3683
return fix_key_label(keysym[0], keycode) | modifiers;
3684
}
3685
}
3686
return p_keycode;
3687
}
3688
3689
void DisplayServerWindows::show_emoji_and_symbol_picker() const {
3690
// Send Win + Period shortcut, there's no non-WinRT public API.
3691
3692
INPUT input[4] = {};
3693
input[0].type = INPUT_KEYBOARD; // Win down.
3694
input[0].ki.wVk = VK_LWIN;
3695
3696
input[1].type = INPUT_KEYBOARD; // Period down.
3697
input[1].ki.wVk = VK_OEM_PERIOD;
3698
3699
input[2].type = INPUT_KEYBOARD; // Win up.
3700
input[2].ki.wVk = VK_LWIN;
3701
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
3702
3703
input[3].type = INPUT_KEYBOARD; // Period up.
3704
input[3].ki.wVk = VK_OEM_PERIOD;
3705
input[3].ki.dwFlags = KEYEVENTF_KEYUP;
3706
3707
SendInput(4, input, sizeof(INPUT));
3708
}
3709
3710
String DisplayServerWindows::_get_keyboard_layout_display_name(const String &p_klid) const {
3711
String ret;
3712
HKEY key;
3713
if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {
3714
return String();
3715
}
3716
3717
WCHAR buffer[MAX_PATH] = {};
3718
DWORD buffer_size = MAX_PATH;
3719
if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Display Name", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {
3720
if (SHLoadIndirectString(buffer, buffer, buffer_size, nullptr) == S_OK) {
3721
ret = String::utf16((const char16_t *)buffer, buffer_size);
3722
}
3723
} else {
3724
if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Text", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {
3725
ret = String::utf16((const char16_t *)buffer, buffer_size);
3726
}
3727
}
3728
3729
RegCloseKey(key);
3730
return ret;
3731
}
3732
3733
String DisplayServerWindows::_get_klid(HKL p_hkl) const {
3734
String ret;
3735
3736
WORD device = HIWORD(p_hkl);
3737
if ((device & 0xf000) == 0xf000) {
3738
WORD layout_id = device & 0x0fff;
3739
3740
HKEY key;
3741
if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {
3742
return String();
3743
}
3744
3745
DWORD index = 0;
3746
wchar_t klid_buffer[KL_NAMELENGTH];
3747
DWORD klid_buffer_size = KL_NAMELENGTH;
3748
while (RegEnumKeyExW(key, index, klid_buffer, &klid_buffer_size, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) {
3749
wchar_t layout_id_buf[MAX_PATH] = {};
3750
DWORD layout_id_size = MAX_PATH;
3751
if (RegGetValueW(key, klid_buffer, L"Layout Id", RRF_RT_REG_SZ, nullptr, layout_id_buf, &layout_id_size) == ERROR_SUCCESS) {
3752
if (layout_id == String::utf16((char16_t *)layout_id_buf, layout_id_size).hex_to_int()) {
3753
ret = String::utf16((const char16_t *)klid_buffer, klid_buffer_size).lpad(8, "0");
3754
break;
3755
}
3756
}
3757
klid_buffer_size = KL_NAMELENGTH;
3758
++index;
3759
}
3760
3761
RegCloseKey(key);
3762
} else {
3763
if (device == 0) {
3764
device = LOWORD(p_hkl);
3765
}
3766
ret = (String::num_uint64((uint64_t)device, 16, false)).lpad(8, "0");
3767
}
3768
3769
return ret;
3770
}
3771
3772
String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
3773
int layout_count = GetKeyboardLayoutList(0, nullptr);
3774
3775
ERR_FAIL_INDEX_V(p_index, layout_count, "");
3776
3777
HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
3778
GetKeyboardLayoutList(layout_count, layouts);
3779
3780
String ret = _get_keyboard_layout_display_name(_get_klid(layouts[p_index])); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine).
3781
if (ret.is_empty()) {
3782
WCHAR buf[LOCALE_NAME_MAX_LENGTH];
3783
memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));
3784
LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);
3785
3786
WCHAR name[1024];
3787
memset(name, 0, 1024 * sizeof(WCHAR));
3788
GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024);
3789
3790
ret = String::utf16((const char16_t *)name);
3791
}
3792
memfree(layouts);
3793
3794
return ret;
3795
}
3796
3797
void DisplayServerWindows::process_events() {
3798
ERR_FAIL_COND(!Thread::is_main_thread());
3799
3800
if (!drop_events) {
3801
#ifdef SDL_ENABLED
3802
if (joypad_sdl) {
3803
joypad_sdl->process_events();
3804
}
3805
#endif
3806
}
3807
3808
_THREAD_SAFE_LOCK_
3809
MSG msg = {};
3810
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
3811
TranslateMessage(&msg);
3812
DispatchMessageW(&msg);
3813
}
3814
_THREAD_SAFE_UNLOCK_
3815
3816
if (tts) {
3817
tts->process_events();
3818
}
3819
3820
if (!drop_events) {
3821
_process_key_events();
3822
Input::get_singleton()->flush_buffered_events();
3823
}
3824
3825
LocalVector<List<FileDialogData *>::Element *> to_remove;
3826
for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {
3827
FileDialogData *fd = E->get();
3828
if (fd->finished.is_set()) {
3829
if (fd->listener_thread.is_started()) {
3830
fd->listener_thread.wait_to_finish();
3831
}
3832
to_remove.push_back(E);
3833
}
3834
}
3835
for (List<FileDialogData *>::Element *E : to_remove) {
3836
memdelete(E->get());
3837
E->erase();
3838
}
3839
process_file_dialog_callbacks();
3840
}
3841
3842
void DisplayServerWindows::force_process_and_drop_events() {
3843
ERR_FAIL_COND(!Thread::is_main_thread());
3844
3845
drop_events = true;
3846
process_events();
3847
drop_events = false;
3848
}
3849
3850
void DisplayServerWindows::release_rendering_thread() {
3851
#if defined(GLES3_ENABLED)
3852
if (gl_manager_angle) {
3853
gl_manager_angle->release_current();
3854
}
3855
if (gl_manager_native) {
3856
gl_manager_native->release_current();
3857
}
3858
#endif
3859
}
3860
3861
void DisplayServerWindows::swap_buffers() {
3862
#if defined(GLES3_ENABLED)
3863
if (gl_manager_angle) {
3864
gl_manager_angle->swap_buffers();
3865
}
3866
if (gl_manager_native) {
3867
gl_manager_native->swap_buffers();
3868
}
3869
#endif
3870
}
3871
3872
void DisplayServerWindows::set_native_icon(const String &p_filename) {
3873
_THREAD_SAFE_METHOD_
3874
3875
Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);
3876
ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'.");
3877
3878
ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));
3879
int pos = 0;
3880
3881
icon_dir->idReserved = f->get_32();
3882
pos += sizeof(WORD);
3883
f->seek(pos);
3884
3885
icon_dir->idType = f->get_32();
3886
pos += sizeof(WORD);
3887
f->seek(pos);
3888
3889
ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!");
3890
3891
icon_dir->idCount = f->get_32();
3892
pos += sizeof(WORD);
3893
f->seek(pos);
3894
3895
icon_dir = (ICONDIR *)memrealloc(icon_dir, sizeof(ICONDIR) - sizeof(ICONDIRENTRY) + icon_dir->idCount * sizeof(ICONDIRENTRY));
3896
f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY));
3897
3898
int small_icon_index = -1; // Select 16x16 with largest color count.
3899
int small_icon_cc = 0;
3900
int big_icon_index = -1; // Select largest.
3901
int big_icon_width = 16;
3902
int big_icon_cc = 0;
3903
3904
for (int i = 0; i < icon_dir->idCount; i++) {
3905
int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount;
3906
int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth;
3907
if (width == 16) {
3908
if (colors >= small_icon_cc) {
3909
small_icon_index = i;
3910
small_icon_cc = colors;
3911
}
3912
}
3913
if (width >= big_icon_width) {
3914
if (colors >= big_icon_cc) {
3915
big_icon_index = i;
3916
big_icon_width = width;
3917
big_icon_cc = colors;
3918
}
3919
}
3920
}
3921
3922
ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!");
3923
3924
if (small_icon_index == -1) {
3925
WARN_PRINT("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!");
3926
small_icon_index = big_icon_index;
3927
small_icon_cc = big_icon_cc;
3928
}
3929
3930
// Read the big icon.
3931
DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes;
3932
Vector<uint8_t> data_big;
3933
data_big.resize(bytecount_big);
3934
pos = icon_dir->idEntries[big_icon_index].dwImageOffset;
3935
f->seek(pos);
3936
f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big);
3937
HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000);
3938
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()) + ".");
3939
3940
// Read the small icon.
3941
DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;
3942
Vector<uint8_t> data_small;
3943
data_small.resize(bytecount_small);
3944
pos = icon_dir->idEntries[small_icon_index].dwImageOffset;
3945
f->seek(pos);
3946
f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small);
3947
HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000);
3948
ERR_FAIL_NULL_MSG(icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
3949
3950
// Online tradition says to be sure last error is cleared and set the small icon first.
3951
int err = 0;
3952
SetLastError(err);
3953
3954
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
3955
err = GetLastError();
3956
ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + ".");
3957
3958
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
3959
err = GetLastError();
3960
ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
3961
3962
memdelete(icon_dir);
3963
}
3964
3965
void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
3966
_THREAD_SAFE_METHOD_
3967
3968
if (p_icon.is_valid()) {
3969
ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);
3970
3971
Ref<Image> img = p_icon;
3972
if (img != icon) {
3973
img = img->duplicate();
3974
img->convert(Image::FORMAT_RGBA8);
3975
}
3976
3977
int w = img->get_width();
3978
int h = img->get_height();
3979
3980
// Create temporary bitmap buffer.
3981
int icon_len = 40 + h * w * 4;
3982
Vector<BYTE> v;
3983
v.resize(icon_len);
3984
BYTE *icon_bmp = v.ptrw();
3985
3986
encode_uint32(40, &icon_bmp[0]);
3987
encode_uint32(w, &icon_bmp[4]);
3988
encode_uint32(h * 2, &icon_bmp[8]);
3989
encode_uint16(1, &icon_bmp[12]);
3990
encode_uint16(32, &icon_bmp[14]);
3991
encode_uint32(BI_RGB, &icon_bmp[16]);
3992
encode_uint32(w * h * 4, &icon_bmp[20]);
3993
encode_uint32(0, &icon_bmp[24]);
3994
encode_uint32(0, &icon_bmp[28]);
3995
encode_uint32(0, &icon_bmp[32]);
3996
encode_uint32(0, &icon_bmp[36]);
3997
3998
uint8_t *wr = &icon_bmp[40];
3999
const uint8_t *r = img->get_data().ptr();
4000
4001
for (int i = 0; i < h; i++) {
4002
for (int j = 0; j < w; j++) {
4003
const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
4004
uint8_t *wpx = &wr[(i * w + j) * 4];
4005
wpx[0] = rpx[2];
4006
wpx[1] = rpx[1];
4007
wpx[2] = rpx[0];
4008
wpx[3] = rpx[3];
4009
}
4010
}
4011
4012
HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
4013
ERR_FAIL_NULL(hicon);
4014
4015
icon = img;
4016
4017
// Set the icon for the window.
4018
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
4019
4020
// Set the icon in the task manager (should we do this?).
4021
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
4022
} else {
4023
icon = Ref<Image>();
4024
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, 0);
4025
SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, 0);
4026
}
4027
}
4028
4029
DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) {
4030
HICON hicon = nullptr;
4031
if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
4032
Ref<Image> img = p_icon->get_image();
4033
img = img->duplicate();
4034
if (img->is_compressed()) {
4035
img->decompress();
4036
}
4037
img->convert(Image::FORMAT_RGBA8);
4038
4039
int w = img->get_width();
4040
int h = img->get_height();
4041
4042
// Create temporary bitmap buffer.
4043
int icon_len = 40 + h * w * 4;
4044
Vector<BYTE> v;
4045
v.resize(icon_len);
4046
BYTE *icon_bmp = v.ptrw();
4047
4048
encode_uint32(40, &icon_bmp[0]);
4049
encode_uint32(w, &icon_bmp[4]);
4050
encode_uint32(h * 2, &icon_bmp[8]);
4051
encode_uint16(1, &icon_bmp[12]);
4052
encode_uint16(32, &icon_bmp[14]);
4053
encode_uint32(BI_RGB, &icon_bmp[16]);
4054
encode_uint32(w * h * 4, &icon_bmp[20]);
4055
encode_uint32(0, &icon_bmp[24]);
4056
encode_uint32(0, &icon_bmp[28]);
4057
encode_uint32(0, &icon_bmp[32]);
4058
encode_uint32(0, &icon_bmp[36]);
4059
4060
uint8_t *wr = &icon_bmp[40];
4061
const uint8_t *r = img->get_data().ptr();
4062
4063
for (int i = 0; i < h; i++) {
4064
for (int j = 0; j < w; j++) {
4065
const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
4066
uint8_t *wpx = &wr[(i * w + j) * 4];
4067
wpx[0] = rpx[2];
4068
wpx[1] = rpx[1];
4069
wpx[2] = rpx[0];
4070
wpx[3] = rpx[3];
4071
}
4072
}
4073
4074
hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
4075
}
4076
4077
IndicatorData idat;
4078
idat.callback = p_callback;
4079
4080
NOTIFYICONDATAW ndat;
4081
ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
4082
ndat.cbSize = sizeof(NOTIFYICONDATAW);
4083
ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
4084
ndat.uID = indicator_id_counter;
4085
ndat.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
4086
ndat.uCallbackMessage = WM_INDICATOR_CALLBACK_MESSAGE;
4087
ndat.hIcon = hicon;
4088
memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));
4089
ndat.uVersion = NOTIFYICON_VERSION;
4090
4091
Shell_NotifyIconW(NIM_ADD, &ndat);
4092
Shell_NotifyIconW(NIM_SETVERSION, &ndat);
4093
4094
IndicatorID iid = indicator_id_counter++;
4095
indicators[iid] = idat;
4096
4097
return iid;
4098
}
4099
4100
void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon) {
4101
ERR_FAIL_COND(!indicators.has(p_id));
4102
4103
HICON hicon = nullptr;
4104
if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
4105
Ref<Image> img = p_icon->get_image();
4106
img = img->duplicate();
4107
if (img->is_compressed()) {
4108
img->decompress();
4109
}
4110
img->convert(Image::FORMAT_RGBA8);
4111
4112
int w = img->get_width();
4113
int h = img->get_height();
4114
4115
// Create temporary bitmap buffer.
4116
int icon_len = 40 + h * w * 4;
4117
Vector<BYTE> v;
4118
v.resize(icon_len);
4119
BYTE *icon_bmp = v.ptrw();
4120
4121
encode_uint32(40, &icon_bmp[0]);
4122
encode_uint32(w, &icon_bmp[4]);
4123
encode_uint32(h * 2, &icon_bmp[8]);
4124
encode_uint16(1, &icon_bmp[12]);
4125
encode_uint16(32, &icon_bmp[14]);
4126
encode_uint32(BI_RGB, &icon_bmp[16]);
4127
encode_uint32(w * h * 4, &icon_bmp[20]);
4128
encode_uint32(0, &icon_bmp[24]);
4129
encode_uint32(0, &icon_bmp[28]);
4130
encode_uint32(0, &icon_bmp[32]);
4131
encode_uint32(0, &icon_bmp[36]);
4132
4133
uint8_t *wr = &icon_bmp[40];
4134
const uint8_t *r = img->get_data().ptr();
4135
4136
for (int i = 0; i < h; i++) {
4137
for (int j = 0; j < w; j++) {
4138
const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
4139
uint8_t *wpx = &wr[(i * w + j) * 4];
4140
wpx[0] = rpx[2];
4141
wpx[1] = rpx[1];
4142
wpx[2] = rpx[0];
4143
wpx[3] = rpx[3];
4144
}
4145
}
4146
4147
hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
4148
}
4149
4150
NOTIFYICONDATAW ndat;
4151
ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
4152
ndat.cbSize = sizeof(NOTIFYICONDATAW);
4153
ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
4154
ndat.uID = p_id;
4155
ndat.uFlags = NIF_ICON;
4156
ndat.hIcon = hicon;
4157
ndat.uVersion = NOTIFYICON_VERSION;
4158
4159
Shell_NotifyIconW(NIM_MODIFY, &ndat);
4160
}
4161
4162
void DisplayServerWindows::status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) {
4163
ERR_FAIL_COND(!indicators.has(p_id));
4164
4165
NOTIFYICONDATAW ndat;
4166
ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
4167
ndat.cbSize = sizeof(NOTIFYICONDATAW);
4168
ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
4169
ndat.uID = p_id;
4170
ndat.uFlags = NIF_TIP;
4171
memcpy(ndat.szTip, p_tooltip.utf16().get_data(), MIN(p_tooltip.utf16().length(), 127) * sizeof(WCHAR));
4172
ndat.uVersion = NOTIFYICON_VERSION;
4173
4174
Shell_NotifyIconW(NIM_MODIFY, &ndat);
4175
}
4176
4177
void DisplayServerWindows::status_indicator_set_menu(IndicatorID p_id, const RID &p_menu_rid) {
4178
ERR_FAIL_COND(!indicators.has(p_id));
4179
4180
indicators[p_id].menu_rid = p_menu_rid;
4181
}
4182
4183
void DisplayServerWindows::status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) {
4184
ERR_FAIL_COND(!indicators.has(p_id));
4185
4186
indicators[p_id].callback = p_callback;
4187
}
4188
4189
Rect2 DisplayServerWindows::status_indicator_get_rect(IndicatorID p_id) const {
4190
ERR_FAIL_COND_V(!indicators.has(p_id), Rect2());
4191
4192
NOTIFYICONIDENTIFIER nid;
4193
ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));
4194
nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);
4195
nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;
4196
nid.uID = p_id;
4197
nid.guidItem = GUID_NULL;
4198
4199
RECT rect;
4200
if (Shell_NotifyIconGetRect(&nid, &rect) != S_OK) {
4201
return Rect2();
4202
}
4203
Rect2 ind_rect = Rect2(Point2(rect.left, rect.top) - _get_screens_origin(), Size2(rect.right - rect.left, rect.bottom - rect.top));
4204
for (int i = 0; i < get_screen_count(); i++) {
4205
Rect2 screen_rect = Rect2(screen_get_position(i), screen_get_size(i));
4206
if (screen_rect.encloses(ind_rect)) {
4207
return ind_rect;
4208
}
4209
}
4210
return Rect2();
4211
}
4212
4213
void DisplayServerWindows::delete_status_indicator(IndicatorID p_id) {
4214
ERR_FAIL_COND(!indicators.has(p_id));
4215
4216
NOTIFYICONDATAW ndat;
4217
ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
4218
ndat.cbSize = sizeof(NOTIFYICONDATAW);
4219
ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
4220
ndat.uID = p_id;
4221
ndat.uVersion = NOTIFYICON_VERSION;
4222
4223
Shell_NotifyIconW(NIM_DELETE, &ndat);
4224
indicators.erase(p_id);
4225
}
4226
4227
void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
4228
_THREAD_SAFE_METHOD_
4229
#if defined(RD_ENABLED)
4230
if (rendering_context) {
4231
rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
4232
}
4233
#endif
4234
4235
#if defined(GLES3_ENABLED)
4236
if (gl_manager_native) {
4237
gl_manager_native->set_use_vsync(p_window, p_vsync_mode != DisplayServer::VSYNC_DISABLED);
4238
}
4239
if (gl_manager_angle) {
4240
gl_manager_angle->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
4241
}
4242
#endif
4243
}
4244
4245
DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {
4246
_THREAD_SAFE_METHOD_
4247
#if defined(RD_ENABLED)
4248
if (rendering_context) {
4249
return rendering_context->window_get_vsync_mode(p_window);
4250
}
4251
#endif
4252
4253
#if defined(GLES3_ENABLED)
4254
if (gl_manager_native) {
4255
return gl_manager_native->is_using_vsync(p_window) ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
4256
}
4257
if (gl_manager_angle) {
4258
return gl_manager_angle->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
4259
}
4260
#endif
4261
return DisplayServer::VSYNC_ENABLED;
4262
}
4263
4264
void DisplayServerWindows::window_start_drag(WindowID p_window) {
4265
_THREAD_SAFE_METHOD_
4266
4267
ERR_FAIL_COND(!windows.has(p_window));
4268
WindowData &wd = windows[p_window];
4269
4270
if (wd.parent_hwnd) {
4271
return; // Embedded window.
4272
}
4273
4274
ReleaseCapture();
4275
4276
POINT coords;
4277
GetCursorPos(&coords);
4278
ScreenToClient(wd.hWnd, &coords);
4279
4280
SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(coords.x, coords.y));
4281
4282
for (int btn = (int)MouseButton::LEFT; btn <= (int)MouseButton::MB_XBUTTON2; btn++) {
4283
if (Input::get_singleton()->is_mouse_button_pressed(MouseButton(btn))) {
4284
Ref<InputEventMouseButton> mb;
4285
mb.instantiate();
4286
mb->set_window_id(p_window);
4287
mb->set_pressed(false);
4288
mb->set_button_index(MouseButton::LEFT);
4289
mb->set_position(Vector2(coords.x, coords.y));
4290
mb->set_global_position(mb->get_position());
4291
Input::get_singleton()->parse_input_event(mb);
4292
}
4293
}
4294
}
4295
4296
void DisplayServerWindows::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {
4297
_THREAD_SAFE_METHOD_
4298
4299
ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);
4300
ERR_FAIL_COND(!windows.has(p_window));
4301
WindowData &wd = windows[p_window];
4302
4303
if (wd.parent_hwnd) {
4304
return; // Embedded window.
4305
}
4306
4307
ReleaseCapture();
4308
4309
POINT coords;
4310
GetCursorPos(&coords);
4311
ScreenToClient(wd.hWnd, &coords);
4312
4313
DWORD op = 0;
4314
switch (p_edge) {
4315
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
4316
op = WMSZ_TOPLEFT;
4317
} break;
4318
case DisplayServer::WINDOW_EDGE_TOP: {
4319
op = WMSZ_TOP;
4320
} break;
4321
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
4322
op = WMSZ_TOPRIGHT;
4323
} break;
4324
case DisplayServer::WINDOW_EDGE_LEFT: {
4325
op = WMSZ_LEFT;
4326
} break;
4327
case DisplayServer::WINDOW_EDGE_RIGHT: {
4328
op = WMSZ_RIGHT;
4329
} break;
4330
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
4331
op = WMSZ_BOTTOMLEFT;
4332
} break;
4333
case DisplayServer::WINDOW_EDGE_BOTTOM: {
4334
op = WMSZ_BOTTOM;
4335
} break;
4336
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
4337
op = WMSZ_BOTTOMRIGHT;
4338
} break;
4339
default:
4340
break;
4341
}
4342
4343
SendMessage(wd.hWnd, WM_SYSCOMMAND, SC_SIZE | op, MAKELPARAM(coords.x, coords.y));
4344
4345
for (int btn = (int)MouseButton::LEFT; btn <= (int)MouseButton::MB_XBUTTON2; btn++) {
4346
if (Input::get_singleton()->is_mouse_button_pressed(MouseButton(btn))) {
4347
Ref<InputEventMouseButton> mb;
4348
mb.instantiate();
4349
mb->set_window_id(p_window);
4350
mb->set_pressed(false);
4351
mb->set_button_index(MouseButton::LEFT);
4352
mb->set_position(Vector2(coords.x, coords.y));
4353
mb->set_global_position(mb->get_position());
4354
Input::get_singleton()->parse_input_event(mb);
4355
}
4356
}
4357
}
4358
4359
void DisplayServerWindows::set_context(Context p_context) {
4360
}
4361
4362
bool DisplayServerWindows::is_window_transparency_available() const {
4363
#if defined(RD_ENABLED)
4364
if (rendering_device && !rendering_device->is_composite_alpha_supported()) {
4365
return false;
4366
}
4367
#endif
4368
return OS::get_singleton()->is_layered_allowed();
4369
}
4370
4371
#define MI_WP_SIGNATURE 0xFF515700
4372
#define SIGNATURE_MASK 0xFFFFFF00
4373
// Keeping the name suggested by Microsoft, but this macro really answers:
4374
// Is this mouse event emulated from touch or pen input?
4375
#define IsPenEvent(dw) (((dw) & SIGNATURE_MASK) == MI_WP_SIGNATURE)
4376
// This one tells whether the event comes from touchscreen (and not from pen).
4377
#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw) & 0x80))
4378
4379
void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx) {
4380
if (touch_state.has(idx) == p_pressed) {
4381
return;
4382
}
4383
4384
if (p_pressed) {
4385
touch_state.insert(idx, Vector2(p_x, p_y));
4386
} else {
4387
touch_state.erase(idx);
4388
}
4389
4390
Ref<InputEventScreenTouch> event;
4391
event.instantiate();
4392
event->set_index(idx);
4393
event->set_window_id(p_window);
4394
event->set_pressed(p_pressed);
4395
event->set_position(Vector2(p_x, p_y));
4396
4397
Input::get_singleton()->parse_input_event(event);
4398
}
4399
4400
void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) {
4401
RBMap<int, Vector2>::Element *curr = touch_state.find(idx);
4402
if (!curr) {
4403
return;
4404
}
4405
4406
if (curr->get() == Vector2(p_x, p_y)) {
4407
return;
4408
}
4409
4410
Ref<InputEventScreenDrag> event;
4411
event.instantiate();
4412
event->set_window_id(p_window);
4413
event->set_index(idx);
4414
event->set_position(Vector2(p_x, p_y));
4415
event->set_relative(Vector2(p_x, p_y) - curr->get());
4416
event->set_relative_screen_position(event->get_relative());
4417
4418
Input::get_singleton()->parse_input_event(event);
4419
4420
curr->get() = Vector2(p_x, p_y);
4421
}
4422
4423
void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent p_event) {
4424
if (wd.event_callback.is_valid()) {
4425
Variant event = int(p_event);
4426
wd.event_callback.call(event);
4427
}
4428
}
4429
4430
void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {
4431
static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event);
4432
}
4433
4434
void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {
4435
if (in_dispatch_input_event) {
4436
return;
4437
}
4438
in_dispatch_input_event = true;
4439
4440
{
4441
List<WindowID>::Element *E = popup_list.back();
4442
if (E && Object::cast_to<InputEventKey>(*p_event)) {
4443
// Redirect keyboard input to active popup.
4444
if (windows.has(E->get())) {
4445
Callable callable = windows[E->get()].input_event_callback;
4446
if (callable.is_valid()) {
4447
callable.call(p_event);
4448
}
4449
}
4450
in_dispatch_input_event = false;
4451
return;
4452
}
4453
}
4454
4455
Ref<InputEventFromWindow> event_from_window = p_event;
4456
if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {
4457
// Send to a single window.
4458
if (windows.has(event_from_window->get_window_id())) {
4459
Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
4460
if (callable.is_valid()) {
4461
callable.call(p_event);
4462
}
4463
}
4464
} else {
4465
// Send to all windows. Copy all pending callbacks, since callback can erase window.
4466
Vector<Callable> cbs;
4467
for (KeyValue<WindowID, WindowData> &E : windows) {
4468
Callable callable = E.value.input_event_callback;
4469
if (callable.is_valid()) {
4470
cbs.push_back(callable);
4471
}
4472
}
4473
for (const Callable &cb : cbs) {
4474
cb.call(p_event);
4475
}
4476
}
4477
4478
in_dispatch_input_event = false;
4479
}
4480
4481
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {
4482
DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());
4483
if (ds_win) {
4484
return ds_win->MouseProc(code, wParam, lParam);
4485
} else {
4486
return ::CallNextHookEx(nullptr, code, wParam, lParam);
4487
}
4488
}
4489
4490
DisplayServer::WindowID DisplayServerWindows::window_get_active_popup() const {
4491
const List<WindowID>::Element *E = popup_list.back();
4492
if (E) {
4493
return E->get();
4494
} else {
4495
return INVALID_WINDOW_ID;
4496
}
4497
}
4498
4499
void DisplayServerWindows::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {
4500
_THREAD_SAFE_METHOD_
4501
4502
ERR_FAIL_COND(!windows.has(p_window));
4503
WindowData &wd = windows[p_window];
4504
wd.parent_safe_rect = p_rect;
4505
}
4506
4507
Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const {
4508
_THREAD_SAFE_METHOD_
4509
4510
ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());
4511
const WindowData &wd = windows[p_window];
4512
return wd.parent_safe_rect;
4513
}
4514
4515
void DisplayServerWindows::popup_open(WindowID p_window) {
4516
_THREAD_SAFE_METHOD_
4517
4518
bool has_popup_ancestor = false;
4519
WindowID transient_root = p_window;
4520
while (true) {
4521
WindowID parent = windows[transient_root].transient_parent;
4522
if (parent == INVALID_WINDOW_ID) {
4523
break;
4524
} else {
4525
transient_root = parent;
4526
if (windows[parent].is_popup) {
4527
has_popup_ancestor = true;
4528
break;
4529
}
4530
}
4531
}
4532
4533
// Detect tooltips and other similar popups that shouldn't block input to their parent.
4534
bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);
4535
4536
WindowData &wd = windows[p_window];
4537
if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {
4538
// Find current popup parent, or root popup if new window is not transient.
4539
List<WindowID>::Element *C = nullptr;
4540
List<WindowID>::Element *E = popup_list.back();
4541
while (E) {
4542
if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {
4543
C = E;
4544
E = E->prev();
4545
} else {
4546
break;
4547
}
4548
}
4549
if (C) {
4550
_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
4551
}
4552
4553
time_since_popup = OS::get_singleton()->get_ticks_msec();
4554
popup_list.push_back(p_window);
4555
}
4556
}
4557
4558
void DisplayServerWindows::popup_close(WindowID p_window) {
4559
_THREAD_SAFE_METHOD_
4560
4561
List<WindowID>::Element *E = popup_list.find(p_window);
4562
while (E) {
4563
List<WindowID>::Element *F = E->next();
4564
WindowID win_id = E->get();
4565
popup_list.erase(E);
4566
4567
if (win_id != p_window) {
4568
// Only request close on related windows, not this window. We are already processing it.
4569
_send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
4570
}
4571
E = F;
4572
}
4573
}
4574
4575
BitField<DisplayServerWindows::WinKeyModifierMask> DisplayServerWindows::_get_mods() const {
4576
BitField<WinKeyModifierMask> mask = {};
4577
static unsigned char keyboard_state[256];
4578
if (GetKeyboardState((PBYTE)&keyboard_state)) {
4579
if ((keyboard_state[VK_LSHIFT] & 0x80) || (keyboard_state[VK_RSHIFT] & 0x80)) {
4580
mask.set_flag(WinKeyModifierMask::SHIFT);
4581
}
4582
if ((keyboard_state[VK_LCONTROL] & 0x80) || (keyboard_state[VK_RCONTROL] & 0x80)) {
4583
mask.set_flag(WinKeyModifierMask::CTRL);
4584
}
4585
if ((keyboard_state[VK_LMENU] & 0x80) || (keyboard_state[VK_RMENU] & 0x80)) {
4586
mask.set_flag(WinKeyModifierMask::ALT);
4587
}
4588
if ((keyboard_state[VK_RMENU] & 0x80)) {
4589
mask.set_flag(WinKeyModifierMask::ALT_GR);
4590
}
4591
if ((keyboard_state[VK_LWIN] & 0x80) || (keyboard_state[VK_RWIN] & 0x80)) {
4592
mask.set_flag(WinKeyModifierMask::META);
4593
}
4594
}
4595
4596
return mask;
4597
}
4598
4599
LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) {
4600
_THREAD_SAFE_METHOD_
4601
4602
uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;
4603
if (delta > 250) {
4604
switch (wParam) {
4605
case WM_NCLBUTTONDOWN:
4606
case WM_NCRBUTTONDOWN:
4607
case WM_NCMBUTTONDOWN:
4608
case WM_LBUTTONDOWN:
4609
case WM_RBUTTONDOWN:
4610
case WM_MBUTTONDOWN: {
4611
MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam;
4612
Point2i pos = Point2i(ms->pt.x, ms->pt.y) - _get_screens_origin();
4613
List<WindowID>::Element *C = nullptr;
4614
List<WindowID>::Element *E = popup_list.back();
4615
// Find top popup to close.
4616
while (E) {
4617
// Popup window area.
4618
Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
4619
// Area of the parent window, which responsible for opening sub-menu.
4620
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
4621
if (win_rect.has_point(pos)) {
4622
break;
4623
} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {
4624
break;
4625
} else {
4626
C = E;
4627
E = E->prev();
4628
}
4629
}
4630
if (C) {
4631
_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
4632
return 1;
4633
}
4634
} break;
4635
}
4636
}
4637
return ::CallNextHookEx(mouse_monitor, code, wParam, lParam);
4638
}
4639
4640
// Handle a single window message received while CreateWindowEx is still on the stack and our data
4641
// structures are not fully initialized.
4642
LRESULT DisplayServerWindows::_handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
4643
switch (uMsg) {
4644
case WM_GETMINMAXINFO: {
4645
// We receive this during CreateWindowEx and we haven't initialized the window
4646
// struct, so let Windows figure out the maximized size.
4647
// Silently forward to user/default.
4648
} break;
4649
case WM_NCCREATE: {
4650
// We tunnel an unowned pointer to our window context (WindowData) through the
4651
// first possible message (WM_NCCREATE) to fix up our window context collection.
4652
CREATESTRUCTW *pCreate = (CREATESTRUCTW *)lParam;
4653
WindowData *pWindowData = reinterpret_cast<WindowData *>(pCreate->lpCreateParams);
4654
4655
// Fix this up so we can recognize the remaining messages.
4656
pWindowData->hWnd = hWnd;
4657
4658
#ifdef ACCESSKIT_ENABLED
4659
if (accessibility_driver && !accessibility_driver->window_create(pWindowData->id, (void *)hWnd)) {
4660
if (OS::get_singleton()->is_stdout_verbose()) {
4661
ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");
4662
}
4663
memdelete(accessibility_driver);
4664
accessibility_driver = nullptr;
4665
}
4666
#endif
4667
} break;
4668
default: {
4669
// Additional messages during window creation should happen after we fixed
4670
// up the data structures on WM_NCCREATE, but this might change in the future,
4671
// so report an error here and then we can implement them.
4672
ERR_PRINT_ONCE(vformat("Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error.", uMsg));
4673
} break;
4674
}
4675
4676
if (user_proc) {
4677
return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
4678
}
4679
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
4680
}
4681
4682
// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events.
4683
// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures
4684
LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
4685
if (drop_events) {
4686
if (user_proc) {
4687
return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
4688
} else {
4689
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
4690
}
4691
}
4692
4693
WindowID window_id = INVALID_WINDOW_ID;
4694
bool window_created = false;
4695
4696
// Check whether window exists
4697
// FIXME this is O(n), where n is the set of currently open windows and subwindows
4698
// we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below
4699
for (const KeyValue<WindowID, WindowData> &E : windows) {
4700
if (E.value.hWnd == hWnd) {
4701
window_id = E.key;
4702
window_created = true;
4703
break;
4704
}
4705
}
4706
4707
// WARNING: We get called with events before the window is registered in our collection
4708
// specifically, even the call to CreateWindowEx already calls here while still on the stack,
4709
// so there is no way to store the window handle in our collection before we get here.
4710
if (!window_created) {
4711
// don't let code below operate on incompletely initialized window objects or missing window_id
4712
return _handle_early_window_message(hWnd, uMsg, wParam, lParam);
4713
}
4714
4715
// Process window messages.
4716
switch (uMsg) {
4717
case WM_GETOBJECT: {
4718
get_object_received = true;
4719
} break;
4720
case WM_MENUCOMMAND: {
4721
native_menu->_menu_activate(HMENU(lParam), (int)wParam);
4722
} break;
4723
case WM_CREATE: {
4724
{
4725
DWORD value = windows[window_id].sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;
4726
::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));
4727
}
4728
if (is_dark_mode_supported() && dark_title_available) {
4729
BOOL value = is_dark_mode();
4730
4731
::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));
4732
SendMessageW(windows[window_id].hWnd, WM_PAINT, 0, 0);
4733
}
4734
} break;
4735
case WM_NCHITTEST: {
4736
if (windows[window_id].mpass) {
4737
return HTTRANSPARENT;
4738
}
4739
} break;
4740
case WM_MOUSEACTIVATE: {
4741
if (windows[window_id].no_focus || windows[window_id].is_popup) {
4742
return MA_NOACTIVATE; // Do not activate, but process mouse messages.
4743
}
4744
// When embedded, the window is a child of the parent and is not activated
4745
// by default because it lacks native controls.
4746
if (windows[window_id].parent_hwnd) {
4747
SetFocus(windows[window_id].hWnd);
4748
return MA_ACTIVATE;
4749
}
4750
} break;
4751
case WM_ACTIVATEAPP: {
4752
bool new_app_focused = (bool)wParam;
4753
if (new_app_focused == app_focused) {
4754
break;
4755
}
4756
app_focused = new_app_focused;
4757
if (OS::get_singleton()->get_main_loop()) {
4758
OS::get_singleton()->get_main_loop()->notification(app_focused ? MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN : MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
4759
}
4760
} break;
4761
case WM_ACTIVATE: {
4762
// Activation can happen just after the window has been created, even before the callbacks are set.
4763
// Therefore, it's safer to defer the delivery of the event.
4764
// It's important to set an nIDEvent different from the SetTimer for move_timer_id because
4765
// if the same nIDEvent is passed, the timer is replaced and the same timer_id is returned.
4766
// The problem with the timer is that the window cannot be resized or the buttons cannot be used correctly
4767
// if the window is not activated first. This happens because the code in the activation process runs
4768
// after the mouse click is handled. To address this, the timer is now used only when the window is created.
4769
windows[window_id].activate_state = GET_WM_ACTIVATE_STATE(wParam, lParam);
4770
if (windows[window_id].first_activation_done) {
4771
_process_activate_event(window_id);
4772
} else {
4773
windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_WINDOW_ACTIVATION, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
4774
}
4775
return 0;
4776
} break;
4777
case WM_GETMINMAXINFO: {
4778
if (windows[window_id].resizable && !windows[window_id].fullscreen) {
4779
// Size of window decorations.
4780
Size2 decor = window_get_size_with_decorations(window_id) - window_get_size(window_id);
4781
4782
MINMAXINFO *min_max_info = (MINMAXINFO *)lParam;
4783
if (windows[window_id].min_size != Size2()) {
4784
min_max_info->ptMinTrackSize.x = windows[window_id].min_size.x + decor.x;
4785
min_max_info->ptMinTrackSize.y = windows[window_id].min_size.y + decor.y;
4786
}
4787
if (windows[window_id].max_size != Size2()) {
4788
min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x;
4789
min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y;
4790
}
4791
if (windows[window_id].borderless) {
4792
Rect2i screen_rect = screen_get_usable_rect(window_get_current_screen(window_id));
4793
4794
// Set the size of (borderless) maximized mode to exclude taskbar (or any other panel) if present.
4795
min_max_info->ptMaxPosition.x = screen_rect.position.x;
4796
min_max_info->ptMaxPosition.y = screen_rect.position.y;
4797
min_max_info->ptMaxSize.x = screen_rect.size.x;
4798
min_max_info->ptMaxSize.y = screen_rect.size.y;
4799
}
4800
return 0;
4801
}
4802
} break;
4803
case WM_ERASEBKGND: {
4804
Color early_color;
4805
if (!_get_window_early_clear_override(early_color)) {
4806
break;
4807
}
4808
bool must_recreate_brush = !window_bkg_brush || window_bkg_brush_color != early_color.to_argb32();
4809
if (must_recreate_brush) {
4810
if (window_bkg_brush) {
4811
DeleteObject(window_bkg_brush);
4812
}
4813
window_bkg_brush = CreateSolidBrush(RGB(early_color.get_r8(), early_color.get_g8(), early_color.get_b8()));
4814
}
4815
HDC hdc = (HDC)wParam;
4816
RECT rect = {};
4817
if (GetUpdateRect(hWnd, &rect, true)) {
4818
FillRect(hdc, &rect, window_bkg_brush);
4819
}
4820
return 1;
4821
} break;
4822
case WM_PAINT: {
4823
Main::force_redraw();
4824
} break;
4825
case WM_SETTINGCHANGE:
4826
case WM_SYSCOLORCHANGE: {
4827
if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {
4828
if (is_dark_mode_supported() && dark_title_available) {
4829
BOOL value = is_dark_mode();
4830
::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));
4831
}
4832
}
4833
if (system_theme_changed.is_valid()) {
4834
Variant ret;
4835
Callable::CallError ce;
4836
system_theme_changed.callp(nullptr, 0, ret, ce);
4837
if (ce.error != Callable::CallError::CALL_OK) {
4838
ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
4839
}
4840
}
4841
} break;
4842
case WM_THEMECHANGED: {
4843
if (is_dark_mode_supported() && dark_title_available) {
4844
BOOL value = is_dark_mode();
4845
::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));
4846
}
4847
} break;
4848
case WM_SYSCOMMAND: // Intercept system commands.
4849
{
4850
switch (wParam) // Check system calls.
4851
{
4852
case SC_SCREENSAVE: // Screensaver trying to start?
4853
case SC_MONITORPOWER: // Monitor trying to enter powersave?
4854
return 0; // Prevent from happening.
4855
case SC_KEYMENU:
4856
Engine *engine = Engine::get_singleton();
4857
if (((lParam >> 16) <= 0) && !engine->is_project_manager_hint() && !engine->is_editor_hint() && !GLOBAL_GET_CACHED(bool, "application/run/enable_alt_space_menu")) {
4858
return 0;
4859
}
4860
if (!_get_mods().has_flag(WinKeyModifierMask::ALT) || !(GetAsyncKeyState(VK_SPACE) & (1 << 15))) {
4861
return 0;
4862
}
4863
SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_SPACE, 0);
4864
SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_MENU, 0);
4865
}
4866
} break;
4867
case WM_INDICATOR_CALLBACK_MESSAGE: {
4868
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN || lParam == WM_MBUTTONDOWN || lParam == WM_XBUTTONDOWN) {
4869
IndicatorID iid = (IndicatorID)wParam;
4870
MouseButton mb = MouseButton::LEFT;
4871
if (lParam == WM_RBUTTONDOWN) {
4872
mb = MouseButton::RIGHT;
4873
} else if (lParam == WM_MBUTTONDOWN) {
4874
mb = MouseButton::MIDDLE;
4875
} else if (lParam == WM_XBUTTONDOWN) {
4876
mb = MouseButton::MB_XBUTTON1;
4877
}
4878
if (indicators.has(iid)) {
4879
if (lParam == WM_RBUTTONDOWN && indicators[iid].menu_rid.is_valid() && native_menu->has_menu(indicators[iid].menu_rid)) {
4880
NOTIFYICONIDENTIFIER nid;
4881
ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));
4882
nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);
4883
nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;
4884
nid.uID = iid;
4885
nid.guidItem = GUID_NULL;
4886
4887
RECT rect;
4888
if (Shell_NotifyIconGetRect(&nid, &rect) == S_OK) {
4889
native_menu->popup(indicators[iid].menu_rid, Vector2i((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2));
4890
}
4891
} else if (indicators[iid].callback.is_valid()) {
4892
Variant v_button = mb;
4893
Variant v_pos = mouse_get_position();
4894
const Variant *v_args[2] = { &v_button, &v_pos };
4895
Variant ret;
4896
Callable::CallError ce;
4897
indicators[iid].callback.callp((const Variant **)&v_args, 2, ret, ce);
4898
if (ce.error != Callable::CallError::CALL_OK) {
4899
ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(indicators[iid].callback, v_args, 2, ce)));
4900
}
4901
}
4902
}
4903
return 0;
4904
}
4905
} break;
4906
case WM_CLOSE: {
4907
if (windows[window_id].activate_timer_id) {
4908
KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);
4909
windows[window_id].activate_timer_id = 0;
4910
}
4911
_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
4912
return 0;
4913
}
4914
case WM_MOUSELEAVE: {
4915
if (window_mouseover_id == window_id) {
4916
old_invalid = true;
4917
window_mouseover_id = INVALID_WINDOW_ID;
4918
4919
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
4920
} else if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {
4921
// This is reached during drag and drop, after dropping in a different window.
4922
// Once-off notification, must call again.
4923
track_mouse_leave_event(windows[window_mouseover_id].hWnd);
4924
}
4925
4926
} break;
4927
case WM_INPUT: {
4928
if (!use_raw_input) {
4929
break;
4930
}
4931
4932
UINT dwSize;
4933
4934
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER));
4935
LPBYTE lpb = new BYTE[dwSize];
4936
if (lpb == nullptr) {
4937
return 0;
4938
}
4939
4940
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
4941
OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
4942
}
4943
4944
RAWINPUT *raw = (RAWINPUT *)lpb;
4945
4946
const BitField<WinKeyModifierMask> &mods = _get_mods();
4947
if (raw->header.dwType == RIM_TYPEKEYBOARD) {
4948
if (raw->data.keyboard.VKey == VK_SHIFT) {
4949
// If multiple Shifts are held down at the same time,
4950
// Windows natively only sends a KEYUP for the last one to be released.
4951
if (raw->data.keyboard.Flags & RI_KEY_BREAK) {
4952
// Make sure to check the latest key state since
4953
// we're in the middle of the message queue.
4954
if (GetAsyncKeyState(VK_SHIFT) < 0) {
4955
// A Shift is released, but another Shift is still held
4956
ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
4957
4958
KeyEvent ke;
4959
ke.shift = false;
4960
ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);
4961
ke.alt = mods.has_flag(WinKeyModifierMask::ALT);
4962
ke.control = mods.has_flag(WinKeyModifierMask::CTRL);
4963
ke.meta = mods.has_flag(WinKeyModifierMask::META);
4964
ke.uMsg = WM_KEYUP;
4965
ke.window_id = window_id;
4966
4967
ke.wParam = VK_SHIFT;
4968
// data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.
4969
// Bit 30 -> key was previously down, bit 31 -> key is being released.
4970
ke.lParam = raw->data.keyboard.MakeCode << 16 | 1 << 30 | 1 << 31;
4971
key_event_buffer[key_event_pos++] = ke;
4972
}
4973
}
4974
}
4975
} else if (mouse_mode == MOUSE_MODE_CAPTURED && raw->header.dwType == RIM_TYPEMOUSE) {
4976
Ref<InputEventMouseMotion> mm;
4977
mm.instantiate();
4978
4979
mm->set_window_id(window_id);
4980
mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
4981
mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
4982
mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
4983
mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
4984
4985
mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f);
4986
4987
mm->set_button_mask(mouse_get_button_state());
4988
4989
Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
4990
4991
// Centering just so it works as before.
4992
POINT pos = { (int)c.x, (int)c.y };
4993
ClientToScreen(windows[window_id].hWnd, &pos);
4994
SetCursorPos(pos.x, pos.y);
4995
4996
mm->set_position(c);
4997
mm->set_global_position(c);
4998
mm->set_velocity(Vector2(0, 0));
4999
mm->set_screen_velocity(Vector2(0, 0));
5000
5001
if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
5002
mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));
5003
5004
} else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {
5005
int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
5006
int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
5007
int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
5008
int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
5009
5010
Vector2 abs_pos(
5011
(double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,
5012
(double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);
5013
5014
POINT coords; // Client coords.
5015
coords.x = abs_pos.x;
5016
coords.y = abs_pos.y;
5017
5018
ScreenToClient(hWnd, &coords);
5019
5020
mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));
5021
old_x = coords.x;
5022
old_y = coords.y;
5023
}
5024
mm->set_relative_screen_position(mm->get_relative());
5025
5026
if ((windows[window_id].window_focused || windows[window_id].is_popup) && mm->get_relative() != Vector2()) {
5027
Input::get_singleton()->parse_input_event(mm);
5028
}
5029
}
5030
delete[] lpb;
5031
} break;
5032
case WT_CSRCHANGE:
5033
case WT_PROXIMITY: {
5034
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
5035
AXIS pressure;
5036
if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {
5037
windows[window_id].min_pressure = int(pressure.axMin);
5038
windows[window_id].max_pressure = int(pressure.axMax);
5039
}
5040
AXIS orientation[3];
5041
if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {
5042
windows[window_id].tilt_supported = orientation[0].axResolution && orientation[1].axResolution;
5043
}
5044
return 0;
5045
}
5046
} break;
5047
case WT_PACKET: {
5048
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
5049
PACKET packet;
5050
if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) {
5051
POINT coords;
5052
GetCursorPos(&coords);
5053
ScreenToClient(windows[window_id].hWnd, &coords);
5054
5055
windows[window_id].last_pressure_update = 0;
5056
5057
float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);
5058
double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math::PI / 180);
5059
double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math::PI / 180));
5060
bool inverted = packet.pkStatus & TPS_INVERT;
5061
5062
Vector2 tilt = (windows[window_id].tilt_supported) ? Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)) : Vector2();
5063
5064
// Nothing changed, ignore event.
5065
if (!old_invalid && coords.x == old_x && coords.y == old_y && windows[window_id].last_pressure == pressure && windows[window_id].last_tilt == tilt && windows[window_id].last_pen_inverted == inverted) {
5066
break;
5067
}
5068
5069
windows[window_id].last_pressure = pressure;
5070
windows[window_id].last_tilt = tilt;
5071
windows[window_id].last_pen_inverted = inverted;
5072
5073
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
5074
if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {
5075
break;
5076
}
5077
5078
const BitField<WinKeyModifierMask> &mods = _get_mods();
5079
Ref<InputEventMouseMotion> mm;
5080
mm.instantiate();
5081
mm->set_window_id(window_id);
5082
mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
5083
mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
5084
mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
5085
mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
5086
5087
mm->set_pressure(windows[window_id].last_pressure);
5088
mm->set_tilt(windows[window_id].last_tilt);
5089
mm->set_pen_inverted(windows[window_id].last_pen_inverted);
5090
5091
mm->set_button_mask(mouse_get_button_state());
5092
5093
mm->set_position(Vector2(coords.x, coords.y));
5094
mm->set_global_position(Vector2(coords.x, coords.y));
5095
5096
if (mouse_mode == MOUSE_MODE_CAPTURED) {
5097
Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
5098
old_x = c.x;
5099
old_y = c.y;
5100
5101
if (mm->get_position() == c) {
5102
center = c;
5103
return 0;
5104
}
5105
5106
Point2i ncenter = mm->get_position();
5107
center = ncenter;
5108
POINT pos = { (int)c.x, (int)c.y };
5109
ClientToScreen(windows[window_id].hWnd, &pos);
5110
SetCursorPos(pos.x, pos.y);
5111
}
5112
5113
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
5114
mm->set_screen_velocity(mm->get_velocity());
5115
5116
if (old_invalid) {
5117
old_x = mm->get_position().x;
5118
old_y = mm->get_position().y;
5119
old_invalid = false;
5120
}
5121
5122
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
5123
mm->set_relative_screen_position(mm->get_relative());
5124
old_x = mm->get_position().x;
5125
old_y = mm->get_position().y;
5126
5127
if (windows[window_id].window_focused || window_get_active_popup() == window_id) {
5128
Input::get_singleton()->parse_input_event(mm);
5129
}
5130
}
5131
return 0;
5132
}
5133
} break;
5134
case WM_POINTERENTER: {
5135
if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
5136
break;
5137
}
5138
5139
if (tablet_get_current_driver() != "winink") {
5140
break;
5141
}
5142
5143
uint32_t pointer_id = LOWORD(wParam);
5144
POINTER_INPUT_TYPE pointer_type = PT_POINTER;
5145
if (!GetPointerType(pointer_id, &pointer_type)) {
5146
break;
5147
}
5148
5149
if (pointer_type != PT_PEN) {
5150
break;
5151
}
5152
5153
pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;
5154
windows[window_id].block_mm = true;
5155
return 0;
5156
} break;
5157
case WM_POINTERLEAVE: {
5158
pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;
5159
windows[window_id].block_mm = false;
5160
return 0;
5161
} break;
5162
case WM_POINTERDOWN:
5163
case WM_POINTERUP: {
5164
if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
5165
break;
5166
}
5167
5168
if (tablet_get_current_driver() != "winink") {
5169
break;
5170
}
5171
5172
uint32_t pointer_id = LOWORD(wParam);
5173
POINTER_INPUT_TYPE pointer_type = PT_POINTER;
5174
if (!GetPointerType(pointer_id, &pointer_type)) {
5175
break;
5176
}
5177
5178
if (pointer_type != PT_PEN) {
5179
break;
5180
}
5181
5182
Ref<InputEventMouseButton> mb;
5183
mb.instantiate();
5184
mb->set_window_id(window_id);
5185
5186
BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;
5187
if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {
5188
last_button_state.set_flag(MouseButtonMask::LEFT);
5189
mb->set_button_index(MouseButton::LEFT);
5190
}
5191
if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {
5192
last_button_state.set_flag(MouseButtonMask::RIGHT);
5193
mb->set_button_index(MouseButton::RIGHT);
5194
}
5195
if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {
5196
last_button_state.set_flag(MouseButtonMask::MIDDLE);
5197
mb->set_button_index(MouseButton::MIDDLE);
5198
}
5199
if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {
5200
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);
5201
mb->set_button_index(MouseButton::MB_XBUTTON1);
5202
}
5203
if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {
5204
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);
5205
mb->set_button_index(MouseButton::MB_XBUTTON2);
5206
}
5207
mb->set_button_mask(last_button_state);
5208
5209
const BitField<WinKeyModifierMask> &mods = _get_mods();
5210
mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
5211
mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
5212
mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
5213
mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
5214
5215
POINT coords; // Client coords.
5216
coords.x = GET_X_LPARAM(lParam);
5217
coords.y = GET_Y_LPARAM(lParam);
5218
5219
// Note: Handle popup closing here, since mouse event is not emulated and hook will not be called.
5220
uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;
5221
if (delta > 250) {
5222
Point2i pos = Point2i(coords.x, coords.y) - _get_screens_origin();
5223
List<WindowID>::Element *C = nullptr;
5224
List<WindowID>::Element *E = popup_list.back();
5225
// Find top popup to close.
5226
while (E) {
5227
// Popup window area.
5228
Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
5229
// Area of the parent window, which responsible for opening sub-menu.
5230
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
5231
if (win_rect.has_point(pos)) {
5232
break;
5233
} else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {
5234
break;
5235
} else {
5236
C = E;
5237
E = E->prev();
5238
}
5239
}
5240
if (C) {
5241
_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
5242
}
5243
}
5244
5245
int64_t pen_id = GET_POINTERID_WPARAM(wParam);
5246
if (uMsg == WM_POINTERDOWN) {
5247
mb->set_pressed(true);
5248
if (pointer_down_time.has(pen_id) && (pointer_prev_button[pen_id] == mb->get_button_index()) && (Math::abs(coords.y - pointer_last_pos[pen_id].y) < GetSystemMetrics(SM_CYDOUBLECLK)) && GetMessageTime() - pointer_down_time[pen_id] < (LONG)GetDoubleClickTime()) {
5249
mb->set_double_click(true);
5250
pointer_down_time[pen_id] = 0;
5251
} else {
5252
pointer_down_time[pen_id] = GetMessageTime();
5253
pointer_prev_button[pen_id] = mb->get_button_index();
5254
pointer_last_pos[pen_id] = Vector2(coords.x, coords.y);
5255
}
5256
pointer_button[pen_id] = mb->get_button_index();
5257
} else {
5258
if (!pointer_button.has(pen_id)) {
5259
return 0;
5260
}
5261
mb->set_pressed(false);
5262
mb->set_button_index(pointer_button[pen_id]);
5263
pointer_button[pen_id] = MouseButton::NONE;
5264
}
5265
5266
ScreenToClient(windows[window_id].hWnd, &coords);
5267
5268
mb->set_position(Vector2(coords.x, coords.y));
5269
mb->set_global_position(Vector2(coords.x, coords.y));
5270
5271
Input::get_singleton()->parse_input_event(mb);
5272
5273
return 0;
5274
} break;
5275
case WM_POINTERUPDATE: {
5276
if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
5277
break;
5278
}
5279
5280
if (tablet_get_current_driver() != "winink") {
5281
break;
5282
}
5283
5284
uint32_t pointer_id = LOWORD(wParam);
5285
POINTER_INPUT_TYPE pointer_type = PT_POINTER;
5286
if (!GetPointerType(pointer_id, &pointer_type)) {
5287
break;
5288
}
5289
5290
if (pointer_type != PT_PEN) {
5291
break;
5292
}
5293
5294
POINTER_PEN_INFO pen_info;
5295
if (!GetPointerPenInfo(pointer_id, &pen_info)) {
5296
break;
5297
}
5298
5299
if (Input::get_singleton()->is_emulating_mouse_from_touch()) {
5300
// Universal translation enabled; ignore OS translation.
5301
LPARAM extra = GetMessageExtraInfo();
5302
if (IsTouchEvent(extra)) {
5303
break;
5304
}
5305
}
5306
5307
if (window_mouseover_id != window_id) {
5308
// Mouse enter.
5309
5310
if (mouse_mode != MOUSE_MODE_CAPTURED) {
5311
if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {
5312
// Leave previous window.
5313
_send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
5314
}
5315
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
5316
}
5317
5318
CursorShape c = cursor_shape;
5319
cursor_shape = CURSOR_MAX;
5320
cursor_set_shape(c);
5321
window_mouseover_id = window_id;
5322
5323
// Once-off notification, must call again.
5324
track_mouse_leave_event(hWnd);
5325
}
5326
5327
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
5328
if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {
5329
break;
5330
}
5331
5332
Ref<InputEventMouseMotion> mm;
5333
mm.instantiate();
5334
5335
mm->set_window_id(window_id);
5336
if (pen_info.penMask & PEN_MASK_PRESSURE) {
5337
mm->set_pressure((float)pen_info.pressure / 1024);
5338
} else {
5339
mm->set_pressure((HIWORD(wParam) & POINTER_MESSAGE_FLAG_FIRSTBUTTON) ? 1.0f : 0.0f);
5340
}
5341
if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) {
5342
mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90));
5343
}
5344
mm->set_pen_inverted(pen_info.penFlags & (PEN_FLAG_INVERTED | PEN_FLAG_ERASER));
5345
5346
const BitField<WinKeyModifierMask> &mods = _get_mods();
5347
mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
5348
mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
5349
mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
5350
mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
5351
5352
BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;
5353
if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {
5354
last_button_state.set_flag(MouseButtonMask::LEFT);
5355
}
5356
if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {
5357
last_button_state.set_flag(MouseButtonMask::RIGHT);
5358
}
5359
if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {
5360
last_button_state.set_flag(MouseButtonMask::MIDDLE);
5361
}
5362
if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {
5363
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);
5364
}
5365
if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {
5366
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);
5367
}
5368
mm->set_button_mask(last_button_state);
5369
5370
POINT coords; // Client coords.
5371
coords.x = GET_X_LPARAM(lParam);
5372
coords.y = GET_Y_LPARAM(lParam);
5373
5374
ScreenToClient(windows[window_id].hWnd, &coords);
5375
5376
mm->set_position(Vector2(coords.x, coords.y));
5377
mm->set_global_position(Vector2(coords.x, coords.y));
5378
5379
if (mouse_mode == MOUSE_MODE_CAPTURED) {
5380
Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
5381
old_x = c.x;
5382
old_y = c.y;
5383
5384
if (mm->get_position() == c) {
5385
center = c;
5386
return 0;
5387
}
5388
5389
Point2i ncenter = mm->get_position();
5390
center = ncenter;
5391
POINT pos = { (int)c.x, (int)c.y };
5392
ClientToScreen(hWnd, &pos);
5393
SetCursorPos(pos.x, pos.y);
5394
}
5395
5396
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
5397
mm->set_screen_velocity(mm->get_velocity());
5398
5399
if (old_invalid) {
5400
old_x = mm->get_position().x;
5401
old_y = mm->get_position().y;
5402
old_invalid = false;
5403
}
5404
5405
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
5406
mm->set_relative_screen_position(mm->get_relative());
5407
old_x = mm->get_position().x;
5408
old_y = mm->get_position().y;
5409
if (windows[window_id].window_focused || window_get_active_popup() == window_id) {
5410
Input::get_singleton()->parse_input_event(mm);
5411
}
5412
5413
return 0; // Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event.
5414
} break;
5415
case WM_MOUSEMOVE: {
5416
if (windows[window_id].block_mm) {
5417
break;
5418
}
5419
5420
if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
5421
break;
5422
}
5423
5424
if (Input::get_singleton()->is_emulating_mouse_from_touch()) {
5425
// Universal translation enabled; ignore OS translation.
5426
LPARAM extra = GetMessageExtraInfo();
5427
if (IsTouchEvent(extra)) {
5428
break;
5429
}
5430
}
5431
5432
DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position());
5433
if (windows.has(over_id) && !Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) {
5434
// Don't consider the windowborder as part of the window.
5435
over_id = INVALID_WINDOW_ID;
5436
}
5437
if (window_mouseover_id != over_id) {
5438
// Mouse enter.
5439
5440
if (mouse_mode != MOUSE_MODE_CAPTURED) {
5441
if (window_mouseover_id != INVALID_WINDOW_ID && windows.has(window_mouseover_id)) {
5442
// Leave previous window.
5443
_send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
5444
}
5445
5446
if (over_id != INVALID_WINDOW_ID && windows.has(over_id)) {
5447
_send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER);
5448
}
5449
}
5450
5451
CursorShape c = cursor_shape;
5452
cursor_shape = CURSOR_MAX;
5453
cursor_set_shape(c);
5454
window_mouseover_id = over_id;
5455
5456
// Once-off notification, must call again.
5457
track_mouse_leave_event(hWnd);
5458
}
5459
5460
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
5461
if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) {
5462
break;
5463
}
5464
5465
DisplayServer::WindowID receiving_window_id = window_id;
5466
if (!windows[window_id].no_focus) {
5467
receiving_window_id = _get_focused_window_or_popup();
5468
if (receiving_window_id == INVALID_WINDOW_ID) {
5469
receiving_window_id = window_id;
5470
}
5471
}
5472
5473
const BitField<WinKeyModifierMask> &mods = _get_mods();
5474
Ref<InputEventMouseMotion> mm;
5475
mm.instantiate();
5476
mm->set_window_id(receiving_window_id);
5477
mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
5478
mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
5479
mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
5480
mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
5481
5482
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
5483
// Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not updated recently.
5484
if (windows[window_id].last_pressure_update < 10) {
5485
windows[window_id].last_pressure_update++;
5486
} else {
5487
windows[window_id].last_tilt = Vector2();
5488
windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;
5489
windows[window_id].last_pen_inverted = false;
5490
}
5491
} else {
5492
windows[window_id].last_tilt = Vector2();
5493
windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;
5494
windows[window_id].last_pen_inverted = false;
5495
}
5496
5497
mm->set_pressure(windows[window_id].last_pressure);
5498
mm->set_tilt(windows[window_id].last_tilt);
5499
mm->set_pen_inverted(windows[window_id].last_pen_inverted);
5500
5501
mm->set_button_mask(mouse_get_button_state());
5502
5503
mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
5504
mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
5505
5506
if (mouse_mode == MOUSE_MODE_CAPTURED) {
5507
Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
5508
old_x = c.x;
5509
old_y = c.y;
5510
5511
if (mm->get_position() == c) {
5512
center = c;
5513
return 0;
5514
}
5515
5516
Point2i ncenter = mm->get_position();
5517
center = ncenter;
5518
POINT pos = { (int)c.x, (int)c.y };
5519
ClientToScreen(windows[window_id].hWnd, &pos);
5520
SetCursorPos(pos.x, pos.y);
5521
}
5522
5523
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
5524
mm->set_screen_velocity(mm->get_velocity());
5525
5526
if (old_invalid) {
5527
old_x = mm->get_position().x;
5528
old_y = mm->get_position().y;
5529
old_invalid = false;
5530
}
5531
5532
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
5533
mm->set_relative_screen_position(mm->get_relative());
5534
old_x = mm->get_position().x;
5535
old_y = mm->get_position().y;
5536
5537
if (receiving_window_id != window_id) {
5538
// Adjust event position relative to window distance when event is sent to a different window.
5539
mm->set_position(mm->get_position() - window_get_position(receiving_window_id) + window_get_position(window_id));
5540
mm->set_global_position(mm->get_position());
5541
}
5542
5543
Input::get_singleton()->parse_input_event(mm);
5544
5545
} break;
5546
case WM_LBUTTONDOWN:
5547
case WM_LBUTTONUP:
5548
if (Input::get_singleton()->is_emulating_mouse_from_touch()) {
5549
// Universal translation enabled; ignore OS translations for left button.
5550
LPARAM extra = GetMessageExtraInfo();
5551
if (IsTouchEvent(extra)) {
5552
break;
5553
}
5554
}
5555
[[fallthrough]];
5556
case WM_MBUTTONDOWN:
5557
case WM_MBUTTONUP:
5558
case WM_RBUTTONDOWN:
5559
case WM_RBUTTONUP:
5560
case WM_MOUSEWHEEL:
5561
case WM_MOUSEHWHEEL:
5562
case WM_LBUTTONDBLCLK:
5563
case WM_MBUTTONDBLCLK:
5564
case WM_RBUTTONDBLCLK:
5565
case WM_XBUTTONDBLCLK:
5566
case WM_XBUTTONDOWN:
5567
case WM_XBUTTONUP: {
5568
Ref<InputEventMouseButton> mb;
5569
mb.instantiate();
5570
mb->set_window_id(window_id);
5571
5572
switch (uMsg) {
5573
case WM_LBUTTONDOWN: {
5574
mb->set_pressed(true);
5575
mb->set_button_index(MouseButton::LEFT);
5576
} break;
5577
case WM_LBUTTONUP: {
5578
mb->set_pressed(false);
5579
mb->set_button_index(MouseButton::LEFT);
5580
} break;
5581
case WM_MBUTTONDOWN: {
5582
mb->set_pressed(true);
5583
mb->set_button_index(MouseButton::MIDDLE);
5584
} break;
5585
case WM_MBUTTONUP: {
5586
mb->set_pressed(false);
5587
mb->set_button_index(MouseButton::MIDDLE);
5588
} break;
5589
case WM_RBUTTONDOWN: {
5590
mb->set_pressed(true);
5591
mb->set_button_index(MouseButton::RIGHT);
5592
} break;
5593
case WM_RBUTTONUP: {
5594
mb->set_pressed(false);
5595
mb->set_button_index(MouseButton::RIGHT);
5596
} break;
5597
case WM_LBUTTONDBLCLK: {
5598
mb->set_pressed(true);
5599
mb->set_button_index(MouseButton::LEFT);
5600
mb->set_double_click(true);
5601
} break;
5602
case WM_RBUTTONDBLCLK: {
5603
mb->set_pressed(true);
5604
mb->set_button_index(MouseButton::RIGHT);
5605
mb->set_double_click(true);
5606
} break;
5607
case WM_MBUTTONDBLCLK: {
5608
mb->set_pressed(true);
5609
mb->set_button_index(MouseButton::MIDDLE);
5610
mb->set_double_click(true);
5611
} break;
5612
case WM_MOUSEWHEEL: {
5613
mb->set_pressed(true);
5614
int motion = (short)HIWORD(wParam);
5615
if (!motion) {
5616
return 0;
5617
}
5618
5619
if (motion > 0) {
5620
mb->set_button_index(MouseButton::WHEEL_UP);
5621
} else {
5622
mb->set_button_index(MouseButton::WHEEL_DOWN);
5623
}
5624
mb->set_factor(std::fabs((double)motion / (double)WHEEL_DELTA));
5625
} break;
5626
case WM_MOUSEHWHEEL: {
5627
mb->set_pressed(true);
5628
int motion = (short)HIWORD(wParam);
5629
if (!motion) {
5630
return 0;
5631
}
5632
5633
if (motion < 0) {
5634
mb->set_button_index(MouseButton::WHEEL_LEFT);
5635
} else {
5636
mb->set_button_index(MouseButton::WHEEL_RIGHT);
5637
}
5638
mb->set_factor(std::fabs((double)motion / (double)WHEEL_DELTA));
5639
} break;
5640
case WM_XBUTTONDOWN: {
5641
mb->set_pressed(true);
5642
if (HIWORD(wParam) == XBUTTON1) {
5643
mb->set_button_index(MouseButton::MB_XBUTTON1);
5644
} else {
5645
mb->set_button_index(MouseButton::MB_XBUTTON2);
5646
}
5647
} break;
5648
case WM_XBUTTONUP: {
5649
mb->set_pressed(false);
5650
if (HIWORD(wParam) == XBUTTON1) {
5651
mb->set_button_index(MouseButton::MB_XBUTTON1);
5652
} else {
5653
mb->set_button_index(MouseButton::MB_XBUTTON2);
5654
}
5655
} break;
5656
case WM_XBUTTONDBLCLK: {
5657
mb->set_pressed(true);
5658
if (HIWORD(wParam) == XBUTTON1) {
5659
mb->set_button_index(MouseButton::MB_XBUTTON1);
5660
} else {
5661
mb->set_button_index(MouseButton::MB_XBUTTON2);
5662
}
5663
mb->set_double_click(true);
5664
} break;
5665
default: {
5666
return 0;
5667
}
5668
}
5669
5670
const BitField<WinKeyModifierMask> &mods = _get_mods();
5671
mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
5672
mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
5673
mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
5674
mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
5675
5676
if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) {
5677
MouseButtonMask mask = mouse_button_to_mask(mb->get_button_index());
5678
BitField<MouseButtonMask> scroll_mask = mouse_get_button_state();
5679
scroll_mask.set_flag(mask);
5680
mb->set_button_mask(scroll_mask);
5681
} else {
5682
mb->set_button_mask(mouse_get_button_state());
5683
}
5684
5685
mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
5686
5687
if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {
5688
mb->set_position(Vector2(old_x, old_y));
5689
}
5690
5691
if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
5692
if (mb->is_pressed()) {
5693
if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED) {
5694
SetCapture(hWnd);
5695
}
5696
} else {
5697
if (--pressrc <= 0 || mouse_get_button_state().is_empty()) {
5698
if (mouse_mode != MOUSE_MODE_CAPTURED) {
5699
ReleaseCapture();
5700
}
5701
pressrc = 0;
5702
}
5703
}
5704
} else {
5705
// For reasons unknown to humanity, wheel comes in screen coordinates.
5706
POINT coords;
5707
coords.x = mb->get_position().x;
5708
coords.y = mb->get_position().y;
5709
5710
ScreenToClient(hWnd, &coords);
5711
5712
mb->set_position(Vector2(coords.x, coords.y));
5713
}
5714
5715
mb->set_global_position(mb->get_position());
5716
5717
Input::get_singleton()->parse_input_event(mb);
5718
if (mb->is_pressed() && mb->get_button_index() >= MouseButton::WHEEL_UP && mb->get_button_index() <= MouseButton::WHEEL_RIGHT) {
5719
// Send release for mouse wheel.
5720
Ref<InputEventMouseButton> mbd = mb->duplicate();
5721
mbd->set_window_id(window_id);
5722
mbd->set_button_mask(mouse_get_button_state());
5723
mbd->set_pressed(false);
5724
Input::get_singleton()->parse_input_event(mbd);
5725
}
5726
5727
// Propagate the button up event to the window on which the button down
5728
// event was triggered. This is needed for drag & drop to work between windows,
5729
// because the engine expects events to keep being processed
5730
// on the same window dragging started.
5731
if (mb->is_pressed()) {
5732
last_mouse_button_down_window = window_id;
5733
} else if (last_mouse_button_down_window != INVALID_WINDOW_ID) {
5734
mb->set_window_id(last_mouse_button_down_window);
5735
last_mouse_button_down_window = INVALID_WINDOW_ID;
5736
}
5737
} break;
5738
5739
case WM_WINDOWPOSCHANGED: {
5740
WindowData &window = windows[window_id];
5741
5742
int off_x = (window.multiwindow_fs || (!window.fullscreen && window.borderless && IsZoomed(hWnd))) ? FS_TRANSP_BORDER : 0;
5743
Rect2i window_client_rect;
5744
Rect2i window_rect;
5745
{
5746
RECT rect;
5747
GetClientRect(hWnd, &rect);
5748
ClientToScreen(hWnd, (POINT *)&rect.left);
5749
ClientToScreen(hWnd, (POINT *)&rect.right);
5750
window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left - off_x, rect.bottom - rect.top);
5751
window_client_rect.position -= _get_screens_origin();
5752
5753
RECT wrect;
5754
GetWindowRect(hWnd, &wrect);
5755
window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left - off_x, wrect.bottom - wrect.top);
5756
window_rect.position -= _get_screens_origin();
5757
}
5758
5759
WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam;
5760
5761
bool rect_changed = false;
5762
if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) {
5763
int screen_id = window_get_current_screen(window_id);
5764
Size2i screen_size = screen_get_size(screen_id);
5765
Point2i screen_position = screen_get_position(screen_id);
5766
Rect2i usable = screen_get_usable_rect(screen_id);
5767
5768
window.maximized = false;
5769
window.minimized = false;
5770
window.fullscreen = false;
5771
5772
if (IsIconic(hWnd)) {
5773
window.minimized = true;
5774
} else if (IsZoomed(hWnd)) {
5775
window.maximized = true;
5776
5777
// If maximized_window_size == screen_size add 1px border to prevent switching to exclusive_fs.
5778
if (!window.maximized_fs && window.borderless && window_rect.position == screen_position && window_rect.size == screen_size) {
5779
// Window (borderless) was just maximized and the covers the entire screen.
5780
window.maximized_fs = true;
5781
_update_window_style(window_id, false);
5782
}
5783
if (window.borderless && (screen_size != usable.size || screen_position != usable.position)) {
5784
Point2 pos = usable.position + _get_screens_origin();
5785
Size2 size = usable.size;
5786
MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
5787
}
5788
} else if (window_rect.position == screen_position && window_rect.size == screen_size) {
5789
window.fullscreen = true;
5790
} else if (window.borderless && usable.position == window_rect.position && usable.size == window_rect.size) {
5791
window.maximized = true;
5792
}
5793
5794
if (window.maximized_fs && !window.maximized) {
5795
// Window (maximized and covering fullscreen) was just non-maximized.
5796
window.maximized_fs = false;
5797
_update_window_style(window_id, false);
5798
}
5799
5800
if (!window.minimized) {
5801
window.width = window_client_rect.size.width;
5802
window.height = window_client_rect.size.height;
5803
5804
rect_changed = true;
5805
}
5806
#if defined(RD_ENABLED)
5807
if (window.create_completed && rendering_context && window.context_created) {
5808
// Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.
5809
rendering_context->window_set_size(window_id, window.width, window.height);
5810
}
5811
#endif
5812
#if defined(GLES3_ENABLED)
5813
if (window.create_completed && gl_manager_native) {
5814
gl_manager_native->window_resize(window_id, window.width, window.height);
5815
}
5816
if (window.create_completed && gl_manager_angle) {
5817
gl_manager_angle->window_resize(window_id, window.width, window.height);
5818
}
5819
#endif
5820
}
5821
5822
if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) {
5823
window.last_pos = window_client_rect.position;
5824
rect_changed = true;
5825
}
5826
5827
if (rect_changed) {
5828
if (window.rect_changed_callback.is_valid()) {
5829
window.rect_changed_callback.call(Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height));
5830
}
5831
5832
// Update cursor clip region after window rect has changed.
5833
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
5834
RECT crect;
5835
GetClientRect(window.hWnd, &crect);
5836
crect.right -= off_x;
5837
ClientToScreen(window.hWnd, (POINT *)&crect.left);
5838
ClientToScreen(window.hWnd, (POINT *)&crect.right);
5839
ClipCursor(&crect);
5840
}
5841
5842
if (!window.minimized && window.was_fullscreen_pre_min) {
5843
// Restore fullscreen mode if window was in fullscreen before it was minimized.
5844
int cs = window_get_current_screen(window_id);
5845
Point2 pos = screen_get_position(cs) + _get_screens_origin();
5846
Size2 size = screen_get_size(cs);
5847
5848
window.was_fullscreen_pre_min = false;
5849
window.fullscreen = true;
5850
window.maximized = false;
5851
window.minimized = false;
5852
5853
_update_window_style(window_id, false);
5854
5855
MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
5856
}
5857
} else {
5858
if (window.parent_hwnd) {
5859
// WM_WINDOWPOSCHANGED is sent when the parent changes.
5860
// If we are supposed to have a parent and now we don't, it's likely
5861
// because the parent was closed. We will close our window as well.
5862
// This prevents an embedded game from staying alive when the editor is closed or crashes.
5863
if (!GetParent(window.hWnd)) {
5864
SendMessage(window.hWnd, WM_CLOSE, 0, 0);
5865
}
5866
}
5867
}
5868
5869
// Return here to prevent WM_MOVE and WM_SIZE from being sent
5870
// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks
5871
return 0;
5872
} break;
5873
5874
case WM_ENTERSIZEMOVE: {
5875
Input::get_singleton()->release_pressed_events();
5876
windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_MOVE_REDRAW, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
5877
} break;
5878
case WM_EXITSIZEMOVE: {
5879
KillTimer(windows[window_id].hWnd, windows[window_id].move_timer_id);
5880
windows[window_id].move_timer_id = 0;
5881
// Reset the correct mouse mode because we couldn't call ReleaseCapture in
5882
// _set_mouse_mode_impl while in _process_activate_event (because the user was moving a window).
5883
_set_mouse_mode_impl(mouse_mode);
5884
} break;
5885
case WM_TIMER: {
5886
if (wParam == windows[window_id].move_timer_id) {
5887
_THREAD_SAFE_UNLOCK_
5888
_process_key_events();
5889
if (!Main::is_iterating()) {
5890
Main::iteration();
5891
}
5892
_THREAD_SAFE_LOCK_
5893
} else if (wParam == windows[window_id].activate_timer_id) {
5894
_process_activate_event(window_id);
5895
KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);
5896
windows[window_id].activate_timer_id = 0;
5897
windows[window_id].first_activation_done = true;
5898
}
5899
} break;
5900
case WM_SYSKEYUP:
5901
case WM_KEYUP:
5902
case WM_SYSKEYDOWN:
5903
case WM_KEYDOWN: {
5904
if (windows[window_id].ime_suppress_next_keyup && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)) {
5905
windows[window_id].ime_suppress_next_keyup = false;
5906
break;
5907
}
5908
if (windows[window_id].ime_in_progress) {
5909
break;
5910
}
5911
5912
if (mouse_mode == MOUSE_MODE_CAPTURED) {
5913
// When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves
5914
if (wParam == VK_F4 && _get_mods().has_flag(WinKeyModifierMask::ALT) && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) {
5915
_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
5916
}
5917
}
5918
[[fallthrough]];
5919
}
5920
case WM_CHAR: {
5921
ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
5922
const BitField<WinKeyModifierMask> &mods = _get_mods();
5923
5924
KeyEvent ke;
5925
ke.shift = mods.has_flag(WinKeyModifierMask::SHIFT);
5926
ke.alt = mods.has_flag(WinKeyModifierMask::ALT);
5927
ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);
5928
ke.control = mods.has_flag(WinKeyModifierMask::CTRL);
5929
ke.meta = mods.has_flag(WinKeyModifierMask::META);
5930
ke.uMsg = uMsg;
5931
ke.window_id = window_id;
5932
5933
if (ke.uMsg == WM_SYSKEYDOWN) {
5934
ke.uMsg = WM_KEYDOWN;
5935
}
5936
if (ke.uMsg == WM_SYSKEYUP) {
5937
ke.uMsg = WM_KEYUP;
5938
}
5939
5940
ke.wParam = wParam;
5941
ke.lParam = lParam;
5942
key_event_buffer[key_event_pos++] = ke;
5943
5944
} break;
5945
case WM_IME_COMPOSITION: {
5946
CANDIDATEFORM cf;
5947
cf.dwIndex = 0;
5948
5949
cf.dwStyle = CFS_CANDIDATEPOS;
5950
cf.ptCurrentPos.x = windows[window_id].im_position.x;
5951
cf.ptCurrentPos.y = windows[window_id].im_position.y;
5952
ImmSetCandidateWindow(windows[window_id].im_himc, &cf);
5953
5954
cf.dwStyle = CFS_EXCLUDE;
5955
cf.rcArea.left = windows[window_id].im_position.x;
5956
cf.rcArea.right = windows[window_id].im_position.x;
5957
cf.rcArea.top = windows[window_id].im_position.y;
5958
cf.rcArea.bottom = windows[window_id].im_position.y;
5959
ImmSetCandidateWindow(windows[window_id].im_himc, &cf);
5960
5961
if (windows[window_id].ime_active) {
5962
SetCaretPos(windows[window_id].im_position.x, windows[window_id].im_position.y);
5963
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
5964
}
5965
} break;
5966
case WM_INPUTLANGCHANGEREQUEST: {
5967
// FIXME: Do something?
5968
} break;
5969
case WM_IME_STARTCOMPOSITION: {
5970
if (windows[window_id].ime_active) {
5971
windows[window_id].ime_in_progress = true;
5972
if (key_event_pos > 0) {
5973
key_event_pos--;
5974
}
5975
}
5976
return 0;
5977
} break;
5978
case WM_IME_ENDCOMPOSITION: {
5979
if (windows[window_id].ime_active) {
5980
windows[window_id].ime_in_progress = false;
5981
windows[window_id].ime_suppress_next_keyup = true;
5982
}
5983
return 0;
5984
} break;
5985
case WM_IME_NOTIFY: {
5986
return 0;
5987
} break;
5988
case WM_TOUCH: {
5989
BOOL bHandled = FALSE;
5990
UINT cInputs = LOWORD(wParam);
5991
PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);
5992
if (pInputs) {
5993
if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {
5994
for (UINT i = 0; i < cInputs; i++) {
5995
TOUCHINPUT ti = pInputs[i];
5996
POINT touch_pos = {
5997
TOUCH_COORD_TO_PIXEL(ti.x),
5998
TOUCH_COORD_TO_PIXEL(ti.y),
5999
};
6000
ScreenToClient(hWnd, &touch_pos);
6001
// Do something with each touch input entry.
6002
if (ti.dwFlags & TOUCHEVENTF_MOVE) {
6003
_drag_event(window_id, touch_pos.x, touch_pos.y, ti.dwID);
6004
} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
6005
_touch_event(window_id, ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);
6006
}
6007
}
6008
bHandled = TRUE;
6009
} else {
6010
// TODO: Handle the error here.
6011
}
6012
memdelete_arr(pInputs);
6013
} else {
6014
// TODO: Handle the error here, probably out of memory.
6015
}
6016
if (bHandled) {
6017
CloseTouchInputHandle((HTOUCHINPUT)lParam);
6018
return 0;
6019
}
6020
6021
} break;
6022
case WM_DESTROY: {
6023
#ifdef ACCESSKIT_ENABLED
6024
if (accessibility_driver) {
6025
accessibility_driver->window_destroy(window_id);
6026
}
6027
#endif
6028
Input::get_singleton()->flush_buffered_events();
6029
if (window_mouseover_id == window_id) {
6030
window_mouseover_id = INVALID_WINDOW_ID;
6031
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
6032
}
6033
} break;
6034
case WM_SETCURSOR: {
6035
if (LOWORD(lParam) == HTCLIENT) {
6036
if (windows[window_id].window_focused && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
6037
// Hide the cursor.
6038
if (hCursor == nullptr) {
6039
hCursor = SetCursor(nullptr);
6040
} else {
6041
SetCursor(nullptr);
6042
}
6043
} else {
6044
if (hCursor != nullptr) {
6045
CursorShape c = cursor_shape;
6046
cursor_shape = CURSOR_MAX;
6047
cursor_set_shape(c);
6048
hCursor = nullptr;
6049
}
6050
}
6051
}
6052
} break;
6053
default: {
6054
if (user_proc) {
6055
return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
6056
}
6057
}
6058
}
6059
6060
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
6061
}
6062
6063
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
6064
DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());
6065
if (ds_win) {
6066
return ds_win->WndProc(hWnd, uMsg, wParam, lParam);
6067
} else {
6068
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
6069
}
6070
}
6071
6072
void DisplayServerWindows::_process_activate_event(WindowID p_window_id) {
6073
WindowData &wd = windows[p_window_id];
6074
if (wd.activate_state == WA_ACTIVE || wd.activate_state == WA_CLICKACTIVE) {
6075
last_focused_window = p_window_id;
6076
_set_mouse_mode_impl(mouse_mode);
6077
if (!IsIconic(wd.hWnd)) {
6078
SetFocus(wd.hWnd);
6079
}
6080
wd.window_focused = true;
6081
#ifdef ACCESSKIT_ENABLED
6082
if (accessibility_driver) {
6083
accessibility_driver->accessibility_set_window_focused(p_window_id, true);
6084
}
6085
#endif
6086
_send_window_event(wd, WINDOW_EVENT_FOCUS_IN);
6087
} else { // WM_INACTIVE.
6088
Input::get_singleton()->release_pressed_events();
6089
track_mouse_leave_event(wd.hWnd);
6090
// Release capture unconditionally because it can be set due to dragging, in addition to captured mode.
6091
// When the user is moving a window, it's important to not ReleaseCapture because it will cause
6092
// the window movement to stop and if the user tries to move the Windows when it's not activated,
6093
// it will prevent the window movement. If we are here and a window is moving, it's because we had multiple
6094
// opened windows in the editor and we are definitively not in a middle of dragging.
6095
if (!_has_moving_window()) {
6096
ReleaseCapture();
6097
}
6098
wd.window_focused = false;
6099
#ifdef ACCESSKIT_ENABLED
6100
if (accessibility_driver) {
6101
accessibility_driver->accessibility_set_window_focused(p_window_id, false);
6102
}
6103
#endif
6104
_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);
6105
}
6106
6107
if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {
6108
wintab_WTEnable(wd.wtctx, wd.activate_state);
6109
}
6110
}
6111
6112
void DisplayServerWindows::_process_key_events() {
6113
for (int i = 0; i < key_event_pos; i++) {
6114
KeyEvent &ke = key_event_buffer[i];
6115
switch (ke.uMsg) {
6116
case WM_CHAR: {
6117
// Extended keys should only be processed as WM_KEYDOWN message.
6118
if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) {
6119
static char32_t prev_wc = 0;
6120
char32_t unicode = ke.wParam;
6121
if ((unicode & 0xfffffc00) == 0xd800) {
6122
if (prev_wc != 0) {
6123
ERR_PRINT("invalid utf16 surrogate input");
6124
}
6125
prev_wc = unicode;
6126
break; // Skip surrogate.
6127
} else if ((unicode & 0xfffffc00) == 0xdc00) {
6128
if (prev_wc == 0) {
6129
ERR_PRINT("invalid utf16 surrogate input");
6130
break; // Skip invalid surrogate.
6131
}
6132
unicode = (prev_wc << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
6133
prev_wc = 0;
6134
} else {
6135
prev_wc = 0;
6136
}
6137
Ref<InputEventKey> k;
6138
k.instantiate();
6139
6140
UINT vk = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK);
6141
bool is_oem = (vk >= 0xB8) && (vk <= 0xE6);
6142
Key keycode = KeyMappingWindows::get_keysym(vk);
6143
Key key_label = keycode;
6144
Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));
6145
6146
static BYTE keyboard_state[256];
6147
memset(keyboard_state, 0, 256);
6148
wchar_t chars[256] = {};
6149
UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX);
6150
if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) {
6151
String keysym = String::utf16((char16_t *)chars, 255);
6152
if (!keysym.is_empty()) {
6153
char32_t unicode_value = keysym[0];
6154
// For printable ASCII characters (0x20-0x7E), override the original keycode with the character value.
6155
if (is_oem && Key::SPACE <= (Key)unicode_value && (Key)unicode_value <= Key::ASCIITILDE) {
6156
keycode = fix_keycode(unicode_value, (Key)unicode_value);
6157
}
6158
key_label = fix_key_label(unicode_value, keycode);
6159
}
6160
}
6161
6162
k->set_window_id(ke.window_id);
6163
if (keycode != Key::SHIFT) {
6164
k->set_shift_pressed(ke.shift);
6165
}
6166
if (keycode != Key::ALT) {
6167
k->set_alt_pressed(ke.alt);
6168
}
6169
if (keycode != Key::CTRL) {
6170
k->set_ctrl_pressed(ke.control);
6171
}
6172
if (keycode != Key::META) {
6173
k->set_meta_pressed(ke.meta);
6174
}
6175
k->set_pressed(true);
6176
k->set_keycode(keycode);
6177
k->set_physical_keycode(physical_keycode);
6178
k->set_key_label(key_label);
6179
k->set_unicode(fix_unicode(unicode));
6180
if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {
6181
k->set_alt_pressed(false);
6182
k->set_ctrl_pressed(false);
6183
}
6184
6185
Input::get_singleton()->parse_input_event(k);
6186
} else {
6187
// Do nothing.
6188
}
6189
} break;
6190
case WM_KEYUP:
6191
case WM_KEYDOWN: {
6192
Ref<InputEventKey> k;
6193
k.instantiate();
6194
6195
k->set_window_id(ke.window_id);
6196
k->set_pressed(ke.uMsg == WM_KEYDOWN);
6197
6198
bool is_oem = (ke.wParam >= 0xB8) && (ke.wParam <= 0xE6);
6199
Key keycode = KeyMappingWindows::get_keysym(ke.wParam);
6200
if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) {
6201
// Special case for Numpad Enter key.
6202
keycode = Key::KP_ENTER;
6203
}
6204
Key key_label = keycode;
6205
Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));
6206
KeyLocation location = KeyMappingWindows::get_location((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));
6207
6208
static BYTE keyboard_state[256];
6209
memset(keyboard_state, 0, 256);
6210
wchar_t chars[256] = {};
6211
UINT extended_code = MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX);
6212
if (!(ke.lParam & (1 << 24)) && ToUnicodeEx(extended_code, (ke.lParam >> 16) & 0xFF, keyboard_state, chars, 255, 4, GetKeyboardLayout(0)) > 0) {
6213
String keysym = String::utf16((char16_t *)chars, 255);
6214
if (!keysym.is_empty()) {
6215
char32_t unicode_value = keysym[0];
6216
// For printable ASCII characters (0x20-0x7E), override the original keycode with the character value.
6217
if (is_oem && Key::SPACE <= (Key)unicode_value && (Key)unicode_value <= Key::ASCIITILDE) {
6218
keycode = fix_keycode(unicode_value, (Key)unicode_value);
6219
}
6220
key_label = fix_key_label(unicode_value, keycode);
6221
}
6222
}
6223
6224
if (keycode != Key::SHIFT) {
6225
k->set_shift_pressed(ke.shift);
6226
}
6227
if (keycode != Key::ALT) {
6228
k->set_alt_pressed(ke.alt);
6229
}
6230
if (keycode != Key::CTRL) {
6231
k->set_ctrl_pressed(ke.control);
6232
}
6233
if (keycode != Key::META) {
6234
k->set_meta_pressed(ke.meta);
6235
}
6236
k->set_keycode(keycode);
6237
k->set_physical_keycode(physical_keycode);
6238
k->set_location(location);
6239
k->set_key_label(key_label);
6240
6241
if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {
6242
char32_t unicode = key_event_buffer[i + 1].wParam;
6243
static char32_t prev_wck = 0;
6244
if ((unicode & 0xfffffc00) == 0xd800) {
6245
if (prev_wck != 0) {
6246
ERR_PRINT("invalid utf16 surrogate input");
6247
}
6248
prev_wck = unicode;
6249
break; // Skip surrogate.
6250
} else if ((unicode & 0xfffffc00) == 0xdc00) {
6251
if (prev_wck == 0) {
6252
ERR_PRINT("invalid utf16 surrogate input");
6253
break; // Skip invalid surrogate.
6254
}
6255
unicode = (prev_wck << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
6256
prev_wck = 0;
6257
} else {
6258
prev_wck = 0;
6259
}
6260
k->set_unicode(fix_unicode(unicode));
6261
}
6262
if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {
6263
k->set_alt_pressed(false);
6264
k->set_ctrl_pressed(false);
6265
}
6266
6267
k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));
6268
6269
Input::get_singleton()->parse_input_event(k);
6270
6271
} break;
6272
}
6273
}
6274
6275
key_event_pos = 0;
6276
}
6277
6278
void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const String &p_new_driver) {
6279
for (KeyValue<WindowID, WindowData> &E : windows) {
6280
WindowData &wd = E.value;
6281
wd.block_mm = false;
6282
if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) {
6283
wintab_WTEnable(wd.wtctx, false);
6284
wintab_WTClose(wd.wtctx);
6285
wd.wtctx = nullptr;
6286
}
6287
if ((p_new_driver == "wintab") && wintab_available) {
6288
wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);
6289
wd.wtlc.lcOptions |= CXO_MESSAGES;
6290
wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;
6291
wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
6292
wd.wtlc.lcPktMode = 0;
6293
wd.wtlc.lcOutOrgX = 0;
6294
wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;
6295
wd.wtlc.lcOutOrgY = 0;
6296
wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;
6297
wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);
6298
if (wd.wtctx) {
6299
wintab_WTEnable(wd.wtctx, true);
6300
AXIS pressure;
6301
if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {
6302
wd.min_pressure = int(pressure.axMin);
6303
wd.max_pressure = int(pressure.axMax);
6304
}
6305
AXIS orientation[3];
6306
if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {
6307
wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;
6308
}
6309
wintab_WTEnable(wd.wtctx, true);
6310
} else {
6311
print_verbose("WinTab context creation failed.");
6312
}
6313
}
6314
}
6315
}
6316
6317
DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent, HWND p_parent_hwnd) {
6318
DWORD dwExStyle;
6319
DWORD dwStyle;
6320
6321
_get_window_style(window_id_counter == MAIN_WINDOW_ID, false, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_flags & WINDOW_FLAG_MINIMIZE_DISABLED_BIT, p_flags & WINDOW_FLAG_MAXIMIZE_DISABLED_BIT, p_mode == WINDOW_MODE_MINIMIZED, p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP_BIT), p_parent_hwnd, dwStyle, dwExStyle);
6322
6323
int rq_screen = get_screen_from_rect(p_rect);
6324
if (rq_screen < 0) {
6325
rq_screen = get_primary_screen(); // Requested window rect is outside any screen bounds.
6326
}
6327
Rect2i usable_rect = screen_get_usable_rect(rq_screen);
6328
6329
Point2i offset = _get_screens_origin();
6330
6331
RECT WindowRect;
6332
6333
int off_x = (p_mode == WINDOW_MODE_FULLSCREEN || ((p_flags & WINDOW_FLAG_BORDERLESS_BIT) && p_mode == WINDOW_MODE_MAXIMIZED)) ? FS_TRANSP_BORDER : 0;
6334
6335
WindowRect.left = p_rect.position.x;
6336
WindowRect.right = p_rect.position.x + p_rect.size.x + off_x;
6337
WindowRect.top = p_rect.position.y;
6338
WindowRect.bottom = p_rect.position.y + p_rect.size.y;
6339
6340
if (!p_parent_hwnd) {
6341
if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
6342
Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));
6343
6344
WindowRect.left = screen_rect.position.x;
6345
WindowRect.right = screen_rect.position.x + screen_rect.size.x + off_x;
6346
WindowRect.top = screen_rect.position.y;
6347
WindowRect.bottom = screen_rect.position.y + screen_rect.size.y;
6348
} else {
6349
Rect2i srect = screen_get_usable_rect(rq_screen);
6350
Point2i wpos = p_rect.position;
6351
if (srect != Rect2i()) {
6352
wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);
6353
}
6354
6355
WindowRect.left = wpos.x;
6356
WindowRect.right = wpos.x + p_rect.size.x + off_x;
6357
WindowRect.top = wpos.y;
6358
WindowRect.bottom = wpos.y + p_rect.size.y;
6359
}
6360
}
6361
6362
WindowRect.left += offset.x;
6363
WindowRect.right += offset.x;
6364
WindowRect.top += offset.y;
6365
WindowRect.bottom += offset.y;
6366
6367
if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
6368
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
6369
}
6370
6371
WindowID id = window_id_counter;
6372
{
6373
WindowData *wd_transient_parent = nullptr;
6374
HWND owner_hwnd = nullptr;
6375
if (p_parent_hwnd) {
6376
owner_hwnd = p_parent_hwnd;
6377
} else if (p_transient_parent != INVALID_WINDOW_ID) {
6378
if (!windows.has(p_transient_parent)) {
6379
ERR_PRINT("Condition \"!windows.has(p_transient_parent)\" is true.");
6380
p_transient_parent = INVALID_WINDOW_ID;
6381
} else {
6382
wd_transient_parent = &windows[p_transient_parent];
6383
if (p_exclusive) {
6384
owner_hwnd = wd_transient_parent->hWnd;
6385
}
6386
}
6387
}
6388
6389
WindowData &wd = windows[id];
6390
6391
wd.id = id;
6392
wd.hWnd = CreateWindowExW(
6393
dwExStyle,
6394
L"Engine", L"",
6395
dwStyle,
6396
WindowRect.left,
6397
WindowRect.top,
6398
WindowRect.right - WindowRect.left,
6399
WindowRect.bottom - WindowRect.top,
6400
owner_hwnd,
6401
nullptr,
6402
hInstance,
6403
// tunnel the WindowData we need to handle creation message
6404
// lifetime is ensured because we are still on the stack when this is
6405
// processed in the window proc
6406
reinterpret_cast<void *>(&wd));
6407
if (!wd.hWnd) {
6408
MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
6409
windows.erase(id);
6410
ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window.");
6411
}
6412
6413
wd.parent_hwnd = p_parent_hwnd;
6414
6415
// Detach the input queue from the parent window.
6416
// This prevents the embedded window from waiting on the main window's input queue,
6417
// causing lags input lags when resizing or moving the main window.
6418
if (p_parent_hwnd) {
6419
DWORD mainThreadId = GetWindowThreadProcessId(owner_hwnd, nullptr);
6420
DWORD embeddedThreadId = GetCurrentThreadId();
6421
AttachThreadInput(embeddedThreadId, mainThreadId, FALSE);
6422
}
6423
6424
if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
6425
wd.fullscreen = true;
6426
if (p_mode == WINDOW_MODE_FULLSCREEN) {
6427
wd.multiwindow_fs = true;
6428
}
6429
}
6430
6431
if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
6432
// Save initial non-fullscreen rect.
6433
Rect2i srect = screen_get_usable_rect(rq_screen);
6434
Point2i wpos = p_rect.position;
6435
if (srect != Rect2i()) {
6436
wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);
6437
}
6438
6439
wd.pre_fs_rect.left = wpos.x + offset.x;
6440
wd.pre_fs_rect.right = wpos.x + p_rect.size.x + offset.x;
6441
wd.pre_fs_rect.top = wpos.y + offset.y;
6442
wd.pre_fs_rect.bottom = wpos.y + p_rect.size.y + offset.y;
6443
wd.pre_fs_valid = true;
6444
}
6445
6446
wd.exclusive = p_exclusive;
6447
if (wd_transient_parent) {
6448
wd.transient_parent = p_transient_parent;
6449
wd_transient_parent->transient_children.insert(id);
6450
}
6451
6452
wd.sharp_corners = p_flags & WINDOW_FLAG_SHARP_CORNERS_BIT;
6453
{
6454
DWORD value = wd.sharp_corners ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;
6455
::DwmSetWindowAttribute(wd.hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));
6456
}
6457
6458
if (is_dark_mode_supported() && dark_title_available) {
6459
BOOL value = is_dark_mode();
6460
::DwmSetWindowAttribute(wd.hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
6461
}
6462
6463
RECT real_client_rect;
6464
GetClientRect(wd.hWnd, &real_client_rect);
6465
6466
#ifdef RD_ENABLED
6467
if (rendering_context) {
6468
union {
6469
#ifdef VULKAN_ENABLED
6470
RenderingContextDriverVulkanWindows::WindowPlatformData vulkan;
6471
#endif
6472
#ifdef D3D12_ENABLED
6473
RenderingContextDriverD3D12::WindowPlatformData d3d12;
6474
#endif
6475
} wpd;
6476
#ifdef VULKAN_ENABLED
6477
if (rendering_driver == "vulkan") {
6478
wpd.vulkan.window = wd.hWnd;
6479
wpd.vulkan.instance = hInstance;
6480
}
6481
#endif
6482
#ifdef D3D12_ENABLED
6483
if (rendering_driver == "d3d12") {
6484
wpd.d3d12.window = wd.hWnd;
6485
}
6486
#endif
6487
if (rendering_context->window_create(id, &wpd) != OK) {
6488
ERR_PRINT(vformat("Failed to create %s window.", rendering_driver));
6489
memdelete(rendering_context);
6490
rendering_context = nullptr;
6491
windows.erase(id);
6492
return INVALID_WINDOW_ID;
6493
}
6494
6495
rendering_context->window_set_size(id, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top);
6496
rendering_context->window_set_vsync_mode(id, p_vsync_mode);
6497
wd.context_created = true;
6498
}
6499
#endif
6500
6501
#ifdef GLES3_ENABLED
6502
if (gl_manager_native) {
6503
if (gl_manager_native->window_create(id, wd.hWnd, hInstance, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top) != OK) {
6504
memdelete(gl_manager_native);
6505
gl_manager_native = nullptr;
6506
windows.erase(id);
6507
ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");
6508
}
6509
window_set_vsync_mode(p_vsync_mode, id);
6510
}
6511
6512
if (gl_manager_angle) {
6513
if (gl_manager_angle->window_create(id, nullptr, wd.hWnd, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top) != OK) {
6514
memdelete(gl_manager_angle);
6515
gl_manager_angle = nullptr;
6516
windows.erase(id);
6517
ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window.");
6518
}
6519
window_set_vsync_mode(p_vsync_mode, id);
6520
}
6521
#endif
6522
6523
RegisterTouchWindow(wd.hWnd, 0);
6524
DragAcceptFiles(wd.hWnd, true);
6525
6526
if ((tablet_get_current_driver() == "wintab") && wintab_available) {
6527
wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);
6528
wd.wtlc.lcOptions |= CXO_MESSAGES;
6529
wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;
6530
wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
6531
wd.wtlc.lcPktMode = 0;
6532
wd.wtlc.lcOutOrgX = 0;
6533
wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;
6534
wd.wtlc.lcOutOrgY = 0;
6535
wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;
6536
wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);
6537
if (wd.wtctx) {
6538
wintab_WTEnable(wd.wtctx, true);
6539
AXIS pressure;
6540
if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {
6541
wd.min_pressure = int(pressure.axMin);
6542
wd.max_pressure = int(pressure.axMax);
6543
}
6544
AXIS orientation[3];
6545
if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {
6546
wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;
6547
}
6548
} else {
6549
print_verbose("WinTab context creation failed.");
6550
}
6551
} else {
6552
wd.wtctx = nullptr;
6553
}
6554
6555
if (p_mode == WINDOW_MODE_MAXIMIZED) {
6556
wd.maximized = true;
6557
wd.minimized = false;
6558
}
6559
6560
if (p_mode == WINDOW_MODE_MINIMIZED) {
6561
wd.maximized = false;
6562
wd.minimized = true;
6563
}
6564
6565
wd.last_pressure = 0;
6566
wd.last_pressure_update = 0;
6567
wd.last_tilt = Vector2();
6568
6569
IPropertyStore *prop_store;
6570
HRESULT hr = SHGetPropertyStoreForWindow(wd.hWnd, IID_IPropertyStore, (void **)&prop_store);
6571
if (hr == S_OK) {
6572
PROPVARIANT val;
6573
String appname;
6574
if (Engine::get_singleton()->is_editor_hint()) {
6575
appname = "Godot.GodotEditor." + String(GODOT_VERSION_FULL_CONFIG);
6576
} else {
6577
String name = GLOBAL_GET("application/config/name");
6578
String version = GLOBAL_GET("application/config/version");
6579
if (version.is_empty()) {
6580
version = "0";
6581
}
6582
String clean_app_name = name.to_pascal_case();
6583
for (int i = 0; i < clean_app_name.length(); i++) {
6584
if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {
6585
clean_app_name[i] = '_';
6586
}
6587
}
6588
clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");
6589
appname = "Godot." + clean_app_name + "." + version;
6590
}
6591
InitPropVariantFromString((PCWSTR)appname.utf16().get_data(), &val);
6592
prop_store->SetValue(PKEY_AppUserModel_ID, val);
6593
prop_store->Release();
6594
}
6595
6596
// IME.
6597
wd.im_himc = ImmGetContext(wd.hWnd);
6598
ImmAssociateContext(wd.hWnd, (HIMC) nullptr);
6599
6600
wd.im_position = Vector2();
6601
6602
if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN || p_mode == WINDOW_MODE_MAXIMIZED) {
6603
RECT r;
6604
GetClientRect(wd.hWnd, &r);
6605
ClientToScreen(wd.hWnd, (POINT *)&r.left);
6606
ClientToScreen(wd.hWnd, (POINT *)&r.right);
6607
wd.last_pos = Point2i(r.left, r.top) - _get_screens_origin();
6608
wd.width = r.right - r.left - off_x;
6609
wd.height = r.bottom - r.top;
6610
} else {
6611
wd.last_pos = p_rect.position;
6612
wd.width = p_rect.size.width;
6613
wd.height = p_rect.size.height;
6614
}
6615
6616
wd.create_completed = true;
6617
// Set size of maximized borderless window (by default it covers the entire screen).
6618
if (!p_parent_hwnd && p_mode == WINDOW_MODE_MAXIMIZED && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {
6619
SetWindowPos(wd.hWnd, HWND_TOP, usable_rect.position.x - off_x, usable_rect.position.y, usable_rect.size.width + off_x, usable_rect.size.height, SWP_NOZORDER | SWP_NOACTIVATE);
6620
}
6621
_update_window_mouse_passthrough(id);
6622
window_id_counter++;
6623
}
6624
6625
return id;
6626
}
6627
6628
BitField<DisplayServerWindows::DriverID> DisplayServerWindows::tested_drivers = 0;
6629
6630
// WinTab API.
6631
bool DisplayServerWindows::wintab_available = false;
6632
WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr;
6633
WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr;
6634
WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr;
6635
WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;
6636
WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
6637
6638
// UXTheme API.
6639
bool DisplayServerWindows::dark_title_available = false;
6640
bool DisplayServerWindows::use_legacy_dark_mode_before_20H1 = false;
6641
bool DisplayServerWindows::ux_theme_available = false;
6642
ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;
6643
GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;
6644
GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr;
6645
GetImmersiveUserColorSetPreferencePtr DisplayServerWindows::GetImmersiveUserColorSetPreference = nullptr;
6646
6647
Vector2i _get_device_ids_reg(const String &p_device_name) {
6648
Vector2i out;
6649
6650
String subkey = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}";
6651
HKEY hkey = nullptr;
6652
LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)subkey.utf16().get_data(), 0, KEY_READ, &hkey);
6653
if (result != ERROR_SUCCESS) {
6654
return Vector2i();
6655
}
6656
6657
DWORD subkeys = 0;
6658
result = RegQueryInfoKeyW(hkey, nullptr, nullptr, nullptr, &subkeys, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
6659
if (result != ERROR_SUCCESS) {
6660
RegCloseKey(hkey);
6661
return Vector2i();
6662
}
6663
for (DWORD i = 0; i < subkeys; i++) {
6664
WCHAR key_name[MAX_PATH] = L"";
6665
DWORD key_name_size = MAX_PATH;
6666
result = RegEnumKeyExW(hkey, i, key_name, &key_name_size, nullptr, nullptr, nullptr, nullptr);
6667
if (result != ERROR_SUCCESS) {
6668
continue;
6669
}
6670
String id = String::utf16((const char16_t *)key_name, key_name_size);
6671
if (!id.is_empty()) {
6672
HKEY sub_hkey = nullptr;
6673
result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(subkey + "\\" + id).utf16().get_data(), 0, KEY_QUERY_VALUE, &sub_hkey);
6674
if (result != ERROR_SUCCESS) {
6675
continue;
6676
}
6677
6678
WCHAR buffer[4096];
6679
DWORD buffer_len = 4096;
6680
DWORD vtype = REG_SZ;
6681
if (RegQueryValueExW(sub_hkey, L"DriverDesc", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) != ERROR_SUCCESS || buffer_len == 0) {
6682
buffer_len = 4096;
6683
if (RegQueryValueExW(sub_hkey, L"HardwareInformation.AdapterString", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) != ERROR_SUCCESS || buffer_len == 0) {
6684
RegCloseKey(sub_hkey);
6685
continue;
6686
}
6687
}
6688
6689
String driver_name = String::utf16((const char16_t *)buffer, buffer_len).strip_edges();
6690
if (driver_name == p_device_name) {
6691
String driver_id;
6692
6693
buffer_len = 4096;
6694
if (RegQueryValueExW(sub_hkey, L"MatchingDeviceId", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) == ERROR_SUCCESS && buffer_len != 0) {
6695
driver_id = String::utf16((const char16_t *)buffer, buffer_len).strip_edges();
6696
6697
Vector<String> id_parts = driver_id.to_lower().split("&");
6698
for (const String &id_part : id_parts) {
6699
int ven_off = id_part.find("ven_");
6700
if (ven_off >= 0) {
6701
out.x = id_part.substr(ven_off + 4).hex_to_int();
6702
}
6703
int dev_off = id_part.find("dev_");
6704
if (dev_off >= 0) {
6705
out.y = id_part.substr(dev_off + 4).hex_to_int();
6706
}
6707
}
6708
6709
RegCloseKey(sub_hkey);
6710
break;
6711
}
6712
}
6713
RegCloseKey(sub_hkey);
6714
}
6715
}
6716
RegCloseKey(hkey);
6717
return out;
6718
}
6719
6720
Vector2i _get_device_ids_wmi(const String &p_device_name) {
6721
if (p_device_name.is_empty()) {
6722
return Vector2i();
6723
}
6724
6725
REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID
6726
REFIID uuid = IID_IWbemLocator; // Interface UUID
6727
IWbemLocator *wbemLocator = nullptr; // to get the services
6728
IWbemServices *wbemServices = nullptr; // to get the class
6729
IEnumWbemClassObject *iter = nullptr;
6730
IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.
6731
6732
HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);
6733
if (hr != S_OK) {
6734
return Vector2i();
6735
}
6736
BSTR resource_name = SysAllocString(L"root\\CIMV2");
6737
hr = wbemLocator->ConnectServer(resource_name, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemServices);
6738
SysFreeString(resource_name);
6739
6740
SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices`
6741
if (hr != S_OK) {
6742
SAFE_RELEASE(wbemServices)
6743
return Vector2i();
6744
}
6745
6746
Vector2i ids;
6747
6748
const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", p_device_name);
6749
BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data());
6750
BSTR query_lang = SysAllocString(L"WQL");
6751
hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, nullptr, &iter);
6752
SysFreeString(query_lang);
6753
SysFreeString(query);
6754
if (hr == S_OK) {
6755
ULONG resultCount;
6756
hr = iter->Next(5000, 1, pnpSDriverObject, &resultCount); // Get exactly 1. Wait max 5 seconds.
6757
6758
if (hr == S_OK && resultCount > 0) {
6759
VARIANT did;
6760
VariantInit(&did);
6761
BSTR object_name = SysAllocString(L"DeviceID");
6762
hr = pnpSDriverObject[0]->Get(object_name, 0, &did, nullptr, nullptr);
6763
SysFreeString(object_name);
6764
if (hr == S_OK) {
6765
String device_id = String(V_BSTR(&did));
6766
ids.x = device_id.get_slicec('&', 0).lstrip("PCI\\VEN_").hex_to_int();
6767
ids.y = device_id.get_slicec('&', 1).lstrip("DEV_").hex_to_int();
6768
}
6769
6770
for (ULONG i = 0; i < resultCount; i++) {
6771
SAFE_RELEASE(pnpSDriverObject[i])
6772
}
6773
}
6774
}
6775
6776
SAFE_RELEASE(wbemServices)
6777
SAFE_RELEASE(iter)
6778
6779
return ids;
6780
}
6781
6782
Vector2i _get_device_ids(const String &p_device_name) {
6783
Vector2i out = _get_device_ids_reg(p_device_name);
6784
if (out == Vector2i()) {
6785
out = _get_device_ids_wmi(p_device_name);
6786
}
6787
return out;
6788
}
6789
6790
bool DisplayServerWindows::is_dark_mode_supported() const {
6791
return ux_theme_available;
6792
}
6793
6794
bool DisplayServerWindows::is_dark_mode() const {
6795
return ux_theme_available && ShouldAppsUseDarkMode();
6796
}
6797
6798
Color DisplayServerWindows::get_accent_color() const {
6799
if (!ux_theme_available) {
6800
return Color(0, 0, 0, 0);
6801
}
6802
6803
int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"), false, 0);
6804
return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);
6805
}
6806
6807
Color DisplayServerWindows::get_base_color() const {
6808
if (!ux_theme_available) {
6809
return Color(0, 0, 0, 0);
6810
}
6811
6812
int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(ShouldAppsUseDarkMode() ? L"ImmersiveDarkChromeMediumLow" : L"ImmersiveLightChromeMediumLow"), false, 0);
6813
return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);
6814
}
6815
6816
void DisplayServerWindows::set_system_theme_change_callback(const Callable &p_callable) {
6817
system_theme_changed = p_callable;
6818
}
6819
6820
int DisplayServerWindows::tablet_get_driver_count() const {
6821
return tablet_drivers.size();
6822
}
6823
6824
String DisplayServerWindows::tablet_get_driver_name(int p_driver) const {
6825
if (p_driver < 0 || p_driver >= tablet_drivers.size()) {
6826
return "";
6827
} else {
6828
return tablet_drivers[p_driver];
6829
}
6830
}
6831
6832
String DisplayServerWindows::tablet_get_current_driver() const {
6833
return tablet_driver;
6834
}
6835
6836
void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {
6837
if (tablet_get_driver_count() == 0) {
6838
return;
6839
}
6840
6841
String driver = p_driver;
6842
if (driver == "auto") {
6843
if (!winink_disabled) {
6844
driver = "winink";
6845
} else if (wintab_available) {
6846
driver = "wintab";
6847
} else {
6848
driver = "dummy";
6849
}
6850
}
6851
6852
bool found = false;
6853
for (int i = 0; i < tablet_get_driver_count(); i++) {
6854
if (driver == tablet_get_driver_name(i)) {
6855
found = true;
6856
}
6857
}
6858
if (found) {
6859
_update_tablet_ctx(tablet_driver, driver);
6860
tablet_driver = driver;
6861
} else {
6862
ERR_PRINT("Unknown tablet driver " + p_driver + ".");
6863
}
6864
}
6865
6866
DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
6867
KeyMappingWindows::initialize();
6868
6869
tested_drivers.clear();
6870
6871
drop_events = false;
6872
key_event_pos = 0;
6873
6874
hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance();
6875
6876
pressrc = 0;
6877
old_invalid = true;
6878
mouse_mode = MOUSE_MODE_VISIBLE;
6879
6880
rendering_driver = p_rendering_driver;
6881
6882
// Init TTS
6883
bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
6884
if (tts_enabled) {
6885
initialize_tts();
6886
}
6887
native_menu = memnew(NativeMenuWindows);
6888
6889
#ifdef ACCESSKIT_ENABLED
6890
if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {
6891
accessibility_driver = memnew(AccessibilityDriverAccessKit);
6892
if (accessibility_driver->init() != OK) {
6893
if (OS::get_singleton()->is_stdout_verbose()) {
6894
ERR_PRINT("Can't create an accessibility driver, accessibility support disabled!");
6895
}
6896
memdelete(accessibility_driver);
6897
accessibility_driver = nullptr;
6898
}
6899
}
6900
#endif
6901
6902
// Enforce default keep screen on value.
6903
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
6904
6905
// Load Windows version info.
6906
ZeroMemory(&os_ver, sizeof(OSVERSIONINFOW));
6907
os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
6908
6909
HMODULE nt_lib = LoadLibraryW(L"ntdll.dll");
6910
bool is_wine = false;
6911
if (nt_lib) {
6912
WineGetVersionPtr wine_get_version = (WineGetVersionPtr)(void *)GetProcAddress(nt_lib, "wine_get_version"); // Do not read Windows build number under Wine, it can be set to arbitrary value.
6913
if (wine_get_version) {
6914
is_wine = true;
6915
} else {
6916
RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)(void *)GetProcAddress(nt_lib, "RtlGetVersion");
6917
if (RtlGetVersion) {
6918
RtlGetVersion(&os_ver);
6919
}
6920
}
6921
FreeLibrary(nt_lib);
6922
}
6923
6924
// Load UXTheme.
6925
if (os_ver.dwBuildNumber >= 10240) { // Not available on Wine, use only if real Windows 10/11 detected.
6926
HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");
6927
if (ux_theme_lib) {
6928
ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132));
6929
GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));
6930
GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));
6931
GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
6932
if (os_ver.dwBuildNumber >= 17763) { // Windows 10 Redstone 5 (1809)+ only.
6933
AllowDarkModeForAppPtr AllowDarkModeForApp = nullptr;
6934
SetPreferredAppModePtr SetPreferredAppMode = nullptr;
6935
FlushMenuThemesPtr FlushMenuThemes = nullptr;
6936
if (os_ver.dwBuildNumber < 18362) { // Windows 10 Redstone 5 (1809) and 19H1 (1903) only.
6937
AllowDarkModeForApp = (AllowDarkModeForAppPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));
6938
} else { // Windows 10 19H2 (1909)+ only.
6939
SetPreferredAppMode = (SetPreferredAppModePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));
6940
FlushMenuThemes = (FlushMenuThemesPtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));
6941
}
6942
RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyState = (RefreshImmersiveColorPolicyStatePtr)(void *)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(104));
6943
if (ShouldAppsUseDarkMode) {
6944
bool dark_mode = ShouldAppsUseDarkMode();
6945
if (SetPreferredAppMode) {
6946
SetPreferredAppMode(dark_mode ? APPMODE_ALLOWDARK : APPMODE_DEFAULT);
6947
} else if (AllowDarkModeForApp) {
6948
AllowDarkModeForApp(dark_mode);
6949
}
6950
if (RefreshImmersiveColorPolicyState) {
6951
RefreshImmersiveColorPolicyState();
6952
}
6953
if (FlushMenuThemes) {
6954
FlushMenuThemes();
6955
}
6956
}
6957
}
6958
6959
ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
6960
if (os_ver.dwBuildNumber >= 18363) {
6961
dark_title_available = true;
6962
if (os_ver.dwBuildNumber < 19041) {
6963
use_legacy_dark_mode_before_20H1 = true;
6964
}
6965
}
6966
}
6967
}
6968
6969
tablet_drivers.push_back("auto");
6970
tablet_drivers.push_back("winink");
6971
6972
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
6973
HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
6974
if (wintab_lib) {
6975
wintab_WTOpen = (WTOpenPtr)(void *)GetProcAddress(wintab_lib, "WTOpenW");
6976
wintab_WTClose = (WTClosePtr)(void *)GetProcAddress(wintab_lib, "WTClose");
6977
wintab_WTInfo = (WTInfoPtr)(void *)GetProcAddress(wintab_lib, "WTInfoW");
6978
wintab_WTPacket = (WTPacketPtr)(void *)GetProcAddress(wintab_lib, "WTPacket");
6979
wintab_WTEnable = (WTEnablePtr)(void *)GetProcAddress(wintab_lib, "WTEnable");
6980
6981
wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable;
6982
}
6983
6984
if (wintab_available) {
6985
tablet_drivers.push_back("wintab");
6986
}
6987
6988
tablet_drivers.push_back("dummy");
6989
6990
String wacom_cfg = OS::get_singleton()->get_config_path().path_join("WTablet").path_join("Wacom_Tablet.dat");
6991
if (FileAccess::exists(wacom_cfg)) {
6992
Ref<XMLParser> parser;
6993
parser.instantiate();
6994
if (parser->open(wacom_cfg) == OK) {
6995
while (parser->read() == OK) {
6996
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
6997
continue;
6998
}
6999
if (parser->get_node_name() == "WinUseInk") {
7000
parser->read();
7001
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
7002
winink_disabled = (parser->get_node_data().to_lower().strip_edges() != "true");
7003
print_verbose(vformat("Wacom tablet config found at \"%s\", Windows Ink support is %s.", wacom_cfg, winink_disabled ? "disabled" : "enabled"));
7004
break;
7005
}
7006
}
7007
}
7008
}
7009
}
7010
7011
if (OS::get_singleton()->is_hidpi_allowed()) {
7012
SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE);
7013
}
7014
7015
HMODULE comctl32 = LoadLibraryW(L"comctl32.dll");
7016
if (comctl32) {
7017
typedef BOOL(WINAPI * InitCommonControlsExPtr)(_In_ const INITCOMMONCONTROLSEX *picce);
7018
InitCommonControlsExPtr init_common_controls_ex = (InitCommonControlsExPtr)(void *)GetProcAddress(comctl32, "InitCommonControlsEx");
7019
7020
// Fails if the incorrect version was loaded. Probably not a big enough deal to print an error about.
7021
if (init_common_controls_ex) {
7022
INITCOMMONCONTROLSEX icc = {};
7023
icc.dwICC = ICC_STANDARD_CLASSES;
7024
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
7025
if (!init_common_controls_ex(&icc)) {
7026
WARN_PRINT("Unable to initialize Windows common controls. Native dialogs may not work properly.");
7027
}
7028
}
7029
FreeLibrary(comctl32);
7030
}
7031
7032
OleInitialize(nullptr);
7033
7034
memset(&wc, 0, sizeof(WNDCLASSEXW));
7035
wc.cbSize = sizeof(WNDCLASSEXW);
7036
wc.style = CS_OWNDC | CS_DBLCLKS;
7037
wc.lpfnWndProc = (WNDPROC)::WndProc;
7038
wc.cbClsExtra = 0;
7039
wc.cbWndExtra = 0;
7040
wc.hInstance = hInstance ? hInstance : GetModuleHandle(nullptr);
7041
wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO);
7042
wc.hCursor = nullptr;
7043
wc.hbrBackground = nullptr;
7044
wc.lpszMenuName = nullptr;
7045
wc.lpszClassName = L"Engine";
7046
7047
if (!RegisterClassExW(&wc)) {
7048
r_error = ERR_UNAVAILABLE;
7049
return;
7050
}
7051
7052
_register_raw_input_devices(INVALID_WINDOW_ID);
7053
7054
// Init context and rendering device.
7055
if (rendering_driver == "dummy") {
7056
RasterizerDummy::make_current();
7057
}
7058
7059
#if defined(RD_ENABLED)
7060
[[maybe_unused]] bool fallback_to_vulkan = GLOBAL_GET("rendering/rendering_device/fallback_to_vulkan");
7061
[[maybe_unused]] bool fallback_to_d3d12 = GLOBAL_GET("rendering/rendering_device/fallback_to_d3d12");
7062
7063
#if defined(VULKAN_ENABLED)
7064
if (rendering_driver == "vulkan") {
7065
rendering_context = memnew(RenderingContextDriverVulkanWindows);
7066
tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);
7067
}
7068
#else
7069
fallback_to_d3d12 = true; // Always enable fallback if engine was built w/o other driver support.
7070
#endif
7071
#if defined(D3D12_ENABLED)
7072
if (rendering_driver == "d3d12") {
7073
rendering_context = memnew(RenderingContextDriverD3D12);
7074
tested_drivers.set_flag(DRIVER_ID_RD_D3D12);
7075
}
7076
#else
7077
fallback_to_vulkan = true; // Always enable fallback if engine was built w/o other driver support.
7078
#endif
7079
7080
if (rendering_context) {
7081
if (rendering_context->initialize() != OK) {
7082
bool failed = true;
7083
#if defined(VULKAN_ENABLED)
7084
if (failed && fallback_to_vulkan && rendering_driver != "vulkan") {
7085
memdelete(rendering_context);
7086
rendering_context = memnew(RenderingContextDriverVulkanWindows);
7087
tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);
7088
if (rendering_context->initialize() == OK) {
7089
WARN_PRINT("Your video card drivers seem not to support Direct3D 12, switching to Vulkan.");
7090
rendering_driver = "vulkan";
7091
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
7092
failed = false;
7093
}
7094
}
7095
#endif
7096
#if defined(D3D12_ENABLED)
7097
if (failed && fallback_to_d3d12 && rendering_driver != "d3d12") {
7098
memdelete(rendering_context);
7099
rendering_context = memnew(RenderingContextDriverD3D12);
7100
tested_drivers.set_flag(DRIVER_ID_RD_D3D12);
7101
if (rendering_context->initialize() == OK) {
7102
WARN_PRINT("Your video card drivers seem not to support Vulkan, switching to Direct3D 12.");
7103
rendering_driver = "d3d12";
7104
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
7105
failed = false;
7106
}
7107
}
7108
#endif
7109
#if defined(GLES3_ENABLED)
7110
bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");
7111
if (failed && fallback_to_opengl3 && rendering_driver != "opengl3") {
7112
memdelete(rendering_context);
7113
rendering_context = nullptr;
7114
tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);
7115
WARN_PRINT("Your video card drivers seem not to support Direct3D 12 or Vulkan, switching to OpenGL 3.");
7116
rendering_driver = "opengl3";
7117
OS::get_singleton()->set_current_rendering_method("gl_compatibility");
7118
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
7119
failed = false;
7120
}
7121
#endif
7122
if (failed) {
7123
memdelete(rendering_context);
7124
rendering_context = nullptr;
7125
r_error = ERR_UNAVAILABLE;
7126
return;
7127
}
7128
}
7129
}
7130
#endif
7131
7132
#if defined(GLES3_ENABLED)
7133
7134
bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_angle");
7135
bool show_warning = true;
7136
7137
if (rendering_driver == "opengl3") {
7138
// There's no native OpenGL drivers on Windows for ARM, always enable fallback.
7139
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
7140
fallback = true;
7141
show_warning = false;
7142
#else
7143
typedef BOOL(WINAPI * IsWow64Process2Ptr)(HANDLE, USHORT *, USHORT *);
7144
7145
IsWow64Process2Ptr IsWow64Process2 = (IsWow64Process2Ptr)(void *)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process2");
7146
if (IsWow64Process2) {
7147
USHORT process_arch = 0;
7148
USHORT machine_arch = 0;
7149
if (!IsWow64Process2(GetCurrentProcess(), &process_arch, &machine_arch)) {
7150
machine_arch = 0;
7151
}
7152
if (machine_arch == 0xAA64) {
7153
fallback = true;
7154
show_warning = false;
7155
}
7156
}
7157
#endif
7158
}
7159
7160
bool gl_supported = true;
7161
if (fallback && !is_wine && (rendering_driver == "opengl3")) {
7162
Dictionary gl_info = detect_wgl();
7163
7164
bool force_angle = false;
7165
gl_supported = gl_info["version"].operator int() >= 30003;
7166
7167
Vector2i device_id = Vector2i(-1, -1);
7168
Array device_list = GLOBAL_GET("rendering/gl_compatibility/force_angle_on_devices");
7169
for (int i = 0; i < device_list.size(); i++) {
7170
const Dictionary &device = device_list[i];
7171
if (device.has("vendor") && device.has("name")) {
7172
const String &vendor = device["vendor"];
7173
const String &name = device["name"];
7174
if (gl_info["vendor"].operator String().containsn(vendor) && (name == "*" || gl_info["name"].operator String().containsn(name))) {
7175
// Check vendor/device names.
7176
force_angle = true;
7177
break;
7178
} else if (vendor.begins_with("0x") && name.begins_with("0x")) {
7179
if (device_id == Vector2i(-1, -1)) {
7180
// Load device IDs.
7181
device_id = _get_device_ids(gl_info["name"]);
7182
}
7183
if (device_id.x == vendor.lstrip("0x").hex_to_int() && device_id.y == name.lstrip("0x").hex_to_int()) {
7184
// Check vendor/device IDs.
7185
force_angle = true;
7186
break;
7187
}
7188
}
7189
}
7190
}
7191
7192
if (force_angle || (gl_info["version"].operator int() < 30003)) {
7193
tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);
7194
if (show_warning) {
7195
if (gl_info["version"].operator int() < 30003) {
7196
WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE.");
7197
} else {
7198
WARN_PRINT("Your video card drivers are known to have low quality OpenGL 3.3 support, switching to ANGLE.");
7199
}
7200
}
7201
rendering_driver = "opengl3_angle";
7202
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
7203
}
7204
}
7205
7206
if (rendering_driver == "opengl3_angle") {
7207
gl_manager_angle = memnew(GLManagerANGLE_Windows);
7208
tested_drivers.set_flag(DRIVER_ID_COMPAT_ANGLE_D3D11);
7209
7210
if (gl_manager_angle->initialize() != OK) {
7211
memdelete(gl_manager_angle);
7212
gl_manager_angle = nullptr;
7213
bool fallback_to_native = GLOBAL_GET("rendering/gl_compatibility/fallback_to_native");
7214
if (fallback_to_native && gl_supported) {
7215
#ifdef EGL_STATIC
7216
WARN_PRINT("Your video card drivers seem not to support GLES3 / ANGLE, switching to native OpenGL.");
7217
#else
7218
WARN_PRINT("Your video card drivers seem not to support GLES3 / ANGLE or ANGLE dynamic libraries (libEGL.dll and libGLESv2.dll) are missing, switching to native OpenGL.");
7219
#endif
7220
rendering_driver = "opengl3";
7221
} else {
7222
r_error = ERR_UNAVAILABLE;
7223
ERR_FAIL_MSG("Could not initialize ANGLE OpenGL.");
7224
}
7225
}
7226
}
7227
if (rendering_driver == "opengl3") {
7228
gl_manager_native = memnew(GLManagerNative_Windows);
7229
tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);
7230
7231
if (gl_manager_native->initialize() != OK) {
7232
memdelete(gl_manager_native);
7233
gl_manager_native = nullptr;
7234
r_error = ERR_UNAVAILABLE;
7235
ERR_FAIL_MSG("Could not initialize native OpenGL.");
7236
}
7237
}
7238
7239
if (rendering_driver == "opengl3") {
7240
RasterizerGLES3::make_current(true);
7241
}
7242
if (rendering_driver == "opengl3_angle") {
7243
RasterizerGLES3::make_current(false);
7244
}
7245
#endif
7246
String appname;
7247
if (Engine::get_singleton()->is_editor_hint()) {
7248
appname = "Godot.GodotEditor." + String(GODOT_VERSION_FULL_CONFIG);
7249
} else {
7250
String name = GLOBAL_GET("application/config/name");
7251
String version = GLOBAL_GET("application/config/version");
7252
if (version.is_empty()) {
7253
version = "0";
7254
}
7255
String clean_app_name = name.to_pascal_case();
7256
for (int i = 0; i < clean_app_name.length(); i++) {
7257
if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {
7258
clean_app_name[i] = '_';
7259
}
7260
}
7261
clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");
7262
appname = "Godot." + clean_app_name + "." + version;
7263
7264
#ifndef TOOLS_ENABLED
7265
// Set for exported projects only.
7266
HKEY key;
7267
if (RegOpenKeyW(HKEY_CURRENT_USER_LOCAL_SETTINGS, L"Software\\Microsoft\\Windows\\Shell\\MuiCache", &key) == ERROR_SUCCESS) {
7268
Char16String cs_name = name.utf16();
7269
String value_name = OS::get_singleton()->get_executable_path().replace_char('/', '\\') + ".FriendlyAppName";
7270
RegSetValueExW(key, (LPCWSTR)value_name.utf16().get_data(), 0, REG_SZ, (const BYTE *)cs_name.get_data(), cs_name.size() * sizeof(WCHAR));
7271
RegCloseKey(key);
7272
}
7273
#endif
7274
}
7275
SetCurrentProcessExplicitAppUserModelID((PCWSTR)appname.utf16().get_data());
7276
7277
mouse_monitor = SetWindowsHookEx(WH_MOUSE, ::MouseProc, nullptr, GetCurrentThreadId());
7278
7279
Point2i window_position;
7280
if (p_position != nullptr) {
7281
window_position = *p_position;
7282
} else {
7283
if (p_screen == SCREEN_OF_MAIN_WINDOW) {
7284
p_screen = SCREEN_PRIMARY;
7285
}
7286
Rect2i scr_rect = screen_get_usable_rect(p_screen);
7287
window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2;
7288
}
7289
7290
HWND parent_hwnd = NULL;
7291
if (p_parent_window) {
7292
// Parented window.
7293
parent_hwnd = (HWND)p_parent_window;
7294
}
7295
7296
WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID, parent_hwnd);
7297
if (main_window == INVALID_WINDOW_ID) {
7298
r_error = ERR_UNAVAILABLE;
7299
ERR_FAIL_MSG("Failed to create main window.");
7300
}
7301
7302
#ifdef SDL_ENABLED
7303
joypad_sdl = memnew(JoypadSDL(windows[MAIN_WINDOW_ID].hWnd));
7304
if (joypad_sdl->initialize() != OK) {
7305
ERR_PRINT("Couldn't initialize SDL joypad input driver.");
7306
memdelete(joypad_sdl);
7307
joypad_sdl = nullptr;
7308
}
7309
#endif
7310
7311
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
7312
if (p_flags & (1 << i)) {
7313
window_set_flag(WindowFlags(i), true, main_window);
7314
}
7315
}
7316
7317
windows[MAIN_WINDOW_ID].initialized = true;
7318
7319
#ifdef ACCESSKIT_ENABLED
7320
if (accessibility_screen_reader_active()) {
7321
_THREAD_SAFE_LOCK_
7322
uint64_t time_wait = OS::get_singleton()->get_ticks_msec();
7323
while (true) {
7324
MSG msg = {};
7325
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
7326
TranslateMessage(&msg);
7327
DispatchMessageW(&msg);
7328
}
7329
7330
uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_wait;
7331
if (delta > 500 || get_object_received) {
7332
break;
7333
}
7334
}
7335
_THREAD_SAFE_UNLOCK_
7336
}
7337
#endif
7338
7339
#if defined(RD_ENABLED)
7340
if (rendering_context) {
7341
rendering_device = memnew(RenderingDevice);
7342
if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {
7343
memdelete(rendering_device);
7344
rendering_device = nullptr;
7345
memdelete(rendering_context);
7346
rendering_context = nullptr;
7347
r_error = ERR_UNAVAILABLE;
7348
return;
7349
}
7350
rendering_device->screen_create(MAIN_WINDOW_ID);
7351
7352
RendererCompositorRD::make_current();
7353
}
7354
#endif
7355
7356
if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) {
7357
// Increase priority for projects that are not in low-processor mode (typically games)
7358
// to reduce the risk of frame stuttering.
7359
// This is not done for the editor to prevent importers or resource bakers
7360
// from making the system unresponsive.
7361
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
7362
DWORD index = 0;
7363
HANDLE handle = AvSetMmThreadCharacteristicsW(L"Games", &index);
7364
if (handle) {
7365
AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);
7366
}
7367
7368
// This is needed to make sure that background work does not starve the main thread.
7369
// This is only setting the priority of this thread, not the whole process.
7370
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
7371
}
7372
7373
cursor_shape = CURSOR_ARROW;
7374
7375
_update_real_mouse_position(MAIN_WINDOW_ID);
7376
7377
r_error = OK;
7378
7379
static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
7380
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
7381
}
7382
7383
Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
7384
Vector<String> drivers;
7385
7386
#ifdef VULKAN_ENABLED
7387
drivers.push_back("vulkan");
7388
#endif
7389
#ifdef D3D12_ENABLED
7390
drivers.push_back("d3d12");
7391
#endif
7392
#ifdef GLES3_ENABLED
7393
drivers.push_back("opengl3");
7394
drivers.push_back("opengl3_angle");
7395
#endif
7396
drivers.push_back("dummy");
7397
7398
return drivers;
7399
}
7400
7401
DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
7402
DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));
7403
if (r_error != OK) {
7404
if (tested_drivers == 0) {
7405
OS::get_singleton()->alert("Failed to register the window class.", "Unable to initialize DisplayServer");
7406
} else if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN) || tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {
7407
Vector<String> drivers;
7408
if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN)) {
7409
drivers.push_back("Vulkan");
7410
}
7411
if (tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {
7412
drivers.push_back("Direct3D 12");
7413
}
7414
String executable_name = OS::get_singleton()->get_executable_path().get_file();
7415
OS::get_singleton()->alert(
7416
vformat("Your video card drivers seem not to support the required %s version.\n\n"
7417
"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
7418
"You can enable the OpenGL 3 driver by starting the engine from the\n"
7419
"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"
7420
"If you have recently updated your video card drivers, try rebooting.",
7421
String(" or ").join(drivers),
7422
executable_name),
7423
"Unable to initialize video driver");
7424
} else {
7425
Vector<String> drivers;
7426
if (tested_drivers.has_flag(DRIVER_ID_COMPAT_OPENGL3)) {
7427
drivers.push_back("OpenGL 3.3");
7428
}
7429
if (tested_drivers.has_flag(DRIVER_ID_COMPAT_ANGLE_D3D11)) {
7430
drivers.push_back("Direct3D 11");
7431
}
7432
OS::get_singleton()->alert(
7433
vformat(
7434
"Your video card drivers seem not to support the required %s version.\n\n"
7435
"If possible, consider updating your video card drivers.\n\n"
7436
"If you have recently updated your video card drivers, try rebooting.",
7437
String(" or ").join(drivers)),
7438
"Unable to initialize video driver");
7439
}
7440
}
7441
return ds;
7442
}
7443
7444
void DisplayServerWindows::register_windows_driver() {
7445
register_create_function("windows", create_func, get_rendering_drivers_func);
7446
}
7447
7448
DisplayServerWindows::~DisplayServerWindows() {
7449
LocalVector<List<FileDialogData *>::Element *> to_remove;
7450
for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {
7451
FileDialogData *fd = E->get();
7452
if (fd->listener_thread.is_started()) {
7453
fd->close_requested.set();
7454
fd->listener_thread.wait_to_finish();
7455
}
7456
to_remove.push_back(E);
7457
}
7458
for (List<FileDialogData *>::Element *E : to_remove) {
7459
memdelete(E->get());
7460
E->erase();
7461
}
7462
7463
#ifdef SDL_ENABLED
7464
if (joypad_sdl) {
7465
memdelete(joypad_sdl);
7466
}
7467
#endif
7468
touch_state.clear();
7469
7470
cursors_cache.clear();
7471
7472
// Destroy all status indicators.
7473
for (HashMap<IndicatorID, IndicatorData>::Iterator E = indicators.begin(); E; ++E) {
7474
NOTIFYICONDATAW ndat;
7475
ZeroMemory(&ndat, sizeof(NOTIFYICONDATAW));
7476
ndat.cbSize = sizeof(NOTIFYICONDATAW);
7477
ndat.hWnd = windows[MAIN_WINDOW_ID].hWnd;
7478
ndat.uID = E->key;
7479
ndat.uVersion = NOTIFYICON_VERSION;
7480
7481
Shell_NotifyIconW(NIM_DELETE, &ndat);
7482
}
7483
7484
if (mouse_monitor) {
7485
UnhookWindowsHookEx(mouse_monitor);
7486
}
7487
7488
if (user_proc) {
7489
SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
7490
}
7491
7492
// Close power request handle.
7493
screen_set_keep_on(false);
7494
7495
if (native_menu) {
7496
memdelete(native_menu);
7497
native_menu = nullptr;
7498
}
7499
7500
#ifdef GLES3_ENABLED
7501
// destroy windows .. NYI?
7502
// FIXME wglDeleteContext is never called
7503
#endif
7504
7505
if (windows.has(MAIN_WINDOW_ID)) {
7506
#ifdef RD_ENABLED
7507
if (rendering_device) {
7508
rendering_device->screen_free(MAIN_WINDOW_ID);
7509
}
7510
7511
if (rendering_context) {
7512
rendering_context->window_destroy(MAIN_WINDOW_ID);
7513
}
7514
#endif
7515
if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) {
7516
wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);
7517
windows[MAIN_WINDOW_ID].wtctx = nullptr;
7518
}
7519
7520
if (windows[MAIN_WINDOW_ID].drop_target != nullptr) {
7521
RevokeDragDrop(windows[MAIN_WINDOW_ID].hWnd);
7522
windows[MAIN_WINDOW_ID].drop_target->Release();
7523
}
7524
7525
DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
7526
}
7527
7528
#ifdef RD_ENABLED
7529
if (rendering_device) {
7530
memdelete(rendering_device);
7531
rendering_device = nullptr;
7532
}
7533
7534
if (rendering_context) {
7535
memdelete(rendering_context);
7536
rendering_context = nullptr;
7537
}
7538
#endif
7539
7540
if (restore_mouse_trails > 1) {
7541
SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);
7542
}
7543
#ifdef GLES3_ENABLED
7544
if (gl_manager_angle) {
7545
memdelete(gl_manager_angle);
7546
gl_manager_angle = nullptr;
7547
}
7548
if (gl_manager_native) {
7549
memdelete(gl_manager_native);
7550
gl_manager_native = nullptr;
7551
}
7552
#endif
7553
#ifdef ACCESSKIT_ENABLED
7554
if (accessibility_driver) {
7555
memdelete(accessibility_driver);
7556
}
7557
#endif
7558
if (tts) {
7559
memdelete(tts);
7560
}
7561
7562
OleUninitialize();
7563
}
7564
7565