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