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