summaryrefslogtreecommitdiff
path: root/platform/windows
diff options
context:
space:
mode:
Diffstat (limited to 'platform/windows')
-rw-r--r--platform/windows/SCsub1
-rw-r--r--platform/windows/crash_handler_windows.cpp67
-rw-r--r--platform/windows/detect.py2
-rw-r--r--platform/windows/display_server_windows.cpp404
-rw-r--r--platform/windows/display_server_windows.h33
-rw-r--r--platform/windows/export/export.cpp79
-rw-r--r--platform/windows/export/export_plugin.cpp174
-rw-r--r--platform/windows/export/export_plugin.h7
-rw-r--r--platform/windows/gl_manager_windows.cpp39
-rw-r--r--platform/windows/gl_manager_windows.h13
-rw-r--r--platform/windows/godot.icobin110755 -> 359559 bytes
-rw-r--r--platform/windows/godot_windows.cpp23
-rw-r--r--platform/windows/joypad_windows.cpp56
-rw-r--r--platform/windows/joypad_windows.h7
-rw-r--r--platform/windows/os_windows.cpp172
-rw-r--r--platform/windows/os_windows.h12
-rw-r--r--platform/windows/tts_windows.cpp269
-rw-r--r--platform/windows/tts_windows.h80
-rw-r--r--platform/windows/windows_terminal_logger.cpp14
19 files changed, 1130 insertions, 322 deletions
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 76234c3065..7e412b140f 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -13,6 +13,7 @@ common_win = [
"display_server_windows.cpp",
"key_mapping_windows.cpp",
"joypad_windows.cpp",
+ "tts_windows.cpp",
"windows_terminal_logger.cpp",
"vulkan_context_win.cpp",
"gl_manager_windows.cpp",
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp
index 5064f6b97f..6ce10e6f0f 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
+#include "core/string/print_string.h"
#include "core/version.h"
#include "main/main.h"
@@ -80,8 +81,9 @@ public:
std::string name() { return std::string(sym->Name); }
std::string undecorated_name() {
- if (*sym->Name == '\0')
+ if (*sym->Name == '\0') {
return "<couldn't map PC to fn name>";
+ }
std::vector<char> und_name(max_name_len);
UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE);
return std::string(&und_name[0], strlen(&und_name[0]));
@@ -128,15 +130,32 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
return EXCEPTION_CONTINUE_SEARCH;
}
- fprintf(stderr, "\n================================================================\n");
- fprintf(stderr, "%s: Program crashed\n", __FUNCTION__);
+ String msg;
+ const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
+ if (proj_settings) {
+ msg = proj_settings->get("debug/settings/crash_handler/message");
+ }
- if (OS::get_singleton()->get_main_loop())
+ // Tell MainLoop about the crash. This can be handled by users too in Node.
+ if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+ }
+
+ print_error("\n================================================================");
+ print_error(vformat("%s: Program crashed", __FUNCTION__));
+
+ // Print the engine version just before, so that people are reminded to include the version in backtrace reports.
+ if (String(VERSION_HASH).is_empty()) {
+ print_error(vformat("Engine version: %s", VERSION_FULL_NAME));
+ } else {
+ print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH));
+ }
+ print_error(vformat("Dumping the backtrace. %s", msg));
// Load the symbols:
- if (!SymInitialize(process, nullptr, false))
+ if (!SymInitialize(process, nullptr, false)) {
return EXCEPTION_CONTINUE_SEARCH;
+ }
SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
@@ -171,20 +190,6 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
IMAGE_NT_HEADERS *h = ImageNtHeader(base);
DWORD image_type = h->FileHeader.Machine;
- String msg;
- const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
- if (proj_settings) {
- msg = proj_settings->get("debug/settings/crash_handler/message");
- }
-
- // Print the engine version just before, so that people are reminded to include the version in backtrace reports.
- if (String(VERSION_HASH).is_empty()) {
- fprintf(stderr, "Engine version: %s\n", VERSION_FULL_NAME);
- } else {
- fprintf(stderr, "Engine version: %s (%s)\n", VERSION_FULL_NAME, VERSION_HASH);
- }
- fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
-
int n = 0;
do {
if (skip_first) {
@@ -193,22 +198,25 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
if (frame.AddrPC.Offset != 0) {
std::string fnName = symbol(process, frame.AddrPC.Offset).undecorated_name();
- if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line))
- fprintf(stderr, "[%d] %s (%s:%d)\n", n, fnName.c_str(), line.FileName, line.LineNumber);
- else
- fprintf(stderr, "[%d] %s\n", n, fnName.c_str());
- } else
- fprintf(stderr, "[%d] ???\n", n);
+ if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line)) {
+ print_error(vformat("[%d] %s (%s:%d)", n, fnName.c_str(), (char *)line.FileName, (int)line.LineNumber));
+ } else {
+ print_error(vformat("[%d] %s", n, fnName.c_str()));
+ }
+ } else {
+ print_error(vformat("[%d] ???", n));
+ }
n++;
}
- if (!StackWalk64(image_type, process, hThread, &frame, context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
+ if (!StackWalk64(image_type, process, hThread, &frame, context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) {
break;
+ }
} while (frame.AddrReturn.Offset != 0 && n < 256);
- fprintf(stderr, "-- END OF BACKTRACE --\n");
- fprintf(stderr, "================================================================\n");
+ print_error("-- END OF BACKTRACE --");
+ print_error("================================================================");
SymCleanup(process);
@@ -225,8 +233,9 @@ CrashHandler::~CrashHandler() {
}
void CrashHandler::disable() {
- if (disabled)
+ if (disabled) {
return;
+ }
disabled = true;
}
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 249a0d2e79..0b18fb74fb 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -252,6 +252,7 @@ def configure_msvc(env, manual_msvc_config):
"kernel32",
"ole32",
"oleaut32",
+ "sapi",
"user32",
"gdi32",
"IPHLPAPI",
@@ -426,6 +427,7 @@ def configure_mingw(env):
"ws2_32",
"kernel32",
"oleaut32",
+ "sapi",
"dinput8",
"dxguid",
"ksuser",
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index c7955ebf31..93cab85441 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -84,6 +84,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
case FEATURE_KEEP_SCREEN_ON:
+ case FEATURE_TEXT_TO_SPEECH:
return true;
default:
return false;
@@ -133,6 +134,41 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
}
}
+bool DisplayServerWindows::tts_is_speaking() const {
+ ERR_FAIL_COND_V(!tts, false);
+ return tts->is_speaking();
+}
+
+bool DisplayServerWindows::tts_is_paused() const {
+ ERR_FAIL_COND_V(!tts, false);
+ return tts->is_paused();
+}
+
+Array DisplayServerWindows::tts_get_voices() const {
+ ERR_FAIL_COND_V(!tts, Array());
+ return tts->get_voices();
+}
+
+void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ ERR_FAIL_COND(!tts);
+ tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
+}
+
+void DisplayServerWindows::tts_pause() {
+ ERR_FAIL_COND(!tts);
+ tts->pause();
+}
+
+void DisplayServerWindows::tts_resume() {
+ ERR_FAIL_COND(!tts);
+ tts->resume();
+}
+
+void DisplayServerWindows::tts_stop() {
+ ERR_FAIL_COND(!tts);
+ tts->stop();
+}
+
void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
_THREAD_SAFE_METHOD_
@@ -150,7 +186,7 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {
return mouse_mode;
}
-void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) {
+void DisplayServerWindows::warp_mouse(const Point2i &p_position) {
_THREAD_SAFE_METHOD_
if (!windows.has(last_focused_window)) {
@@ -158,12 +194,12 @@ void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) {
}
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- old_x = p_to.x;
- old_y = p_to.y;
+ old_x = p_position.x;
+ old_y = p_position.y;
} else {
POINT p;
- p.x = p_to.x;
- p.y = p_to.y;
+ p.x = p_position.x;
+ p.y = p_position.y;
ClientToScreen(windows[last_focused_window].hWnd, &p);
SetCursorPos(p.x, p.y);
@@ -231,7 +267,7 @@ String DisplayServerWindows::clipboard_get() const {
String ret;
if (!OpenClipboard(windows[last_focused_window].hWnd)) {
ERR_FAIL_V_MSG("", "Unable to open clipboard.");
- };
+ }
if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
@@ -240,8 +276,8 @@ String DisplayServerWindows::clipboard_get() const {
if (ptr != nullptr) {
ret = String::utf16((const char16_t *)ptr);
GlobalUnlock(mem);
- };
- };
+ }
+ }
} else if (IsClipboardFormatAvailable(CF_TEXT)) {
HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
@@ -250,9 +286,9 @@ String DisplayServerWindows::clipboard_get() const {
if (ptr != nullptr) {
ret.parse_utf8((const char *)ptr);
GlobalUnlock(mem);
- };
- };
- };
+ }
+ }
+ }
CloseClipboard();
@@ -422,16 +458,16 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau
getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : nullptr;
if ((Shcore == nullptr) || (getDPIForMonitor == nullptr)) {
- if (Shcore)
+ if (Shcore) {
FreeLibrary(Shcore);
+ }
Shcore = (HMODULE)INVALID_HANDLE_VALUE;
}
}
UINT x = 0, y = 0;
- HRESULT hr = E_FAIL;
if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) {
- hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
+ HRESULT hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
dpiX = (int)x;
dpiY = (int)y;
@@ -545,6 +581,9 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
wd.no_focus = true;
}
+ if (p_flags & WINDOW_FLAG_POPUP_BIT) {
+ wd.is_popup = true;
+ }
// Inherit icons from MAIN_WINDOW for all sub windows.
HICON mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_SMALL, 0);
@@ -562,13 +601,14 @@ void DisplayServerWindows::show_window(WindowID p_id) {
ERR_FAIL_COND(!windows.has(p_id));
WindowData &wd = windows[p_id];
+ popup_open(p_id);
if (p_id != MAIN_WINDOW_ID) {
_update_window_style(p_id);
}
- ShowWindow(wd.hWnd, wd.no_focus ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.
- if (!wd.no_focus) {
+ ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.
+ if (!wd.no_focus && !wd.is_popup) {
SetForegroundWindow(wd.hWnd); // Slightly higher priority.
SetFocus(wd.hWnd); // Set keyboard focus.
}
@@ -580,6 +620,8 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted.");
+ popup_close(p_window);
+
WindowData &wd = windows[p_window];
while (wd.transient_children.size()) {
@@ -837,8 +879,8 @@ void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclus
if (wd.exclusive != p_exclusive) {
wd.exclusive = p_exclusive;
if (wd.transient_parent != INVALID_WINDOW_ID) {
- WindowData &wd_parent = windows[wd.transient_parent];
if (wd.exclusive) {
+ WindowData &wd_parent = windows[wd.transient_parent];
SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);
} else {
SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);
@@ -1018,6 +1060,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre
r_style_ex = WS_EX_WINDOWEDGE;
if (p_main_window) {
r_style_ex |= WS_EX_APPWINDOW;
+ r_style |= WS_VISIBLE;
}
if (p_fullscreen || p_borderless) {
@@ -1036,13 +1079,15 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre
r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
}
}
- if (!p_borderless) {
- r_style |= WS_VISIBLE;
- }
if (p_no_activate_focus) {
r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;
}
+
+ if (!p_borderless && !p_no_activate_focus) {
+ r_style |= WS_VISIBLE;
+ }
+
r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
r_style_ex |= WS_EX_ACCEPTFILES;
}
@@ -1056,12 +1101,16 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
DWORD style = 0;
DWORD style_ex = 0;
- _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.no_focus, style, style_ex);
+ _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.no_focus || wd.is_popup, style, style_ex);
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
- SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | (wd.no_focus ? SWP_NOACTIVATE : 0));
+ if (icon.is_valid()) {
+ set_icon(icon);
+ }
+
+ SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));
if (p_repaint) {
RECT rect;
@@ -1212,6 +1261,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
wd.borderless = p_enabled;
_update_window_style(p_window);
_update_window_mouse_passthrough(p_window);
+ ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top");
@@ -1225,6 +1275,11 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
wd.no_focus = p_enabled;
_update_window_style(p_window);
} break;
+ case WINDOW_FLAG_POPUP: {
+ ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
+ ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
+ wd.is_popup = p_enabled;
+ } break;
case WINDOW_FLAG_MAX:
break;
}
@@ -1251,6 +1306,9 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
} break;
+ case WINDOW_FLAG_POPUP: {
+ return wd.is_popup;
+ } break;
case WINDOW_FLAG_MAX:
break;
}
@@ -1262,7 +1320,7 @@ void DisplayServerWindows::window_request_attention(WindowID p_window) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- WindowData &wd = windows[p_window];
+ const WindowData &wd = windows[p_window];
FLASHWINFO info;
info.cbSize = sizeof(FLASHWINFO);
@@ -1279,7 +1337,9 @@ void DisplayServerWindows::window_move_to_foreground(WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- SetForegroundWindow(wd.hWnd);
+ if (!wd.no_focus && !wd.is_popup) {
+ SetForegroundWindow(wd.hWnd);
+ }
}
bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
@@ -1326,8 +1386,9 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI
wd.im_position = p_pos;
HIMC himc = ImmGetContext(wd.hWnd);
- if (himc == (HIMC)0)
+ if (himc == (HIMC)0) {
return;
+ }
COMPOSITIONFORM cps;
cps.dwStyle = CFS_FORCE_POSITION;
@@ -1433,7 +1494,7 @@ void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTra
DeleteDC(hMainDC);
}
-void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
_THREAD_SAFE_METHOD_
if (p_cursor.is_valid()) {
@@ -1540,13 +1601,8 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh
}
}
- if (hAndMask != nullptr) {
- DeleteObject(hAndMask);
- }
-
- if (hXorMask != nullptr) {
- DeleteObject(hXorMask);
- }
+ DeleteObject(hAndMask);
+ DeleteObject(hXorMask);
memfree(buffer);
DeleteObject(bitmap);
@@ -1749,8 +1805,8 @@ void DisplayServerWindows::swap_buffers() {
void DisplayServerWindows::set_native_icon(const String &p_filename) {
_THREAD_SAFE_METHOD_
- FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
- ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'.");
+ Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file with icon '" + p_filename + "'.");
ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));
int pos = 0;
@@ -1836,7 +1892,6 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
err = GetLastError();
ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
- memdelete(f);
memdelete(icon_dir);
}
@@ -1844,9 +1899,11 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!p_icon.is_valid());
- Ref<Image> icon = p_icon->duplicate();
- if (icon->get_format() != Image::FORMAT_RGBA8) {
- icon->convert(Image::FORMAT_RGBA8);
+ if (icon != p_icon) {
+ icon = p_icon->duplicate();
+ if (icon->get_format() != Image::FORMAT_RGBA8) {
+ icon->convert(Image::FORMAT_RGBA8);
+ }
}
int w = icon->get_width();
int h = icon->get_height();
@@ -1895,16 +1952,14 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
_THREAD_SAFE_METHOD_
#if defined(VULKAN_ENABLED)
- // TODO disabling for now
- //context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
#endif
}
DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(VULKAN_ENABLED)
- //TODO disabling for now
- //return context_vulkan->get_vsync_mode(p_window);
+ return context_vulkan->get_vsync_mode(p_window);
#endif
return DisplayServer::VSYNC_ENABLED;
}
@@ -1974,7 +2029,7 @@ void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent
}
void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {
- ((DisplayServerWindows *)(get_singleton()))->_dispatch_input_event(p_event);
+ static_cast<DisplayServerWindows *>(get_singleton())->_dispatch_input_event(p_event);
}
void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {
@@ -1989,33 +2044,155 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
Variant ret;
Callable::CallError ce;
+ {
+ List<WindowID>::Element *E = popup_list.back();
+ if (E && Object::cast_to<InputEventKey>(*p_event)) {
+ // Redirect keyboard input to active popup.
+ if (windows.has(E->get())) {
+ Callable callable = windows[E->get()].input_event_callback;
+ if (callable.is_valid()) {
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ }
+ in_dispatch_input_event = false;
+ return;
+ }
+ }
+
Ref<InputEventFromWindow> event_from_window = p_event;
if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {
// Send to a single window.
- if (!windows.has(event_from_window->get_window_id())) {
- in_dispatch_input_event = false;
- ERR_FAIL_MSG("DisplayServerWindows: Invalid window id in input event.");
- }
- Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
- if (callable.is_null()) {
- in_dispatch_input_event = false;
- return;
+ if (windows.has(event_from_window->get_window_id())) {
+ Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
+ if (callable.is_valid()) {
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
}
- callable.call((const Variant **)&evp, 1, ret, ce);
} else {
// Send to all windows.
for (const KeyValue<WindowID, WindowData> &E : windows) {
const Callable callable = E.value.input_event_callback;
- if (callable.is_null()) {
- continue;
+ if (callable.is_valid()) {
+ callable.call((const Variant **)&evp, 1, ret, ce);
}
- callable.call((const Variant **)&evp, 1, ret, ce);
}
}
in_dispatch_input_event = false;
}
+LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {
+ DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());
+ if (ds_win) {
+ return ds_win->MouseProc(code, wParam, lParam);
+ } else {
+ return ::CallNextHookEx(nullptr, code, wParam, lParam);
+ }
+}
+
+DisplayServer::WindowID DisplayServerWindows::window_get_active_popup() const {
+ const List<WindowID>::Element *E = popup_list.back();
+ if (E) {
+ return E->get();
+ } else {
+ return INVALID_WINDOW_ID;
+ }
+}
+
+void DisplayServerWindows::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.parent_safe_rect = p_rect;
+}
+
+Rect2i DisplayServerWindows::window_get_popup_safe_rect(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());
+ const WindowData &wd = windows[p_window];
+ return wd.parent_safe_rect;
+}
+
+void DisplayServerWindows::popup_open(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ const WindowData &wd = windows[p_window];
+ if (wd.is_popup) {
+ // Find current popup parent, or root popup if new window is not transient.
+ List<WindowID>::Element *C = nullptr;
+ List<WindowID>::Element *E = popup_list.back();
+ while (E) {
+ if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) {
+ C = E;
+ E = E->prev();
+ } else {
+ break;
+ }
+ }
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ }
+
+ time_since_popup = OS::get_singleton()->get_ticks_msec();
+ popup_list.push_back(p_window);
+ }
+}
+
+void DisplayServerWindows::popup_close(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ List<WindowID>::Element *E = popup_list.find(p_window);
+ while (E) {
+ List<WindowID>::Element *F = E->next();
+ WindowID win_id = E->get();
+ popup_list.erase(E);
+
+ _send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ E = F;
+ }
+}
+
+LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) {
+ _THREAD_SAFE_METHOD_
+
+ uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;
+ if (delta > 250) {
+ switch (wParam) {
+ case WM_NCLBUTTONDOWN:
+ case WM_NCRBUTTONDOWN:
+ case WM_NCMBUTTONDOWN:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN: {
+ MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam;
+ Point2i pos = Point2i(ms->pt.x, ms->pt.y);
+ List<WindowID>::Element *C = nullptr;
+ List<WindowID>::Element *E = popup_list.back();
+ // Find top popup to close.
+ while (E) {
+ // Popup window area.
+ Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
+ // Area of the parent window, which responsible for opening sub-menu.
+ Rect2i safe_rect = window_get_popup_safe_rect(E->get());
+ if (win_rect.has_point(pos)) {
+ break;
+ } else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {
+ break;
+ } else {
+ C = E;
+ E = E->prev();
+ }
+ }
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ }
+ } break;
+ }
+ }
+ return ::CallNextHookEx(mouse_monitor, code, wParam, lParam);
+}
+
// Our default window procedure to handle processing of window-related system messages/events.
// Also known as DefProc or DefWindowProc.
// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures
@@ -2026,7 +2203,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} else {
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
- };
+ }
WindowID window_id = INVALID_WINDOW_ID;
bool window_created = false;
@@ -2048,6 +2225,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Process window messages.
switch (uMsg) {
+ case WM_MOUSEACTIVATE: {
+ if (windows[window_id].no_focus) {
+ return MA_NOACTIVATEANDEAT; // Do not activate, and discard mouse messages.
+ } else if (windows[window_id].is_popup) {
+ return MA_NOACTIVATE; // Do not activate, but process mouse messages.
+ }
+ } break;
case WM_SETFOCUS: {
windows[window_id].window_has_focus = true;
last_focused_window = window_id;
@@ -2131,8 +2315,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case SC_MONITORPOWER: // Monitor trying to enter powersave?
return 0; // Prevent from happening.
case SC_KEYMENU:
- if ((lParam >> 16) <= 0)
+ if ((lParam >> 16) <= 0) {
return 0;
+ }
}
} break;
case WM_CLOSE: // Did we receive a close message?
@@ -2164,8 +2349,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
return 0;
}
- if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize)
+ if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
+ }
RAWINPUT *raw = (RAWINPUT *)lpb;
@@ -2260,8 +2446,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
ScreenToClient(windows[window_id].hWnd, &coords);
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
- if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
+ if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) {
break;
+ }
Ref<InputEventMouseMotion> mm;
mm.instantiate();
@@ -2306,8 +2493,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
old_x = mm->get_position().x;
old_y = mm->get_position().y;
- if (windows[window_id].window_has_focus)
+ if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) {
Input::get_singleton()->parse_input_event(mm);
+ }
}
return 0;
}
@@ -2447,7 +2635,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
old_x = mm->get_position().x;
old_y = mm->get_position().y;
- if (windows[window_id].window_has_focus) {
+ if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) {
Input::get_singleton()->parse_input_event(mm);
}
@@ -2547,8 +2735,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
old_x = mm->get_position().x;
old_y = mm->get_position().y;
- if (windows[window_id].window_has_focus)
+ if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) {
Input::get_singleton()->parse_input_event(mm);
+ }
} break;
case WM_LBUTTONDOWN:
@@ -2694,8 +2883,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
if (mb->is_pressed()) {
- if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED)
+ if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED) {
SetCapture(hWnd);
+ }
} else {
if (--pressrc <= 0) {
if (mouse_mode != MOUSE_MODE_CAPTURED) {
@@ -2705,7 +2895,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
} else {
- // For reasons unknown to mankind, wheel comes in screen coordinates.
+ // For reasons unknown to humanity, wheel comes in screen coordinates.
POINT coords;
coords.x = mb->get_position().x;
coords.y = mb->get_position().y;
@@ -2770,13 +2960,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
window.width = window_client_rect.size.width;
window.height = window_client_rect.size.height;
-#if defined(VULKAN_ENABLED)
- if (context_vulkan && window_created) {
- context_vulkan->window_resize(window_id, window.width, window.height);
- }
-#endif
rect_changed = true;
}
+#if defined(VULKAN_ENABLED)
+ if (context_vulkan && window_created) {
+ // Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.
+ context_vulkan->window_resize(window_id, window.width, window.height);
+ }
+#endif
}
if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) {
@@ -2823,14 +3014,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_SYSKEYUP:
case WM_KEYUP:
case WM_KEYDOWN: {
- if (wParam == VK_SHIFT)
+ if (wParam == VK_SHIFT) {
shift_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
- if (wParam == VK_CONTROL)
+ }
+ if (wParam == VK_CONTROL) {
control_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
+ }
if (wParam == VK_MENU) {
alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
- if (lParam & (1 << 24))
+ if (lParam & (1 << 24)) {
gr_mem = alt_mem;
+ }
}
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -2839,10 +3033,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
}
}
- /*
- if (wParam==VK_WIN) TODO wtf is this?
- meta_mem=uMsg==WM_KEYDOWN;
- */
[[fallthrough]];
}
case WM_CHAR: {
@@ -2857,10 +3047,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
ke.uMsg = uMsg;
ke.window_id = window_id;
- if (ke.uMsg == WM_SYSKEYDOWN)
+ if (ke.uMsg == WM_SYSKEYDOWN) {
ke.uMsg = WM_KEYDOWN;
- if (ke.uMsg == WM_SYSKEYUP)
+ }
+ if (ke.uMsg == WM_SYSKEYUP) {
ke.uMsg = WM_KEYUP;
+ }
ke.wParam = wParam;
ke.lParam = lParam;
@@ -2888,7 +3080,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
_drag_event(window_id, touch_pos.x, touch_pos.y, ti.dwID);
} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
_touch_event(window_id, ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);
- };
+ }
}
bHandled = TRUE;
} else {
@@ -2901,7 +3093,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if (bHandled) {
CloseTouchInputHandle((HTOUCHINPUT)lParam);
return 0;
- };
+ }
} break;
case WM_DEVICECHANGE: {
@@ -2955,8 +3147,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
default: {
if (user_proc) {
return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
- };
- };
+ }
+ }
}
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
@@ -2964,10 +3156,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());
- if (ds_win)
+ if (ds_win) {
return ds_win->WndProc(hWnd, uMsg, wParam, lParam);
- else
+ } else {
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ }
}
void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam) {
@@ -3034,8 +3227,9 @@ void DisplayServerWindows::_process_key_events() {
k->set_ctrl_pressed(false);
}
- if (k->get_unicode() < 32)
+ if (k->get_unicode() < 32) {
k->set_unicode(0);
+ }
Input::get_singleton()->parse_input_event(k);
} else {
@@ -3090,8 +3284,9 @@ void DisplayServerWindows::_process_key_events() {
k->set_ctrl_pressed(false);
}
- if (k->get_unicode() < 32)
+ if (k->get_unicode() < 32) {
k->set_unicode(0);
+ }
k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));
@@ -3334,7 +3529,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
shift_mem = false;
control_mem = false;
meta_mem = false;
- hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance();
+ hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance();
pressrc = 0;
old_invalid = true;
@@ -3344,6 +3539,9 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
rendering_driver = p_rendering_driver;
+ // Init TTS
+ tts = memnew(TTS_Windows);
+
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
if (wintab_lib) {
@@ -3395,7 +3593,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
wc.cbWndExtra = 0;
wc.hInstance = hInstance ? hInstance : GetModuleHandle(nullptr);
wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO);
- wc.hCursor = nullptr; //LoadCursor(nullptr, IDC_ARROW);
+ wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = L"Engine";
@@ -3446,11 +3644,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
return;
}
- // gl_manager->set_use_vsync(current_videomode.use_vsync);
RasterizerGLES3::make_current();
}
#endif
+ mouse_monitor = SetWindowsHookEx(WH_MOUSE, ::MouseProc, nullptr, GetCurrentThreadId());
+
Point2i window_position(
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
@@ -3476,14 +3675,17 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
#endif
- //set_ime_active(false);
-
- if (!OS::get_singleton()->is_in_low_processor_usage_mode()) {
+ if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->is_in_low_processor_usage_mode()) {
+ // Increase priority for projects that are not in low-processor mode (typically games)
+ // to reduce the risk of frame stuttering.
+ // This is not done for the editor to prevent importers or resource bakers
+ // from making the system unresponsive.
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
DWORD index = 0;
HANDLE handle = AvSetMmThreadCharacteristics("Games", &index);
- if (handle)
+ if (handle) {
AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);
+ }
// This is needed to make sure that background work does not starve the main thread.
// This is only setting the priority of this thread, not the whole process.
@@ -3498,7 +3700,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
r_error = OK;
- ((OS_Windows *)OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
+ static_cast<OS_Windows *>(OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
}
@@ -3535,12 +3737,16 @@ DisplayServerWindows::~DisplayServerWindows() {
cursors_cache.clear();
+ if (mouse_monitor) {
+ UnhookWindowsHookEx(mouse_monitor);
+ }
+
if (user_proc) {
SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
- };
+ }
#ifdef GLES3_ENABLED
- // destroy windows .. NYI?
+ // destroy windows .. NYI?
#endif
if (windows.has(MAIN_WINDOW_ID)) {
@@ -3578,4 +3784,8 @@ DisplayServerWindows::~DisplayServerWindows() {
gl_manager = nullptr;
}
#endif
+ if (tts) {
+ memdelete(tts);
+ }
+ CoUninitialize();
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 7561f9bb77..febc8a2043 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -46,6 +46,7 @@
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "servers/rendering_server.h"
+#include "tts_windows.h"
#ifdef XAUDIO2_ENABLED
#include "drivers/xaudio2/audio_driver_xaudio2.h"
@@ -320,6 +321,8 @@ class DisplayServerWindows : public DisplayServer {
String rendering_driver;
bool app_focused = false;
+ TTS_Windows *tts = nullptr;
+
struct WindowData {
HWND hWnd;
//layered window
@@ -387,9 +390,16 @@ class DisplayServerWindows : public DisplayServer {
WindowID transient_parent = INVALID_WINDOW_ID;
Set<WindowID> transient_children;
+
+ bool is_popup = false;
+ Rect2i parent_safe_rect;
};
- JoypadWindows *joypad;
+ JoypadWindows *joypad = nullptr;
+ HHOOK mouse_monitor = nullptr;
+ List<WindowID> popup_list;
+ uint64_t time_since_popup = 0;
+ Ref<Image> icon;
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);
WindowID window_id_counter = MAIN_WINDOW_ID;
@@ -440,14 +450,27 @@ class DisplayServerWindows : public DisplayServer {
public:
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam);
+
+ void popup_open(WindowID p_window);
+ void popup_close(WindowID p_window);
virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
+ virtual bool tts_is_speaking() const override;
+ virtual bool tts_is_paused() const override;
+ virtual Array tts_get_voices() const override;
+
+ virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false) override;
+ virtual void tts_pause() override;
+ virtual void tts_resume() override;
+ virtual void tts_stop() override;
+
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
- virtual void mouse_warp_to_position(const Point2i &p_to) override;
+ virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
virtual MouseButton mouse_get_button_state() const override;
@@ -474,6 +497,10 @@ public:
virtual void show_window(WindowID p_window) override;
virtual void delete_sub_window(WindowID p_window) override;
+ virtual WindowID window_get_active_popup() const override;
+ virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) override;
+ virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const override;
+
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
@@ -535,7 +562,7 @@ public:
virtual void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const override;
- virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
+ virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
virtual bool get_swap_cancel_ok() override;
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp
index 17a24c08bf..0fa2913218 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -32,8 +32,6 @@
#include "export_plugin.h"
-static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size);
-
void register_windows_exporter() {
EDITOR_DEF("export/windows/rcedit", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
@@ -57,84 +55,7 @@ void register_windows_exporter() {
logo->create_from_image(img);
platform->set_logo(logo);
platform->set_name("Windows Desktop");
- platform->set_extension("exe");
- platform->set_release_32("windows_32_release.exe");
- platform->set_debug_32("windows_32_debug.exe");
- platform->set_release_64("windows_64_release.exe");
- platform->set_debug_64("windows_64_debug.exe");
platform->set_os_name("Windows");
- platform->set_fixup_embedded_pck_func(&fixup_embedded_pck);
EditorExport::get_singleton()->add_export_platform(platform);
}
-
-static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
- // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data
-
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
- if (!f) {
- return ERR_CANT_OPEN;
- }
-
- // Jump to the PE header and check the magic number
- {
- f->seek(0x3c);
- uint32_t pe_pos = f->get_32();
-
- f->seek(pe_pos);
- uint32_t magic = f->get_32();
- if (magic != 0x00004550) {
- f->close();
- return ERR_FILE_CORRUPT;
- }
- }
-
- // Process header
-
- int num_sections;
- {
- int64_t header_pos = f->get_position();
-
- f->seek(header_pos + 2);
- num_sections = f->get_16();
- f->seek(header_pos + 16);
- uint16_t opt_header_size = f->get_16();
-
- // Skip rest of header + optional header to go to the section headers
- f->seek(f->get_position() + 2 + opt_header_size);
- }
-
- // Search for the "pck" section
-
- int64_t section_table_pos = f->get_position();
-
- bool found = false;
- for (int i = 0; i < num_sections; ++i) {
- int64_t section_header_pos = section_table_pos + i * 40;
- f->seek(section_header_pos);
-
- uint8_t section_name[9];
- f->get_buffer(section_name, 8);
- section_name[8] = '\0';
-
- if (strcmp((char *)section_name, "pck") == 0) {
- // "pck" section found, let's patch!
-
- // Set virtual size to a little to avoid it taking memory (zero would give issues)
- f->seek(section_header_pos + 8);
- f->store_32(8);
-
- f->seek(section_header_pos + 16);
- f->store_32(p_embedded_size);
- f->seek(section_header_pos + 20);
- f->store_32(p_embedded_start);
-
- found = true;
- break;
- }
- }
-
- f->close();
-
- return found ? OK : ERR_FILE_CORRUPT;
-}
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 5ebc930735..7627a3cba3 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -41,22 +41,71 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres
}
}
+Error EditorExportPlatformWindows::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V(f.is_null(), ERR_CANT_CREATE);
+
+ f->store_line("@echo off");
+ f->store_line("title \"" + p_app_name + "\"");
+ f->store_line("\"%~dp0" + p_pkg_name + "\" \"%*\"");
+ f->store_line("pause > nul");
+
+ return OK;
+}
+
+Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+ if (p_preset->get("application/modify_resources")) {
+ return _rcedit_add_data(p_preset, p_path);
+ } else {
+ return OK;
+ }
+}
+
Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags);
+ String pck_path = p_path;
+ if (p_preset->get("binary_format/embed_pck")) {
+ pck_path = p_path.get_basename() + ".tmp";
+ }
+ Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags);
+ if (p_preset->get("codesign/enable") && err == OK) {
+ err = _code_sign(p_preset, pck_path);
+ }
- if (err != OK) {
- return err;
+ if (p_preset->get("binary_format/embed_pck") && err == OK) {
+ Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
+ err = tmp_dir->rename(pck_path, p_path);
}
- _rcedit_add_data(p_preset, p_path);
+ String app_name;
+ if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
+ app_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
+ } else {
+ app_name = "Unnamed";
+ }
+ app_name = OS::get_singleton()->get_safe_dir_name(app_name);
- if (p_preset->get("codesign/enable") && err == OK) {
- err = _code_sign(p_preset, p_path);
+ // Save console script.
+ if (err == OK) {
+ int con_scr = p_preset->get("debug/export_console_script");
+ if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
+ String scr_path = p_path.get_basename() + ".cmd";
+ err = _export_debug_script(p_preset, app_name, p_path.get_file(), scr_path);
+ }
}
return err;
}
+String EditorExportPlatformWindows::get_template_file_name(const String &p_target, const String &p_arch) const {
+ return "windows_" + p_arch + "_" + p_target + ".exe";
+}
+
+List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ List<String> list;
+ list.push_back("exe");
+ return list;
+}
+
bool EditorExportPlatformWindows::get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
// This option is not supported by "osslsigncode", used on non-Windows host.
if (!OS::get_singleton()->has_feature("windows") && p_option == "codesign/identity_type") {
@@ -78,6 +127,7 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/modify_resources"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
@@ -88,17 +138,16 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
}
-void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
- if (rcedit_path.is_empty()) {
- WARN_PRINT("The rcedit tool is not configured in the Editor Settings (Export > Windows > Rcedit). No custom icon or app information data will be embedded in the exported executable.");
- return;
+ if (rcedit_path != String() && !FileAccess::exists(rcedit_path)) {
+ ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", aborting.");
+ return ERR_FILE_NOT_FOUND;
}
- if (!FileAccess::exists(rcedit_path)) {
- ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included.");
- return;
+ if (rcedit_path == String()) {
+ rcedit_path = "rcedit"; // try to run rcedit from PATH
}
#ifndef WINDOWS_ENABLED
@@ -106,8 +155,8 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset>
String wine_path = EditorSettings::get_singleton()->get("export/windows/wine");
if (!wine_path.is_empty() && !FileAccess::exists(wine_path)) {
- ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included.");
- return;
+ ERR_PRINT("Could not find wine executable at " + wine_path + ", aborting.");
+ return ERR_FILE_NOT_FOUND;
}
if (wine_path.is_empty()) {
@@ -165,13 +214,22 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset>
args.push_back(trademarks);
}
-#ifdef WINDOWS_ENABLED
- OS::get_singleton()->execute(rcedit_path, args);
-#else
+#ifndef WINDOWS_ENABLED
// On non-Windows we need WINE to run rcedit
args.push_front(rcedit_path);
- OS::get_singleton()->execute(wine_path, args);
+ rcedit_path = wine_path;
#endif
+
+ String str;
+ Error err = OS::get_singleton()->execute(rcedit_path, args, &str, nullptr, true);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not start rcedit executable, configure rcedit path in the Editor Settings (Export > Windows > Rcedit).");
+ print_line("rcedit (" + p_path + "): " + str);
+
+ if (str.find("Fatal error") != -1) {
+ return FAILED;
+ }
+
+ return OK;
}
Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
@@ -308,7 +366,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
String str;
Error err = OS::get_singleton()->execute(signtool_path, args, &str, nullptr, true);
- ERR_FAIL_COND_V(err != OK, err);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not start signtool executable, configure signtool path in the Editor Settings (Export > Windows > Signtool).");
print_line("codesign (" + p_path + "): " + str);
#ifndef WINDOWS_ENABLED
@@ -320,7 +378,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
}
#ifndef WINDOWS_ENABLED
- DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
+ Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
err = tmp_dir->remove(p_path);
ERR_FAIL_COND_V(err != OK, err);
@@ -337,7 +395,7 @@ bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_pr
bool valid = EditorExportPlatformPC::can_export(p_preset, err, r_missing_templates);
String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
- if (rcedit_path.is_empty()) {
+ if (p_preset->get("application/modify_resources") && rcedit_path.is_empty()) {
err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > Rcedit) to change the icon or app information data.") + "\n";
}
@@ -374,3 +432,75 @@ bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_pr
return valid;
}
+
+Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const {
+ // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data
+
+ if (p_embedded_size + p_embedded_start >= 0x100000000) { // Check for total executable size
+ ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Windows executables cannot be >= 4 GiB.");
+ }
+
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ_WRITE);
+ if (f.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+
+ // Jump to the PE header and check the magic number
+ {
+ f->seek(0x3c);
+ uint32_t pe_pos = f->get_32();
+
+ f->seek(pe_pos);
+ uint32_t magic = f->get_32();
+ if (magic != 0x00004550) {
+ return ERR_FILE_CORRUPT;
+ }
+ }
+
+ // Process header
+
+ int num_sections;
+ {
+ int64_t header_pos = f->get_position();
+
+ f->seek(header_pos + 2);
+ num_sections = f->get_16();
+ f->seek(header_pos + 16);
+ uint16_t opt_header_size = f->get_16();
+
+ // Skip rest of header + optional header to go to the section headers
+ f->seek(f->get_position() + 2 + opt_header_size);
+ }
+
+ // Search for the "pck" section
+
+ int64_t section_table_pos = f->get_position();
+
+ bool found = false;
+ for (int i = 0; i < num_sections; ++i) {
+ int64_t section_header_pos = section_table_pos + i * 40;
+ f->seek(section_header_pos);
+
+ uint8_t section_name[9];
+ f->get_buffer(section_name, 8);
+ section_name[8] = '\0';
+
+ if (strcmp((char *)section_name, "pck") == 0) {
+ // "pck" section found, let's patch!
+
+ // Set virtual size to a little to avoid it taking memory (zero would give issues)
+ f->seek(section_header_pos + 8);
+ f->store_32(8);
+
+ f->seek(section_header_pos + 16);
+ f->store_32(p_embedded_size);
+ f->seek(section_header_pos + 20);
+ f->store_32(p_embedded_start);
+
+ found = true;
+ break;
+ }
+ }
+
+ return found ? OK : ERR_FILE_CORRUPT;
+}
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index 86e9d49b05..b48ee7c985 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -38,15 +38,20 @@
#include "platform/windows/logo.gen.h"
class EditorExportPlatformWindows : public EditorExportPlatformPC {
- void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path);
+ Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
+ Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
public:
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) override;
virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) override;
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual void get_export_options(List<ExportOption> *r_options) override;
virtual bool get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
+ virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const override;
};
#endif
diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp
index 74b5f48502..a97fa99d7f 100644
--- a/platform/windows/gl_manager_windows.cpp
+++ b/platform/windows/gl_manager_windows.cpp
@@ -56,14 +56,9 @@ typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int
int GLManager_Windows::_find_or_create_display(GLWindow &win) {
// find display NYI, only 1 supported so far
- if (_displays.size())
+ if (_displays.size()) {
return 0;
-
- // for (unsigned int n = 0; n < _displays.size(); n++) {
- // const GLDisplay &d = _displays[n];
- // if (d.x11_display == p_x11_display)
- // return n;
- // }
+ }
// create
GLDisplay d_temp = {};
@@ -230,23 +225,27 @@ void GLManager_Windows::window_destroy(DisplayServer::WindowID p_window_id) {
}
void GLManager_Windows::release_current() {
- if (!_current_window)
+ if (!_current_window) {
return;
+ }
wglMakeCurrent(_current_window->hDC, nullptr);
}
void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) {
- if (p_window_id == -1)
+ if (p_window_id == -1) {
return;
+ }
GLWindow &win = _windows[p_window_id];
- if (!win.in_use)
+ if (!win.in_use) {
return;
+ }
// noop
- if (&win == _current_window)
+ if (&win == _current_window) {
return;
+ }
const GLDisplay &disp = get_display(win.gldisplay_id);
wglMakeCurrent(win.hDC, disp.hRC);
@@ -255,8 +254,9 @@ void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id)
}
void GLManager_Windows::make_current() {
- if (!_current_window)
+ if (!_current_window) {
return;
+ }
if (!_current_window->in_use) {
WARN_PRINT("current window not in use!");
return;
@@ -269,8 +269,9 @@ void GLManager_Windows::swap_buffers() {
// NO NEED TO CALL SWAP BUFFERS for each window...
// see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
- if (!_current_window)
+ if (!_current_window) {
return;
+ }
if (!_current_window->in_use) {
WARN_PRINT("current window not in use!");
return;
@@ -304,12 +305,15 @@ void GLManager_Windows::set_use_vsync(bool p_use) {
if (!setup) {
setup = true;
String extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display));
- if (extensions.find("GLX_EXT_swap_control") != -1)
+ if (extensions.find("GLX_EXT_swap_control") != -1) {
glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT");
- if (extensions.find("GLX_MESA_swap_control") != -1)
+ }
+ if (extensions.find("GLX_MESA_swap_control") != -1) {
glXSwapIntervalMESA = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalMESA");
- if (extensions.find("GLX_SGI_swap_control") != -1)
+ }
+ if (extensions.find("GLX_SGI_swap_control") != -1) {
glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI");
+ }
}
int val = p_use ? 1 : 0;
if (glXSwapIntervalMESA) {
@@ -319,8 +323,9 @@ void GLManager_Windows::set_use_vsync(bool p_use) {
} else if (glXSwapIntervalEXT) {
GLXDrawable drawable = glXGetCurrentDrawable();
glXSwapIntervalEXT(x11_display, drawable, val);
- } else
+ } else {
return;
+ }
use_vsync = p_use;
*/
}
diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows.h
index 6423c54855..dc411983e8 100644
--- a/platform/windows/gl_manager_windows.h
+++ b/platform/windows/gl_manager_windows.h
@@ -52,19 +52,18 @@ public:
private:
// any data specific to the window
struct GLWindow {
- GLWindow() { in_use = false; }
- bool in_use;
+ bool in_use = false;
// the external ID .. should match the GL window number .. unused I think
- DisplayServer::WindowID window_id;
- int width;
- int height;
+ DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID;
+ int width = 0;
+ int height = 0;
// windows specific
HDC hDC;
HWND hwnd;
- int gldisplay_id;
+ int gldisplay_id = 0;
};
struct GLDisplay {
@@ -75,7 +74,7 @@ private:
LocalVector<GLWindow> _windows;
LocalVector<GLDisplay> _displays;
- GLWindow *_current_window;
+ GLWindow *_current_window = nullptr;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
diff --git a/platform/windows/godot.ico b/platform/windows/godot.ico
index dd611e07da..25830ffdc6 100644
--- a/platform/windows/godot.ico
+++ b/platform/windows/godot.ico
Binary files differ
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index 618d5670d2..8de3ef294a 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -39,7 +39,18 @@
#ifndef TOOLS_ENABLED
#if defined _MSC_VER
#pragma section("pck", read)
-__declspec(allocate("pck")) static const char dummy[8] = { 0 };
+__declspec(allocate("pck")) static char dummy[8] = { 0 };
+
+// Dummy function to prevent LTO from discarding "pck" section.
+extern "C" char *__cdecl pck_section_dummy_call() {
+ return &dummy[0];
+};
+#if defined _AMD64_
+#pragma comment(linker, "/include:pck_section_dummy_call")
+#elif defined _X86_
+#pragma comment(linker, "/include:_pck_section_dummy_call")
+#endif
+
#elif defined __GNUC__
static const char dummy[8] __attribute__((section("pck"), used)) = { 0 };
#endif
@@ -140,11 +151,6 @@ int widechar_main(int argc, wchar_t **argv) {
setlocale(LC_CTYPE, "");
-#ifndef TOOLS_ENABLED
- // Workaround to prevent LTCG (MSVC LTO) from removing "pck" section
- const char *dummy_guard = dummy;
-#endif
-
char **argv_utf8 = new char *[argc];
for (int i = 0; i < argc; ++i) {
@@ -163,8 +169,9 @@ int widechar_main(int argc, wchar_t **argv) {
return 255;
}
- if (Main::start())
+ if (Main::start()) {
os.run();
+ }
Main::cleanup();
for (int i = 0; i < argc; ++i) {
@@ -173,7 +180,7 @@ int widechar_main(int argc, wchar_t **argv) {
delete[] argv_utf8;
return os.get_exit_code();
-};
+}
int _main() {
LPWSTR *wc_argv;
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index b0dd86a4b7..d039fd13a7 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -60,8 +60,9 @@ JoypadWindows::JoypadWindows(HWND *hwnd) {
load_xinput();
- for (int i = 0; i < JOYPADS_MAX; i++)
+ for (int i = 0; i < JOYPADS_MAX; i++) {
attached_joypads[i] = false;
+ }
HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
if (result == DI_OK) {
@@ -144,8 +145,9 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
HRESULT hr;
int num = input->get_unused_joy_id();
- if (have_device(instance->guidInstance) || num == -1)
+ if (have_device(instance->guidInstance) || num == -1) {
return false;
+ }
d_joypads[num] = dinput_gamepad();
dinput_gamepad *joy = &d_joypads[num];
@@ -196,27 +198,28 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_
DIPROPRANGE prop_range;
DIPROPDWORD dilong;
LONG ofs;
- if (ob->guidType == GUID_XAxis)
+ if (ob->guidType == GUID_XAxis) {
ofs = DIJOFS_X;
- else if (ob->guidType == GUID_YAxis)
+ } else if (ob->guidType == GUID_YAxis) {
ofs = DIJOFS_Y;
- else if (ob->guidType == GUID_ZAxis)
+ } else if (ob->guidType == GUID_ZAxis) {
ofs = DIJOFS_Z;
- else if (ob->guidType == GUID_RxAxis)
+ } else if (ob->guidType == GUID_RxAxis) {
ofs = DIJOFS_RX;
- else if (ob->guidType == GUID_RyAxis)
+ } else if (ob->guidType == GUID_RyAxis) {
ofs = DIJOFS_RY;
- else if (ob->guidType == GUID_RzAxis)
+ } else if (ob->guidType == GUID_RzAxis) {
ofs = DIJOFS_RZ;
- else if (ob->guidType == GUID_Slider) {
+ } else if (ob->guidType == GUID_Slider) {
if (slider_count < 2) {
ofs = DIJOFS_SLIDER(slider_count);
slider_count++;
} else {
return;
}
- } else
+ } else {
return;
+ }
prop_range.diph.dwSize = sizeof(DIPROPRANGE);
prop_range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
prop_range.diph.dwObj = ob->dwType;
@@ -227,8 +230,9 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_
dinput_gamepad &joy = d_joypads[p_joy_id];
res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_RANGE, &prop_range.diph);
- if (FAILED(res))
+ if (FAILED(res)) {
return;
+ }
dilong.diph.dwSize = sizeof(dilong);
dilong.diph.dwHeaderSize = sizeof(dilong.diph);
@@ -237,15 +241,16 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_
dilong.dwData = 0;
res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_DEADZONE, &dilong.diph);
- if (FAILED(res))
+ if (FAILED(res)) {
return;
+ }
joy.joy_axis.push_back(ofs);
}
}
BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context) {
- JoypadWindows *self = (JoypadWindows *)p_context;
+ JoypadWindows *self = static_cast<JoypadWindows *>(p_context);
if (self->is_xinput_device(&p_instance->guidProduct)) {
return DIENUM_CONTINUE;
}
@@ -253,9 +258,9 @@ BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, vo
return DIENUM_CONTINUE;
}
-BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context) {
- JoypadWindows *self = (JoypadWindows *)context;
- self->setup_joypad_object(instance, self->id_to_change);
+BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *p_instance, void *p_context) {
+ JoypadWindows *self = static_cast<JoypadWindows *>(p_context);
+ self->setup_joypad_object(p_instance, self->id_to_change);
return DIENUM_CONTINUE;
}
@@ -268,8 +273,9 @@ void JoypadWindows::close_joypad(int id) {
return;
}
- if (!d_joypads[id].attached)
+ if (!d_joypads[id].attached) {
return;
+ }
d_joypads[id].di_joy->Unacquire();
d_joypads[id].di_joy->Release();
@@ -355,16 +361,18 @@ void JoypadWindows::process_joypads() {
}
} else if (joy.vibrating && joy.ff_end_timestamp != 0) {
uint64_t current_time = OS::get_singleton()->get_ticks_usec();
- if (current_time >= joy.ff_end_timestamp)
+ if (current_time >= joy.ff_end_timestamp) {
joypad_vibration_stop_xinput(i, current_time);
+ }
}
}
for (int i = 0; i < JOYPADS_MAX; i++) {
dinput_gamepad *joy = &d_joypads[i];
- if (!joy->attached)
+ if (!joy->attached) {
continue;
+ }
DIJOYSTATE2 js;
hr = joy->di_joy->Poll();
@@ -396,7 +404,7 @@ void JoypadWindows::process_joypads() {
// on mingw, these constants are not constants
int count = 8;
- LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) };
+ const LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) };
int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz, js.rglSlider[0], js.rglSlider[1] };
for (int j = 0; j < joy->joy_axis.size(); j++) {
@@ -404,9 +412,9 @@ void JoypadWindows::process_joypads() {
if (joy->joy_axis[j] == axes[k]) {
input->joy_axis(joy->id, (JoyAxis)j, axis_correct(values[k]));
break;
- };
- };
- };
+ }
+ }
+ }
}
return;
}
@@ -446,7 +454,7 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
dpad_val = (HatMask)(HatMask::LEFT | HatMask::UP);
}
input->joy_hat(p_device, dpad_val);
-};
+}
float JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
if (Math::abs(p_val) < MIN_JOY_AXIS) {
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index 0e3d03fa52..d239471a5c 100644
--- a/platform/windows/joypad_windows.h
+++ b/platform/windows/joypad_windows.h
@@ -86,8 +86,9 @@ private:
attached = false;
confirmed = false;
- for (int i = 0; i < MAX_JOY_BUTTONS; i++)
+ for (int i = 0; i < MAX_JOY_BUTTONS; i++) {
last_buttons[i] = false;
+ }
}
};
@@ -104,10 +105,10 @@ private:
typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState);
typedef DWORD(WINAPI *XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
- HWND *hWnd;
+ HWND *hWnd = nullptr;
HANDLE xinput_dll;
LPDIRECTINPUT8 dinput;
- Input *input;
+ Input *input = nullptr;
int id_to_change;
int slider_count;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index d844531071..d43ab47004 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -46,6 +46,7 @@
#include "windows_terminal_logger.h"
#include <avrt.h>
+#include <bcrypt.h>
#include <direct.h>
#include <knownfolders.h>
#include <process.h>
@@ -106,8 +107,9 @@ void RedirectIOToConsole() {
}
BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
- if (!EngineDebugger::is_active())
+ if (!EngineDebugger::is_active()) {
return FALSE;
+ }
switch (dwCtrlType) {
case CTRL_C_EVENT:
@@ -165,8 +167,9 @@ void OS_Windows::initialize() {
}
void OS_Windows::delete_main_loop() {
- if (main_loop)
+ if (main_loop) {
memdelete(main_loop);
+ }
main_loop = nullptr;
}
@@ -179,8 +182,9 @@ void OS_Windows::finalize() {
driver_midi.close();
#endif
- if (main_loop)
+ if (main_loop) {
memdelete(main_loop);
+ }
main_loop = nullptr;
}
@@ -192,7 +196,13 @@ void OS_Windows::finalize_core() {
NetSocketPosix::cleanup();
}
-Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) {
+ NTSTATUS status = BCryptGenRandom(nullptr, r_buffer, p_bytes, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
+ ERR_FAIL_COND_V(status, FAILED);
+ return OK;
+}
+
+Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) {
String path = p_path.replace("/", "\\");
if (!FileAccess::exists(path)) {
@@ -220,6 +230,10 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han
remove_dll_directory(cookie);
}
+ if (r_resolved_path != nullptr) {
+ *r_resolved_path = path;
+ }
+
return OK;
}
@@ -254,12 +268,19 @@ OS::Date OS_Windows::get_date(bool p_utc) const {
GetLocalTime(&systemtime);
}
+ //Get DST information from Windows, but only if p_utc is false.
+ TIME_ZONE_INFORMATION info;
+ bool daylight = false;
+ if (!p_utc && GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) {
+ daylight = true;
+ }
+
Date date;
date.day = systemtime.wDay;
date.month = Month(systemtime.wMonth);
date.weekday = Weekday(systemtime.wDayOfWeek);
date.year = systemtime.wYear;
- date.dst = false;
+ date.dst = daylight;
return date;
}
@@ -281,19 +302,23 @@ OS::Time OS_Windows::get_time(bool p_utc) const {
OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
TIME_ZONE_INFORMATION info;
bool daylight = false;
- if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT)
+ if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) {
daylight = true;
+ }
+ // Daylight Bias needs to be added to the bias if DST is in effect, or else it will not properly update.
TimeZoneInfo ret;
if (daylight) {
ret.name = info.DaylightName;
+ ret.bias = info.Bias + info.DaylightBias;
} else {
ret.name = info.StandardName;
+ ret.bias = info.Bias + info.StandardBias;
}
// Bias value returned by GetTimeZoneInformation is inverted of what we expect
// For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180
- ret.bias = -info.Bias;
+ ret.bias = -ret.bias;
return ret;
}
@@ -315,10 +340,11 @@ double OS_Windows::get_unix_time() const {
}
void OS_Windows::delay_usec(uint32_t p_usec) const {
- if (p_usec < 1000)
+ if (p_usec < 1000) {
Sleep(1);
- else
+ } else {
Sleep(p_usec / 1000);
+ }
}
uint64_t OS_Windows::get_ticks_usec() const {
@@ -423,7 +449,7 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
if (p_pipe_mutex) {
p_pipe_mutex->unlock();
}
- };
+ }
CloseHandle(pipe[0]); // Close pipe read handle.
} else {
WaitForSingleObject(pi.pi.hProcess, INFINITE);
@@ -439,7 +465,7 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
CloseHandle(pi.pi.hThread);
return OK;
-};
+}
Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
String path = p_path.replace("/", "\\");
@@ -471,7 +497,7 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg
process_map->insert(pid, pi);
return OK;
-};
+}
Error OS_Windows::kill(const ProcessID &p_pid) {
ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED);
@@ -485,15 +511,35 @@ Error OS_Windows::kill(const ProcessID &p_pid) {
CloseHandle(pi.hThread);
return ret != 0 ? OK : FAILED;
-};
+}
int OS_Windows::get_process_id() const {
return _getpid();
}
+bool OS_Windows::is_process_running(const ProcessID &p_pid) const {
+ if (!process_map->has(p_pid)) {
+ return false;
+ }
+
+ const PROCESS_INFORMATION &pi = (*process_map)[p_pid].pi;
+
+ DWORD dw_exit_code = 0;
+ if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) {
+ return false;
+ }
+
+ if (dw_exit_code != STILL_ACTIVE) {
+ return false;
+ }
+
+ return true;
+}
+
Error OS_Windows::set_cwd(const String &p_cwd) {
- if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0)
+ if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) {
return ERR_CANT_OPEN;
+ }
return OK;
}
@@ -516,7 +562,7 @@ bool OS_Windows::has_environment(const String &p_var) const {
free(env);
return has_env;
#endif
-};
+}
String OS_Windows::get_environment(const String &p_var) const {
WCHAR wval[0x7fff]; // MSDN says 32767 char is the maximum
@@ -535,7 +581,7 @@ String OS_Windows::get_stdin_string(bool p_block) {
if (p_block) {
char buff[1024];
return fgets(buff, 1024, stdin);
- };
+ }
return String();
}
@@ -573,17 +619,20 @@ String OS_Windows::get_locale() const {
int sublang = SUBLANGID(langid);
while (wl->locale) {
- if (wl->main_lang == lang && wl->sublang == SUBLANG_NEUTRAL)
+ if (wl->main_lang == lang && wl->sublang == SUBLANG_NEUTRAL) {
neutral = wl->locale;
+ }
- if (lang == wl->main_lang && sublang == wl->sublang)
+ if (lang == wl->main_lang && sublang == wl->sublang) {
return String(wl->locale).replace("-", "_");
+ }
wl++;
}
- if (!neutral.is_empty())
+ if (!neutral.is_empty()) {
return String(neutral).replace("-", "_");
+ }
return "en";
}
@@ -610,25 +659,48 @@ BOOL is_wow64() {
int OS_Windows::get_processor_count() const {
SYSTEM_INFO sysinfo;
- if (is_wow64())
+ if (is_wow64()) {
GetNativeSystemInfo(&sysinfo);
- else
+ } else {
GetSystemInfo(&sysinfo);
+ }
return sysinfo.dwNumberOfProcessors;
}
+String OS_Windows::get_processor_name() const {
+ const String id = "Hardware\\Description\\System\\CentralProcessor\\0";
+
+ HKEY hkey;
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(id.utf16().get_data()), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) {
+ ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
+ }
+
+ WCHAR buffer[256];
+ DWORD buffer_len = 256;
+ DWORD vtype = REG_SZ;
+ if (RegQueryValueExW(hkey, L"ProcessorNameString", NULL, &vtype, (LPBYTE)buffer, &buffer_len) == ERROR_SUCCESS) {
+ RegCloseKey(hkey);
+ return String::utf16((const char16_t *)buffer, buffer_len).strip_edges();
+ } else {
+ RegCloseKey(hkey);
+ ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
+ }
+}
+
void OS_Windows::run() {
- if (!main_loop)
+ if (!main_loop) {
return;
+ }
main_loop->initialize();
while (!force_quit) {
DisplayServer::get_singleton()->process_events(); // get rid of pending events
- if (Main::iteration())
+ if (Main::iteration()) {
break;
- };
+ }
+ }
main_loop->finalize();
}
@@ -637,6 +709,58 @@ MainLoop *OS_Windows::get_main_loop() const {
return main_loop;
}
+uint64_t OS_Windows::get_embedded_pck_offset() const {
+ Ref<FileAccess> f = FileAccess::open(get_executable_path(), FileAccess::READ);
+ if (f.is_null()) {
+ return 0;
+ }
+
+ // Process header.
+ {
+ f->seek(0x3c);
+ uint32_t pe_pos = f->get_32();
+
+ f->seek(pe_pos);
+ uint32_t magic = f->get_32();
+ if (magic != 0x00004550) {
+ return 0;
+ }
+ }
+
+ int num_sections;
+ {
+ int64_t header_pos = f->get_position();
+
+ f->seek(header_pos + 2);
+ num_sections = f->get_16();
+ f->seek(header_pos + 16);
+ uint16_t opt_header_size = f->get_16();
+
+ // Skip rest of header + optional header to go to the section headers.
+ f->seek(f->get_position() + 2 + opt_header_size);
+ }
+ int64_t section_table_pos = f->get_position();
+
+ // Search for the "pck" section.
+ int64_t off = 0;
+ for (int i = 0; i < num_sections; ++i) {
+ int64_t section_header_pos = section_table_pos + i * 40;
+ f->seek(section_header_pos);
+
+ uint8_t section_name[9];
+ f->get_buffer(section_name, 8);
+ section_name[8] = '\0';
+
+ if (strcmp((char *)section_name, "pck") == 0) {
+ f->seek(section_header_pos + 20);
+ off = f->get_32();
+ break;
+ }
+ }
+
+ return off;
+}
+
String OS_Windows::get_config_path() const {
// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if (has_environment("XDG_CONFIG_HOME")) {
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 0a11416b1b..378438a075 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -60,14 +60,14 @@
class JoypadWindows;
class OS_Windows : public OS {
#ifdef STDOUT_FILE
- FILE *stdo;
+ FILE *stdo = nullptr;
#endif
uint64_t ticks_start;
uint64_t ticks_per_second;
HINSTANCE hInstance;
- MainLoop *main_loop;
+ MainLoop *main_loop = nullptr;
#ifdef WASAPI_ENABLED
AudioDriverWASAPI driver_wasapi;
@@ -106,7 +106,9 @@ protected:
public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
- virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
+ virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override;
+
+ virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override;
virtual Error close_dynamic_library(void *p_library_handle) override;
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override;
@@ -130,6 +132,7 @@ public:
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
+ virtual bool is_process_running(const ProcessID &p_pid) const override;
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
@@ -140,6 +143,9 @@ public:
virtual String get_locale() const override;
virtual int get_processor_count() const override;
+ virtual String get_processor_name() const override;
+
+ virtual uint64_t get_embedded_pck_offset() const override;
virtual String get_config_path() const override;
virtual String get_data_path() const override;
diff --git a/platform/windows/tts_windows.cpp b/platform/windows/tts_windows.cpp
new file mode 100644
index 0000000000..05249934ba
--- /dev/null
+++ b/platform/windows/tts_windows.cpp
@@ -0,0 +1,269 @@
+/*************************************************************************/
+/* tts_windows.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "tts_windows.h"
+
+TTS_Windows *TTS_Windows::singleton = nullptr;
+
+void __stdcall TTS_Windows::speech_event_callback(WPARAM wParam, LPARAM lParam) {
+ TTS_Windows *tts = TTS_Windows::get_singleton();
+ SPEVENT event;
+ while (tts->synth->GetEvents(1, &event, NULL) == S_OK) {
+ if (tts->ids.has(event.ulStreamNum)) {
+ if (event.eEventId == SPEI_START_INPUT_STREAM) {
+ DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_STARTED, tts->ids[event.ulStreamNum].id);
+ } else if (event.eEventId == SPEI_END_INPUT_STREAM) {
+ DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_ENDED, tts->ids[event.ulStreamNum].id);
+ tts->ids.erase(event.ulStreamNum);
+ tts->_update_tts();
+ } else if (event.eEventId == SPEI_WORD_BOUNDARY) {
+ const Char16String &string = tts->ids[event.ulStreamNum].string;
+ int pos = 0;
+ for (int i = 0; i < MIN(event.lParam, string.length()); i++) {
+ char16_t c = string[i];
+ if ((c & 0xfffffc00) == 0xd800) {
+ i++;
+ }
+ pos++;
+ }
+ DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_BOUNDARY, tts->ids[event.ulStreamNum].id, pos - tts->ids[event.ulStreamNum].offset);
+ }
+ }
+ }
+}
+
+void TTS_Windows::_update_tts() {
+ if (!is_speaking() && !paused && queue.size() > 0) {
+ DisplayServer::TTSUtterance &message = queue.front()->get();
+
+ String text;
+ DWORD flags = SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_IS_XML;
+ String pitch_tag = String("<pitch absmiddle=\"") + String::num_int64(message.pitch * 10 - 10, 10) + String("\">");
+ text = pitch_tag + message.text + String("</pitch>");
+
+ IEnumSpObjectTokens *cpEnum;
+ ISpObjectToken *cpVoiceToken;
+ ULONG ulCount = 0;
+ ULONG stream_number = 0;
+ ISpObjectTokenCategory *cpCategory;
+ HRESULT hr = CoCreateInstance(CLSID_SpObjectTokenCategory, nullptr, CLSCTX_INPROC_SERVER, IID_ISpObjectTokenCategory, (void **)&cpCategory);
+ if (SUCCEEDED(hr)) {
+ hr = cpCategory->SetId(SPCAT_VOICES, false);
+ if (SUCCEEDED(hr)) {
+ hr = cpCategory->EnumTokens(nullptr, nullptr, &cpEnum);
+ if (SUCCEEDED(hr)) {
+ hr = cpEnum->GetCount(&ulCount);
+ while (SUCCEEDED(hr) && ulCount--) {
+ wchar_t *w_id = 0L;
+ hr = cpEnum->Next(1, &cpVoiceToken, nullptr);
+ cpVoiceToken->GetId(&w_id);
+ if (String::utf16((const char16_t *)w_id) == message.voice) {
+ synth->SetVoice(cpVoiceToken);
+ cpVoiceToken->Release();
+ break;
+ }
+ cpVoiceToken->Release();
+ }
+ cpEnum->Release();
+ }
+ }
+ cpCategory->Release();
+ }
+
+ UTData ut;
+ ut.string = text.utf16();
+ ut.offset = pitch_tag.length(); // Substract injected <pitch> tag offset.
+ ut.id = message.id;
+
+ synth->SetVolume(message.volume);
+ synth->SetRate(10.f * log10(message.rate) / log10(3.f));
+ synth->Speak((LPCWSTR)ut.string.get_data(), flags, &stream_number);
+
+ ids[stream_number] = ut;
+
+ queue.pop_front();
+ }
+}
+
+bool TTS_Windows::is_speaking() const {
+ ERR_FAIL_COND_V(!synth, false);
+
+ SPVOICESTATUS status;
+ synth->GetStatus(&status, nullptr);
+ return (status.dwRunningState == SPRS_IS_SPEAKING);
+}
+
+bool TTS_Windows::is_paused() const {
+ ERR_FAIL_COND_V(!synth, false);
+ return paused;
+}
+
+Array TTS_Windows::get_voices() const {
+ Array list;
+ IEnumSpObjectTokens *cpEnum;
+ ISpObjectToken *cpVoiceToken;
+ ISpDataKey *cpDataKeyAttribs;
+ ULONG ulCount = 0;
+ ISpObjectTokenCategory *cpCategory;
+ HRESULT hr = CoCreateInstance(CLSID_SpObjectTokenCategory, nullptr, CLSCTX_INPROC_SERVER, IID_ISpObjectTokenCategory, (void **)&cpCategory);
+ if (SUCCEEDED(hr)) {
+ hr = cpCategory->SetId(SPCAT_VOICES, false);
+ if (SUCCEEDED(hr)) {
+ hr = cpCategory->EnumTokens(nullptr, nullptr, &cpEnum);
+ if (SUCCEEDED(hr)) {
+ hr = cpEnum->GetCount(&ulCount);
+ while (SUCCEEDED(hr) && ulCount--) {
+ hr = cpEnum->Next(1, &cpVoiceToken, nullptr);
+ HRESULT hr_attr = cpVoiceToken->OpenKey(SPTOKENKEY_ATTRIBUTES, &cpDataKeyAttribs);
+ if (SUCCEEDED(hr_attr)) {
+ wchar_t *w_id = nullptr;
+ wchar_t *w_lang = nullptr;
+ wchar_t *w_name = nullptr;
+ cpVoiceToken->GetId(&w_id);
+ cpDataKeyAttribs->GetStringValue(L"Language", &w_lang);
+ cpDataKeyAttribs->GetStringValue(nullptr, &w_name);
+ LCID locale = wcstol(w_lang, nullptr, 16);
+
+ int locale_chars = GetLocaleInfoW(locale, LOCALE_SISO639LANGNAME, nullptr, 0);
+ int region_chars = GetLocaleInfoW(locale, LOCALE_SISO3166CTRYNAME, nullptr, 0);
+ wchar_t *w_lang_code = new wchar_t[locale_chars];
+ wchar_t *w_reg_code = new wchar_t[region_chars];
+ GetLocaleInfoW(locale, LOCALE_SISO639LANGNAME, w_lang_code, locale_chars);
+ GetLocaleInfoW(locale, LOCALE_SISO3166CTRYNAME, w_reg_code, region_chars);
+
+ Dictionary voice_d;
+ voice_d["id"] = String::utf16((const char16_t *)w_id);
+ if (w_name) {
+ voice_d["name"] = String::utf16((const char16_t *)w_name);
+ } else {
+ voice_d["name"] = voice_d["id"].operator String().replace("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices\\Tokens\\", "");
+ }
+ voice_d["language"] = String::utf16((const char16_t *)w_lang_code) + "_" + String::utf16((const char16_t *)w_reg_code);
+ list.push_back(voice_d);
+
+ delete[] w_lang_code;
+ delete[] w_reg_code;
+
+ cpDataKeyAttribs->Release();
+ }
+ cpVoiceToken->Release();
+ }
+ cpEnum->Release();
+ }
+ }
+ cpCategory->Release();
+ }
+ return list;
+}
+
+void TTS_Windows::speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ ERR_FAIL_COND(!synth);
+ if (p_interrupt) {
+ stop();
+ }
+
+ if (p_text.is_empty()) {
+ DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, p_utterance_id);
+ return;
+ }
+
+ DisplayServer::TTSUtterance message;
+ message.text = p_text;
+ message.voice = p_voice;
+ message.volume = CLAMP(p_volume, 0, 100);
+ message.pitch = CLAMP(p_pitch, 0.f, 2.f);
+ message.rate = CLAMP(p_rate, 0.1f, 10.f);
+ message.id = p_utterance_id;
+ queue.push_back(message);
+
+ if (is_paused()) {
+ resume();
+ } else {
+ _update_tts();
+ }
+}
+
+void TTS_Windows::pause() {
+ ERR_FAIL_COND(!synth);
+ if (!paused) {
+ if (synth->Pause() == S_OK) {
+ paused = true;
+ }
+ }
+}
+
+void TTS_Windows::resume() {
+ ERR_FAIL_COND(!synth);
+ synth->Resume();
+ paused = false;
+}
+
+void TTS_Windows::stop() {
+ ERR_FAIL_COND(!synth);
+
+ SPVOICESTATUS status;
+ synth->GetStatus(&status, nullptr);
+ if (ids.has(status.ulCurrentStream)) {
+ DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, ids[status.ulCurrentStream].id);
+ ids.erase(status.ulCurrentStream);
+ }
+ for (DisplayServer::TTSUtterance &message : queue) {
+ DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, message.id);
+ }
+ queue.clear();
+ synth->Speak(nullptr, SPF_PURGEBEFORESPEAK, nullptr);
+ synth->Resume();
+ paused = false;
+}
+
+TTS_Windows *TTS_Windows::get_singleton() {
+ return singleton;
+}
+
+TTS_Windows::TTS_Windows() {
+ singleton = this;
+ CoInitialize(nullptr);
+
+ if (SUCCEEDED(CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL, IID_ISpVoice, (void **)&synth))) {
+ ULONGLONG event_mask = SPFEI(SPEI_END_INPUT_STREAM) | SPFEI(SPEI_START_INPUT_STREAM) | SPFEI(SPEI_WORD_BOUNDARY);
+ synth->SetInterest(event_mask, event_mask);
+ synth->SetNotifyCallbackFunction(&speech_event_callback, (WPARAM)(this), 0);
+ print_verbose("Text-to-Speech: SAPI initialized.");
+ } else {
+ print_verbose("Text-to-Speech: Cannot initialize ISpVoice!");
+ }
+}
+
+TTS_Windows::~TTS_Windows() {
+ if (synth) {
+ synth->Release();
+ }
+ singleton = nullptr;
+}
diff --git a/platform/windows/tts_windows.h b/platform/windows/tts_windows.h
new file mode 100644
index 0000000000..5da404baf9
--- /dev/null
+++ b/platform/windows/tts_windows.h
@@ -0,0 +1,80 @@
+/*************************************************************************/
+/* tts_windows.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TTS_WINDOWS_H
+#define TTS_WINDOWS_H
+
+#include "core/string/ustring.h"
+#include "core/templates/list.h"
+#include "core/templates/map.h"
+#include "core/variant/array.h"
+#include "servers/display_server.h"
+
+#include <objbase.h>
+#include <sapi.h>
+#include <wchar.h>
+#include <winnls.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+class TTS_Windows {
+ List<DisplayServer::TTSUtterance> queue;
+ ISpVoice *synth = nullptr;
+ bool paused = false;
+ struct UTData {
+ Char16String string;
+ int offset;
+ int id;
+ };
+ Map<ULONG, UTData> ids;
+
+ static void __stdcall speech_event_callback(WPARAM wParam, LPARAM lParam);
+ void _update_tts();
+
+ static TTS_Windows *singleton;
+
+public:
+ static TTS_Windows *get_singleton();
+
+ bool is_speaking() const;
+ bool is_paused() const;
+ Array get_voices() const;
+
+ void speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false);
+ void pause();
+ void resume();
+ void stop();
+
+ TTS_Windows();
+ ~TTS_Windows();
+};
+
+#endif // TTS_WINDOWS_H
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 0d5f0e617c..df21977698 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -44,25 +44,29 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er
const unsigned int BUFFER_SIZE = 16384;
char buf[BUFFER_SIZE + 1]; // +1 for the terminating character
int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list);
- if (len <= 0)
+ if (len <= 0) {
return;
- if ((unsigned int)len >= BUFFER_SIZE)
+ }
+ if ((unsigned int)len >= BUFFER_SIZE) {
len = BUFFER_SIZE; // Output is too big, will be truncated
+ }
buf[len] = 0;
int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, nullptr, 0);
- if (wlen < 0)
+ if (wlen < 0) {
return;
+ }
wchar_t *wbuf = (wchar_t *)memalloc((len + 1) * sizeof(wchar_t));
ERR_FAIL_NULL_MSG(wbuf, "Out of memory.");
MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen);
wbuf[wlen] = 0;
- if (p_err)
+ if (p_err) {
fwprintf(stderr, L"%ls", wbuf);
- else
+ } else {
wprintf(L"%ls", wbuf);
+ }
memfree(wbuf);