diff options
Diffstat (limited to 'platform/windows')
| -rw-r--r-- | platform/windows/crash_handler_windows.cpp | 44 | ||||
| -rw-r--r-- | platform/windows/detect.py | 19 | ||||
| -rw-r--r-- | platform/windows/display_server_windows.cpp | 205 | ||||
| -rw-r--r-- | platform/windows/display_server_windows.h | 31 | ||||
| -rw-r--r-- | platform/windows/export/export.cpp | 8 | ||||
| -rw-r--r-- | platform/windows/export/export.h | 2 | ||||
| -rw-r--r-- | platform/windows/export/export_plugin.cpp | 88 | ||||
| -rw-r--r-- | platform/windows/export/export_plugin.h | 9 | ||||
| -rw-r--r-- | platform/windows/gl_manager_windows.cpp | 122 | ||||
| -rw-r--r-- | platform/windows/gl_manager_windows.h | 6 | ||||
| -rw-r--r-- | platform/windows/godot.natvis | 6 | ||||
| -rw-r--r-- | platform/windows/godot_windows.cpp | 4 | ||||
| -rw-r--r-- | platform/windows/key_mapping_windows.cpp | 17 | ||||
| -rw-r--r-- | platform/windows/os_windows.cpp | 254 | ||||
| -rw-r--r-- | platform/windows/os_windows.h | 39 | ||||
| -rw-r--r-- | platform/windows/tts_windows.cpp | 2 | ||||
| -rw-r--r-- | platform/windows/tts_windows.h | 4 | ||||
| -rw-r--r-- | platform/windows/vulkan_context_win.cpp | 4 | ||||
| -rw-r--r-- | platform/windows/vulkan_context_win.h | 6 | ||||
| -rw-r--r-- | platform/windows/windows_terminal_logger.h | 2 | 
20 files changed, 673 insertions, 199 deletions
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp index 3b2c6fe9f6..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" @@ -129,13 +130,28 @@ 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"); +	} +	// 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)) {  		return EXCEPTION_CONTINUE_SEARCH; @@ -174,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) { @@ -197,12 +199,12 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {  				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); +					print_error(vformat("[%d] %s (%s:%d)", n, fnName.c_str(), (char *)line.FileName, (int)line.LineNumber));  				} else { -					fprintf(stderr, "[%d] %s\n", n, fnName.c_str()); +					print_error(vformat("[%d] %s", n, fnName.c_str()));  				}  			} else { -				fprintf(stderr, "[%d] ???\n", n); +				print_error(vformat("[%d] ???", n));  			}  			n++; @@ -213,8 +215,8 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {  		}  	} 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); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 0b18fb74fb..dd2df1f004 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -198,7 +198,6 @@ def configure_msvc(env, manual_msvc_config):      elif env["target"] == "debug":          env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"])          # Allow big objects. Only needed for debug, see MinGW branch for rationale. -        env.AppendUnique(CCFLAGS=["/bigobj"])          env.Append(LINKFLAGS=["/DEBUG"])      if env["debug_symbols"]: @@ -221,6 +220,10 @@ def configure_msvc(env, manual_msvc_config):      env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"])      env.AppendUnique(CCFLAGS=["/utf-8"])  # Force to use Unicode encoding.      env.AppendUnique(CXXFLAGS=["/TP"])  # assume all sources are C++ +    # Once it was thought that only debug builds would be too large, +    # but this has recently stopped being true. See the mingw function +    # for notes on why this shouldn't be enabled for gcc +    env.AppendUnique(CCFLAGS=["/bigobj"])      if manual_msvc_config:  # should be automatic if SCons found it          if os.getenv("WindowsSdkDir") is not None: @@ -267,14 +270,17 @@ def configure_msvc(env, manual_msvc_config):          "bcrypt",          "Avrt",          "dwmapi", +        "dwrite",      ] -    env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"]) -    if not env["use_volk"]: -        LIBS += ["vulkan"] +    if env["vulkan"]: +        env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"]) +        if not env["use_volk"]: +            LIBS += ["vulkan"] -    env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) -    LIBS += ["opengl32"] +    if env["opengl3"]: +        env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) +        LIBS += ["opengl32"]      env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) @@ -436,6 +442,7 @@ def configure_mingw(env):              "avrt",              "uuid",              "dwmapi", +            "dwrite",          ]      ) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 0412eb2d9c..8c8dbef8a4 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -531,10 +531,43 @@ DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(in  }  void DisplayServerWindows::screen_set_keep_on(bool p_enable) { +	if (keep_screen_on == p_enable) { +		return; +	} + +	if (p_enable) { +		const String reason = "Godot Engine running with display/window/energy_saving/keep_screen_on = true"; +		Char16String reason_utf16 = reason.utf16(); + +		REASON_CONTEXT context; +		context.Version = POWER_REQUEST_CONTEXT_VERSION; +		context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; +		context.Reason.SimpleReasonString = (LPWSTR)(reason_utf16.ptrw()); +		power_request = PowerCreateRequest(&context); +		if (power_request == INVALID_HANDLE_VALUE) { +			print_error("Failed to enable screen_keep_on."); +			return; +		} +		if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired) == 0) { +			print_error("Failed to request system sleep override."); +			return; +		} +		if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired) == 0) { +			print_error("Failed to request display timeout override."); +			return; +		} +	} else { +		PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired); +		PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired); +		CloseHandle(power_request); +		power_request = nullptr; +	} + +	keep_screen_on = p_enable;  }  bool DisplayServerWindows::screen_is_kept_on() const { -	return false; +	return keep_screen_on;  }  Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const { @@ -607,8 +640,11 @@ void DisplayServerWindows::show_window(WindowID p_id) {  		_update_window_style(p_id);  	} -	ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window. -	if (!wd.no_focus && !wd.is_popup) { +	if (wd.no_focus || wd.is_popup) { +		// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow +		ShowWindow(wd.hWnd, SW_SHOWNA); +	} else { +		ShowWindow(wd.hWnd, SW_SHOW);  		SetForegroundWindow(wd.hWnd); // Slightly higher priority.  		SetFocus(wd.hWnd); // Set keyboard focus.  	} @@ -625,7 +661,7 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {  	WindowData &wd = windows[p_window];  	while (wd.transient_children.size()) { -		window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID); +		window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);  	}  	if (wd.transient_parent != INVALID_WINDOW_ID) { @@ -653,7 +689,9 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {  void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) {  #if defined(GLES3_ENABLED) -	gl_manager->window_make_current(p_window_id); +	if (gl_manager) { +		gl_manager->window_make_current(p_window_id); +	}  #endif  } @@ -1106,6 +1144,10 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain  	SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);  	SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex); +	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) { @@ -1490,11 +1532,11 @@ 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()) { -		Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape); +		RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);  		if (cursor_c) {  			if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) { @@ -1794,7 +1836,9 @@ void DisplayServerWindows::make_rendering_thread() {  void DisplayServerWindows::swap_buffers() {  #if defined(GLES3_ENABLED) -	gl_manager->swap_buffers(); +	if (gl_manager) { +		gl_manager->swap_buffers(); +	}  #endif  } @@ -1895,9 +1939,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(); @@ -1946,14 +1992,18 @@ 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) -	context_vulkan->set_vsync_mode(p_window, p_vsync_mode); +	if (context_vulkan) { +		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) -	return context_vulkan->get_vsync_mode(p_window); +	if (context_vulkan) { +		return context_vulkan->get_vsync_mode(p_window); +	}  #endif  	return DisplayServer::VSYNC_ENABLED;  } @@ -1991,7 +2041,7 @@ void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float  }  void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) { -	Map<int, Vector2>::Element *curr = touch_state.find(idx); +	RBMap<int, Vector2>::Element *curr = touch_state.find(idx);  	if (!curr) {  		return;  	} @@ -2018,7 +2068,7 @@ void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent  		Variant *eventp = &event;  		Variant ret;  		Callable::CallError ce; -		wd.event_callback.call((const Variant **)&eventp, 1, ret, ce); +		wd.event_callback.callp((const Variant **)&eventp, 1, ret, ce);  	}  } @@ -2045,7 +2095,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)  			if (windows.has(E->get())) {  				Callable callable = windows[E->get()].input_event_callback;  				if (callable.is_valid()) { -					callable.call((const Variant **)&evp, 1, ret, ce); +					callable.callp((const Variant **)&evp, 1, ret, ce);  				}  			}  			in_dispatch_input_event = false; @@ -2059,7 +2109,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)  		if (windows.has(event_from_window->get_window_id())) {  			Callable callable = windows[event_from_window->get_window_id()].input_event_callback;  			if (callable.is_valid()) { -				callable.call((const Variant **)&evp, 1, ret, ce); +				callable.callp((const Variant **)&evp, 1, ret, ce);  			}  		}  	} else { @@ -2067,7 +2117,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)  		for (const KeyValue<WindowID, WindowData> &E : windows) {  			const Callable callable = E.value.input_event_callback;  			if (callable.is_valid()) { -				callable.call((const Variant **)&evp, 1, ret, ce); +				callable.callp((const Variant **)&evp, 1, ret, ce);  			}  		}  	} @@ -2143,7 +2193,10 @@ void DisplayServerWindows::popup_close(WindowID p_window) {  		WindowID win_id = E->get();  		popup_list.erase(E); -		_send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); +		if (win_id != p_window) { +			// Only request close on related windows, not this window.  We are already processing it. +			_send_window_event(windows[win_id], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); +		}  		E = F;  	}  } @@ -2158,6 +2211,7 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)  			case WM_NCRBUTTONDOWN:  			case WM_NCMBUTTONDOWN:  			case WM_LBUTTONDOWN: +			case WM_RBUTTONDOWN:  			case WM_MBUTTONDOWN: {  				MOUSEHOOKSTRUCT *ms = (MOUSEHOOKSTRUCT *)lParam;  				Point2i pos = Point2i(ms->pt.x, ms->pt.y); @@ -2180,6 +2234,7 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)  				}  				if (C) {  					_send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST); +					return 1;  				}  			} break;  		} @@ -2187,8 +2242,39 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam)  	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. +// Handle a single window message received while CreateWindowEx is still on the stack and our data +// structures are not fully initialized. +LRESULT DisplayServerWindows::_handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { +	switch (uMsg) { +		case WM_GETMINMAXINFO: { +			// We receive this during CreateWindowEx and we haven't initialized the window +			// struct, so let Windows figure out the maximized size. +			// Silently forward to user/default. +		} break; +		case WM_NCCREATE: { +			// We tunnel an unowned pointer to our window context (WindowData) through the +			// first possible message (WM_NCCREATE) to fix up our window context collection. +			CREATESTRUCTW *pCreate = (CREATESTRUCTW *)lParam; +			WindowData *pWindowData = reinterpret_cast<WindowData *>(pCreate->lpCreateParams); + +			// Fix this up so we can recognize the remaining messages. +			pWindowData->hWnd = hWnd; +		} break; +		default: { +			// Additional messages during window creation should happen after we fixed +			// up the data structures on WM_NCCREATE, but this might change in the future, +			// so report an error here and then we can implement them. +			ERR_PRINT_ONCE(vformat("Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error.", uMsg)); +		} break; +	} + +	if (user_proc) { +		return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); +	} +	return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events.  // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures  LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {  	if (drop_events) { @@ -2202,7 +2288,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  	WindowID window_id = INVALID_WINDOW_ID;  	bool window_created = false; -	// Check whether window exists. +	// Check whether window exists +	// FIXME this is O(n), where n is the set of currently open windows and subwindows +	// we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below  	for (const KeyValue<WindowID, WindowData> &E : windows) {  		if (E.value.hWnd == hWnd) {  			window_id = E.key; @@ -2211,10 +2299,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  		}  	} -	// Window doesn't exist or creation in progress, don't handle messages yet. +	// WARNING: we get called with events before the window is registered in our collection +	// specifically, even the call to CreateWindowEx already calls here while still on the stack, +	// so there is no way to store the window handle in our collection before we get here  	if (!window_created) { -		window_id = window_id_counter; -		ERR_FAIL_COND_V(!windows.has(window_id), 0); +		// don't let code below operate on incompletely initialized window objects or missing window_id +		return _handle_early_window_message(hWnd, uMsg, wParam, lParam);  	}  	// Process window messages. @@ -2325,7 +2415,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  		}  		case WM_MOUSELEAVE: {  			old_invalid = true; -			outside = true; +			windows[window_id].mouse_outside = true;  			_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); @@ -2435,6 +2525,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  						windows[window_id].last_tilt = Vector2();  					} +					windows[window_id].last_pen_inverted = packet.pkStatus & TPS_INVERT; +  					POINT coords;  					GetCursorPos(&coords);  					ScreenToClient(windows[window_id].hWnd, &coords); @@ -2453,6 +2545,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  					mm->set_pressure(windows[window_id].last_pressure);  					mm->set_tilt(windows[window_id].last_tilt); +					mm->set_pen_inverted(windows[window_id].last_pen_inverted);  					mm->set_button_mask(last_button_state); @@ -2552,7 +2645,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  				}  			} -			if (outside) { +			if (windows[window_id].mouse_outside) {  				// Mouse enter.  				if (mouse_mode != MOUSE_MODE_CAPTURED) { @@ -2562,7 +2655,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  				CursorShape c = cursor_shape;  				cursor_shape = CURSOR_MAX;  				cursor_set_shape(c); -				outside = false; +				windows[window_id].mouse_outside = false;  				// Once-off notification, must call again.  				track_mouse_leave_event(hWnd); @@ -2585,6 +2678,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  			if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) {  				mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90));  			} +			mm->set_pen_inverted(pen_info.penFlags & (PEN_FLAG_INVERTED | PEN_FLAG_ERASER));  			mm->set_ctrl_pressed(GetKeyState(VK_CONTROL) < 0);  			mm->set_shift_pressed(GetKeyState(VK_SHIFT) < 0); @@ -2652,7 +2746,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  				}  			} -			if (outside) { +			if (windows[window_id].mouse_outside) {  				// Mouse enter.  				if (mouse_mode != MOUSE_MODE_CAPTURED) { @@ -2662,7 +2756,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  				CursorShape c = cursor_shape;  				cursor_shape = CURSOR_MAX;  				cursor_set_shape(c); -				outside = false; +				windows[window_id].mouse_outside = false;  				// Once-off notification, must call again.  				track_mouse_leave_event(hWnd); @@ -2687,14 +2781,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  				} else {  					windows[window_id].last_tilt = Vector2();  					windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f; +					windows[window_id].last_pen_inverted = false;  				}  			} else {  				windows[window_id].last_tilt = Vector2();  				windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f; +				windows[window_id].last_pen_inverted = false;  			}  			mm->set_pressure(windows[window_id].last_pressure);  			mm->set_tilt(windows[window_id].last_tilt); +			mm->set_pen_inverted(windows[window_id].last_pen_inverted);  			mm->set_button_mask(last_button_state); @@ -2975,7 +3072,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  					const Variant *args[] = { &size };  					Variant ret;  					Callable::CallError ce; -					window.rect_changed_callback.call(args, 1, ret, ce); +					window.rect_changed_callback.callp(args, 1, ret, ce);  				}  			} @@ -3135,7 +3232,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA  				Variant *vp = &v;  				Variant ret;  				Callable::CallError ce; -				windows[window_id].drop_files_callback.call((const Variant **)&vp, 1, ret, ce); +				windows[window_id].drop_files_callback.callp((const Variant **)&vp, 1, ret, ce);  			}  		} break;  		default: { @@ -3305,8 +3402,8 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const  		if ((p_new_driver == "wintab") && wintab_available) {  			wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);  			wd.wtlc.lcOptions |= CXO_MESSAGES; -			wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; -			wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE; +			wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; +			wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;  			wd.wtlc.lcPktMode = 0;  			wd.wtlc.lcOutOrgX = 0;  			wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX; @@ -3382,11 +3479,23 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,  				WindowRect.top,  				WindowRect.right - WindowRect.left,  				WindowRect.bottom - WindowRect.top, -				nullptr, nullptr, hInstance, nullptr); +				nullptr, +				nullptr, +				hInstance, +				// tunnel the WindowData we need to handle creation message +				// lifetime is ensured because we are still on the stack when this is +				// processed in the window proc +				reinterpret_cast<void *>(&wd));  		if (!wd.hWnd) {  			MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);  			windows.erase(id); -			return INVALID_WINDOW_ID; +			ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window."); +		} +		if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { +			wd.fullscreen = true; +			if (p_mode == WINDOW_MODE_FULLSCREEN) { +				wd.multiwindow_fs = true; +			}  		}  		if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {  			wd.pre_fs_valid = true; @@ -3406,7 +3515,14 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,  #ifdef GLES3_ENABLED  		if (gl_manager) {  			Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top); -			ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Failed to create an OpenGL window."); + +			// shut down OpenGL, to mirror behavior of Vulkan code +			if (err != OK) { +				memdelete(gl_manager); +				gl_manager = nullptr; +				windows.erase(id); +				ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window."); +			}  		}  #endif @@ -3416,8 +3532,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,  		if ((tablet_get_current_driver() == "wintab") && wintab_available) {  			wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);  			wd.wtlc.lcOptions |= CXO_MESSAGES; -			wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; -			wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE; +			wd.wtlc.lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; +			wd.wtlc.lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;  			wd.wtlc.lcPktMode = 0;  			wd.wtlc.lcOutOrgX = 0;  			wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX; @@ -3451,6 +3567,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,  		ImmReleaseContext(wd.hWnd, wd.im_himc);  		wd.im_position = Vector2(); + +		// FIXME this is wrong in cases where the window coordinates were changed due to full screen mode; use WindowRect  		wd.last_pos = p_rect.position;  		wd.width = p_rect.size.width;  		wd.height = p_rect.size.height; @@ -3529,13 +3647,14 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win  	old_invalid = true;  	mouse_mode = MOUSE_MODE_VISIBLE; -	outside = true; -  	rendering_driver = p_rendering_driver;  	// Init TTS  	tts = memnew(TTS_Windows); +	// Enforce default keep screen on value. +	screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); +  	// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.  	HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");  	if (wintab_lib) { @@ -3739,8 +3858,12 @@ DisplayServerWindows::~DisplayServerWindows() {  		SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);  	} +	// Close power request handle. +	screen_set_keep_on(false); +  #ifdef GLES3_ENABLED  	// destroy windows .. NYI? +	// FIXME wglDeleteContext is never called  #endif  	if (windows.has(MAIN_WINDOW_ID)) { diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 80faf71bd4..db9b589304 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -82,10 +82,13 @@  #define DVC_ROTATION 18  #define CXO_MESSAGES 0x0004 +#define PK_STATUS 0x0002  #define PK_NORMAL_PRESSURE 0x0400  #define PK_TANGENT_PRESSURE 0x0800  #define PK_ORIENTATION 0x1000 +#define TPS_INVERT 0x0010 /* 1.1 */ +  typedef struct tagLOGCONTEXTW {  	WCHAR lcName[40];  	UINT lcOptions; @@ -137,6 +140,7 @@ typedef struct tagORIENTATION {  } ORIENTATION;  typedef struct tagPACKET { +	int pkStatus;  	int pkNormalPressure;  	int pkTangentPressure;  	ORIENTATION pkOrientation; @@ -158,6 +162,14 @@ typedef UINT32 POINTER_FLAGS;  typedef UINT32 PEN_FLAGS;  typedef UINT32 PEN_MASK; +#ifndef PEN_FLAG_INVERTED +#define PEN_FLAG_INVERTED 0x00000002 +#endif + +#ifndef PEN_FLAG_ERASER +#define PEN_FLAG_ERASER 0x00000004 +#endif +  #ifndef PEN_MASK_PRESSURE  #define PEN_MASK_PRESSURE 0x00000001  #endif @@ -301,7 +313,6 @@ class DisplayServerWindows : public DisplayServer {  	int key_event_pos;  	bool old_invalid; -	bool outside;  	int old_x, old_y;  	Point2i center; @@ -314,12 +325,14 @@ class DisplayServerWindows : public DisplayServer {  	RenderingDeviceVulkan *rendering_device_vulkan = nullptr;  #endif -	Map<int, Vector2> touch_state; +	RBMap<int, Vector2> touch_state;  	int pressrc;  	HINSTANCE hInstance; // Holds The Instance Of The Application  	String rendering_driver;  	bool app_focused = false; +	bool keep_screen_on = false; +	HANDLE power_request;  	TTS_Windows *tts = nullptr; @@ -357,11 +370,13 @@ class DisplayServerWindows : public DisplayServer {  		int min_pressure;  		int max_pressure;  		bool tilt_supported; +		bool pen_inverted = false;  		bool block_mm = false;  		int last_pressure_update;  		float last_pressure;  		Vector2 last_tilt; +		bool last_pen_inverted = false;  		HBITMAP hBitmap; //DIB section for layered window  		uint8_t *dib_data = nullptr; @@ -373,6 +388,7 @@ class DisplayServerWindows : public DisplayServer {  		Size2 window_rect;  		Point2 last_pos; +		bool mouse_outside = true;  		ObjectID instance_id; @@ -389,7 +405,7 @@ class DisplayServerWindows : public DisplayServer {  		Callable drop_files_callback;  		WindowID transient_parent = INVALID_WINDOW_ID; -		Set<WindowID> transient_children; +		HashSet<WindowID> transient_children;  		bool is_popup = false;  		Rect2i parent_safe_rect; @@ -399,10 +415,11 @@ class DisplayServerWindows : public DisplayServer {  	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; -	Map<WindowID, WindowData> windows; +	RBMap<WindowID, WindowData> windows;  	WindowID last_focused_window = INVALID_WINDOW_ID; @@ -429,7 +446,7 @@ class DisplayServerWindows : public DisplayServer {  	HCURSOR cursors[CURSOR_MAX] = { nullptr };  	CursorShape cursor_shape = CursorShape::CURSOR_ARROW; -	Map<CursorShape, Vector<Variant>> cursors_cache; +	RBMap<CursorShape, Vector<Variant>> cursors_cache;  	void _drag_event(WindowID p_window, float p_x, float p_y, int idx);  	void _touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx); @@ -447,6 +464,8 @@ class DisplayServerWindows : public DisplayServer {  	static void _dispatch_input_events(const Ref<InputEvent> &p_event);  	void _dispatch_input_event(const Ref<InputEvent> &p_event); +	LRESULT _handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +  public:  	LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);  	LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam); @@ -561,7 +580,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 0fa2913218..20320470b8 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -30,6 +30,7 @@  #include "export.h" +#include "editor/export/editor_export.h"  #include "export_plugin.h"  void register_windows_exporter() { @@ -48,12 +49,7 @@ void register_windows_exporter() {  	Ref<EditorExportPlatformWindows> platform;  	platform.instantiate(); - -	Ref<Image> img = memnew(Image(_windows_logo)); -	Ref<ImageTexture> logo; -	logo.instantiate(); -	logo->create_from_image(img); -	platform->set_logo(logo); +	platform->set_logo(ImageTexture::create_from_image(memnew(Image(_windows_logo))));  	platform->set_name("Windows Desktop");  	platform->set_os_name("Windows"); diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h index 09399f2bee..1054e04b1e 100644 --- a/platform/windows/export/export.h +++ b/platform/windows/export/export.h @@ -33,4 +33,4 @@  void register_windows_exporter(); -#endif +#endif // WINDOWS_EXPORT_H diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index b4d8ce64b2..febef5ad12 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -43,7 +43,10 @@ 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); +	if (f.is_null()) { +		add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), vformat(TTR("Could not open file \"%s\"."), p_path)); +		return ERR_CANT_CREATE; +	}  	f->store_line("@echo off");  	f->store_line("title \"" + p_app_name + "\""); @@ -53,24 +56,29 @@ Error EditorExportPlatformWindows::_export_debug_script(const Ref<EditorExportPr  	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")) { +		_rcedit_add_data(p_preset, p_path); +	} +	return OK; +} +  Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int 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::prepare_template(p_preset, p_debug, pck_path, p_flags); -	if (p_preset->get("application/modify_resources") && err == OK) { -		err = _rcedit_add_data(p_preset, pck_path); -	} -	if (err == OK) { -		err = EditorExportPlatformPC::export_project_data(p_preset, p_debug, pck_path, p_flags); -	} +	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); +		_code_sign(p_preset, pck_path);  	} +  	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); +		if (err != OK) { +			add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), vformat(TTR("Failed to rename temporary file \"%s\"."), pck_path)); +		}  	}  	String app_name; @@ -86,7 +94,9 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>  		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); +			if (_export_debug_script(p_preset, app_name, p_path.get_file(), scr_path) != OK) { +				add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), TTR("Could not create console script.")); +			}  		}  	} @@ -94,7 +104,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>  }  String EditorExportPlatformWindows::get_template_file_name(const String &p_target, const String &p_arch) const { -	return "windows_" + p_arch + "_" + p_target + ".exe"; +	return "windows_" + p_target + "_" + p_arch + ".exe";  }  List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { @@ -103,7 +113,7 @@ List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<Editor  	return list;  } -bool EditorExportPlatformWindows::get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { +bool EditorExportPlatformWindows::get_export_option_visibility(const String &p_option, const HashMap<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") {  		return false; @@ -113,6 +123,7 @@ bool EditorExportPlatformWindows::get_export_option_visibility(const String &p_o  void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {  	EditorExportPlatformPC::get_export_options(r_options); +	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32"), "x86_64"));  	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));  	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0)); @@ -139,7 +150,7 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset  	String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");  	if (rcedit_path != String() && !FileAccess::exists(rcedit_path)) { -		ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", aborting."); +		add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Could not find rcedit executable at \"%s\"."), rcedit_path));  		return ERR_FILE_NOT_FOUND;  	} @@ -152,7 +163,7 @@ Error 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 + ", aborting."); +		add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Could not find wine executable at \"%s\"."), wine_path));  		return ERR_FILE_NOT_FOUND;  	} @@ -219,10 +230,14 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset  	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)."); +	if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) { +		add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), TTR("Could not start rcedit executable. Configure rcedit path in the Editor Settings (Export > Windows > Rcedit), or disable \"Application > Modify Resources\" in the export preset.")); +		return err; +	}  	print_line("rcedit (" + p_path + "): " + str);  	if (str.find("Fatal error") != -1) { +		add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("rcedit failed to modify executable: %s."), str));  		return FAILED;  	} @@ -235,7 +250,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p  #ifdef WINDOWS_ENABLED  	String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool");  	if (!signtool_path.is_empty() && !FileAccess::exists(signtool_path)) { -		ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); +		add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Could not find signtool executable at \"%s\"."), signtool_path));  		return ERR_FILE_NOT_FOUND;  	}  	if (signtool_path.is_empty()) { @@ -244,7 +259,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p  #else  	String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode");  	if (!signtool_path.is_empty() && !FileAccess::exists(signtool_path)) { -		ERR_PRINT("Could not find osslsigncode executable at " + signtool_path + ", aborting."); +		add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Could not find osslsigncode executable at \"%s\"."), signtool_path));  		return ERR_FILE_NOT_FOUND;  	}  	if (signtool_path.is_empty()) { @@ -264,7 +279,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p  			args.push_back("/f");  			args.push_back(p_preset->get("codesign/identity"));  		} else { -			EditorNode::add_io_error("codesign: no identity found"); +			add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));  			return FAILED;  		}  	} else if (id_type == 2) { //Windows certificate store @@ -272,11 +287,11 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p  			args.push_back("/sha1");  			args.push_back(p_preset->get("codesign/identity"));  		} else { -			EditorNode::add_io_error("codesign: no identity found"); +			add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));  			return FAILED;  		}  	} else { -		EditorNode::add_io_error("codesign: invalid identity type"); +		add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Invalid identity type."));  		return FAILED;  	}  #else @@ -284,7 +299,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p  		args.push_back("-pkcs12");  		args.push_back(p_preset->get("codesign/identity"));  	} else { -		EditorNode::add_io_error("codesign: no identity found"); +		add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));  		return FAILED;  	}  #endif @@ -316,7 +331,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p  			args.push_back(p_preset->get("codesign/timestamp_server_url"));  #endif  		} else { -			EditorNode::add_io_error("codesign: invalid timestamp server"); +			add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Invalid timestamp server."));  			return FAILED;  		}  	} @@ -363,7 +378,10 @@ 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_MSG(err != OK, err, "Could not start signtool executable, configure signtool path in the Editor Settings (Export > Windows > Signtool)."); +	if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) { +		add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start signtool executable. Configure signtool path in the Editor Settings (Export > Windows > Signtool), or disable \"Codesign\" in the export preset.")); +		return err; +	}  	print_line("codesign (" + p_path + "): " + str);  #ifndef WINDOWS_ENABLED @@ -371,6 +389,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p  #else  	if (str.find("Failed") != -1) {  #endif +		add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Signtool failed to sign executable: %s."), str));  		return FAILED;  	} @@ -378,10 +397,16 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p  	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); +	if (err != OK) { +		add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Failed to remove temporary file \"%s\"."), p_path)); +		return err; +	}  	err = tmp_dir->rename(p_path + "_signed", p_path); -	ERR_FAIL_COND_V(err != OK, err); +	if (err != OK) { +		add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Failed to rename temporary file \"%s\"."), p_path + "_signed")); +		return err; +	}  #endif  	return OK; @@ -430,15 +455,17 @@ 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 { +Error EditorExportPlatformWindows::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  	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."); +		add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("Windows executables cannot be >= 4 GiB.")); +		return ERR_INVALID_DATA;  	}  	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ_WRITE);  	if (f.is_null()) { +		add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), vformat(TTR("Failed to open executable file \"%s\"."), p_path));  		return ERR_CANT_OPEN;  	} @@ -450,6 +477,7 @@ Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int6  		f->seek(pe_pos);  		uint32_t magic = f->get_32();  		if (magic != 0x00004550) { +			add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("Executable file header corrupted."));  			return ERR_FILE_CORRUPT;  		}  	} @@ -499,5 +527,9 @@ Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int6  		}  	} -	return found ? OK : ERR_FILE_CORRUPT; +	if (!found) { +		add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("Executable \"pck\" section not found.")); +		return ERR_FILE_CORRUPT; +	} +	return OK;  } diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index c33c7f1f63..b9e59829a0 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -33,8 +33,8 @@  #include "core/io/file_access.h"  #include "core/os/os.h" -#include "editor/editor_export.h"  #include "editor/editor_settings.h" +#include "editor/export/editor_export_platform_pc.h"  #include "platform/windows/logo.gen.h"  class EditorExportPlatformWindows : public EditorExportPlatformPC { @@ -44,13 +44,14 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC {  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 get_export_option_visibility(const String &p_option, const HashMap<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; +	virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;  }; -#endif +#endif // WINDOWS_EXPORT_PLUGIN_H diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp index a97fa99d7f..d509ff8c51 100644 --- a/platform/windows/gl_manager_windows.cpp +++ b/platform/windows/gl_manager_windows.cpp @@ -54,6 +54,18 @@  typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *); +static String format_error_message(DWORD id) { +	LPWSTR messageBuffer = nullptr; +	size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, +			nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr); + +	String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size); + +	LocalFree(messageBuffer); + +	return msg; +} +  int GLManager_Windows::_find_or_create_display(GLWindow &win) {  	// find display NYI, only 1 supported so far  	if (_displays.size()) { @@ -79,7 +91,7 @@ int GLManager_Windows::_find_or_create_display(GLWindow &win) {  	return new_display_id;  } -Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) { +static Error _configure_pixel_format(HDC hDC) {  	static PIXELFORMATDESCRIPTOR pfd = {  		sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor  		1, @@ -101,9 +113,6 @@ Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {  		0, 0, 0 // Layer Masks Ignored  	}; -	// alias -	HDC hDC = win.hDC; -  	int pixel_format = ChoosePixelFormat(hDC, &pfd);  	if (!pixel_format) // Did Windows Find A Matching Pixel Format?  	{ @@ -116,13 +125,24 @@ Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {  		return ERR_CANT_CREATE; // Return FALSE  	} -	gl_display.hRC = wglCreateContext(hDC); +	return OK; +} + +Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) { +	Error err = _configure_pixel_format(win.hDC); +	if (err != OK) { +		return err; +	} + +	gl_display.hRC = wglCreateContext(win.hDC);  	if (!gl_display.hRC) // Are We Able To Get A Rendering Context?  	{  		return ERR_CANT_CREATE; // Return FALSE  	} -	wglMakeCurrent(hDC, gl_display.hRC); +	if (!wglMakeCurrent(win.hDC, gl_display.hRC)) { +		ERR_PRINT("Could not attach OpenGL context to newly created window: " + format_error_message(GetLastError())); +	}  	int attribs[] = {  		WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context @@ -143,57 +163,61 @@ Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {  		return ERR_CANT_CREATE;  	} -	HGLRC new_hRC = wglCreateContextAttribsARB(hDC, 0, attribs); +	HGLRC new_hRC = wglCreateContextAttribsARB(win.hDC, 0, attribs);  	if (!new_hRC) {  		wglDeleteContext(gl_display.hRC);  		gl_display.hRC = 0; -		return ERR_CANT_CREATE; // Return false +		return ERR_CANT_CREATE;  	} -	wglMakeCurrent(hDC, nullptr); + +	if (!wglMakeCurrent(win.hDC, nullptr)) { +		ERR_PRINT("Could not detach OpenGL context from newly created window: " + format_error_message(GetLastError())); +	} +  	wglDeleteContext(gl_display.hRC);  	gl_display.hRC = new_hRC; -	if (!wglMakeCurrent(hDC, gl_display.hRC)) // Try To Activate The Rendering Context +	if (!wglMakeCurrent(win.hDC, gl_display.hRC)) // Try To Activate The Rendering Context  	{ +		ERR_PRINT("Could not attach OpenGL context to newly created window with replaced OpenGL context: " + format_error_message(GetLastError()));  		wglDeleteContext(gl_display.hRC);  		gl_display.hRC = 0; -		return ERR_CANT_CREATE; // Return FALSE +		return ERR_CANT_CREATE;  	}  	return OK;  }  Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height) { -	HDC hdc = GetDC(p_hwnd); -	if (!hdc) { -		return ERR_CANT_CREATE; // Return FALSE +	HDC hDC = GetDC(p_hwnd); +	if (!hDC) { +		return ERR_CANT_CREATE;  	} -	// make sure vector is big enough... -	// we can mirror the external vector, it is simpler -	// to keep the IDs identical for fast lookup -	if (p_window_id >= (int)_windows.size()) { -		_windows.resize(p_window_id + 1); +	// configure the HDC to use a compatible pixel format +	Error result = _configure_pixel_format(hDC); +	if (result != OK) { +		return result;  	} -	GLWindow &win = _windows[p_window_id]; -	win.in_use = true; -	win.window_id = p_window_id; +	GLWindow win;  	win.width = p_width;  	win.height = p_height;  	win.hwnd = p_hwnd; -	win.hDC = hdc; +	win.hDC = hDC;  	win.gldisplay_id = _find_or_create_display(win);  	if (win.gldisplay_id == -1) { -		// release DC? -		_windows.remove_at(_windows.size() - 1);  		return FAILED;  	} +	// WARNING: p_window_id is an eternally growing integer since popup windows keep coming and going +	// and each of them has a higher id than the previous, so it must be used in a map not a vector +	_windows[p_window_id] = win; +  	// make current -	window_make_current(_windows.size() - 1); +	window_make_current(p_window_id);  	return OK;  } @@ -217,11 +241,10 @@ int GLManager_Windows::window_get_height(DisplayServer::WindowID p_window_id) {  void GLManager_Windows::window_destroy(DisplayServer::WindowID p_window_id) {  	GLWindow &win = get_window(p_window_id); -	win.in_use = false; -  	if (_current_window == &win) {  		_current_window = nullptr;  	} +	_windows.erase(p_window_id);  }  void GLManager_Windows::release_current() { @@ -229,7 +252,9 @@ void GLManager_Windows::release_current() {  		return;  	} -	wglMakeCurrent(_current_window->hDC, nullptr); +	if (!wglMakeCurrent(_current_window->hDC, nullptr)) { +		ERR_PRINT("Could not detach OpenGL context from window marked current: " + format_error_message(GetLastError())); +	}  }  void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) { @@ -237,10 +262,8 @@ void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id)  		return;  	} +	// crash if our data structures are out of sync, i.e. not found  	GLWindow &win = _windows[p_window_id]; -	if (!win.in_use) { -		return; -	}  	// noop  	if (&win == _current_window) { @@ -248,7 +271,9 @@ void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id)  	}  	const GLDisplay &disp = get_display(win.gldisplay_id); -	wglMakeCurrent(win.hDC, disp.hRC); +	if (!wglMakeCurrent(win.hDC, disp.hRC)) { +		ERR_PRINT("Could not switch OpenGL context to other window: " + format_error_message(GetLastError())); +	}  	_internal_set_current_window(&win);  } @@ -257,34 +282,19 @@ void GLManager_Windows::make_current() {  	if (!_current_window) {  		return;  	} -	if (!_current_window->in_use) { -		WARN_PRINT("current window not in use!"); -		return; -	}  	const GLDisplay &disp = get_current_display(); -	wglMakeCurrent(_current_window->hDC, disp.hRC); +	if (!wglMakeCurrent(_current_window->hDC, disp.hRC)) { +		ERR_PRINT("Could not switch OpenGL context to window marked current: " + format_error_message(GetLastError())); +	}  }  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) { -		return; +	// on other platforms, OpenGL swaps buffers for all windows (on all displays, really?) +	// Windows swaps buffers on a per-window basis +	// REVISIT: this could be structurally bad, should we have "dirty" flags then? +	for (KeyValue<DisplayServer::WindowID, GLWindow> &entry : _windows) { +		SwapBuffers(entry.value.hDC);  	} -	if (!_current_window->in_use) { -		WARN_PRINT("current window not in use!"); -		return; -	} - -	//	print_line("\tswap_buffers"); - -	// only for debugging without drawing anything -	//	glClearColor(Math::randf(), 0, 1, 1); -	//glClear(GL_COLOR_BUFFER_BIT); - -	//	const GLDisplay &disp = get_current_display(); -	SwapBuffers(_current_window->hDC);  }  Error GLManager_Windows::initialize() { diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows.h index dc411983e8..5e43a3de2a 100644 --- a/platform/windows/gl_manager_windows.h +++ b/platform/windows/gl_manager_windows.h @@ -52,10 +52,6 @@ public:  private:  	// any data specific to the window  	struct GLWindow { -		bool in_use = false; - -		// the external ID .. should match the GL window number .. unused I think -		DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID;  		int width = 0;  		int height = 0; @@ -71,7 +67,7 @@ private:  		HGLRC hRC;  	}; -	LocalVector<GLWindow> _windows; +	RBMap<DisplayServer::WindowID, GLWindow> _windows;  	LocalVector<GLDisplay> _displays;  	GLWindow *_current_window = nullptr; diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis index bb855e4ac8..cdd1c14978 100644 --- a/platform/windows/godot.natvis +++ b/platform/windows/godot.natvis @@ -40,11 +40,13 @@  		<DisplayString Condition="type == Variant::TRANSFORM2D">{_data._transform2d}</DisplayString>  		<DisplayString Condition="type == Variant::AABB">{_data._aabb}</DisplayString>  		<DisplayString Condition="type == Variant::BASIS">{_data._basis}</DisplayString> -		<DisplayString Condition="type == Variant::TRANSFORM3D">{_data._transform}</DisplayString> +		<DisplayString Condition="type == Variant::TRANSFORM3D">{_data._transform3d}</DisplayString> +		<DisplayString Condition="type == Variant::PROJECTION">{_data._projection}</DisplayString>  		<DisplayString Condition="type == Variant::STRING">{*(String *)_data._mem}</DisplayString>  		<DisplayString Condition="type == Variant::VECTOR2">{*(Vector2 *)_data._mem}</DisplayString>  		<DisplayString Condition="type == Variant::RECT2">{*(Rect2 *)_data._mem}</DisplayString>  		<DisplayString Condition="type == Variant::VECTOR3">{*(Vector3 *)_data._mem}</DisplayString> +		<DisplayString Condition="type == Variant::VECTOR4">{*(Vector4 *)_data._mem}</DisplayString>  		<DisplayString Condition="type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString>  		<DisplayString Condition="type == Variant::QUATERNION">{*(Quaternion *)_data._mem}</DisplayString>  		<DisplayString Condition="type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString> @@ -72,7 +74,7 @@  			<Item Name="[value]" Condition="type == Variant::TRANSFORM2D">_data._transform2d</Item>  			<Item Name="[value]" Condition="type == Variant::AABB">_data._aabb</Item>  			<Item Name="[value]" Condition="type == Variant::BASIS">_data._basis</Item> -			<Item Name="[value]" Condition="type == Variant::TRANSFORM3D">_data._transform</Item> +			<Item Name="[value]" Condition="type == Variant::TRANSFORM3D">_data._transform3d</Item>  			<Item Name="[value]" Condition="type == Variant::STRING">*(String *)_data._mem</Item>  			<Item Name="[value]" Condition="type == Variant::VECTOR2">*(Vector2 *)_data._mem</Item>  			<Item Name="[value]" Condition="type == Variant::RECT2">*(Rect2 *)_data._mem</Item> diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp index 8de3ef294a..72920d2816 100644 --- a/platform/windows/godot_windows.cpp +++ b/platform/windows/godot_windows.cpp @@ -166,6 +166,10 @@ int widechar_main(int argc, wchar_t **argv) {  			delete[] argv_utf8[i];  		}  		delete[] argv_utf8; + +		if (err == ERR_HELP) { // Returned by --help and --version, so success. +			return 0; +		}  		return 255;  	} diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp index e32dc0d1a6..2d8d68a575 100644 --- a/platform/windows/key_mapping_windows.cpp +++ b/platform/windows/key_mapping_windows.cpp @@ -179,7 +179,14 @@ static _WinTranslatePair _vk_to_keycode[] = {  	{ Key::F14, VK_F14 }, // (0x7D)  	{ Key::F15, VK_F15 }, // (0x7E)  	{ Key::F16, VK_F16 }, // (0x7F) -	// We have no mappings for F17-F24. (0x80-87) +	{ Key::F17, VK_F17 }, // (0x80) +	{ Key::F18, VK_F18 }, // (0x81) +	{ Key::F19, VK_F19 }, // (0x82) +	{ Key::F20, VK_F20 }, // (0x83) +	{ Key::F21, VK_F21 }, // (0x84) +	{ Key::F22, VK_F22 }, // (0x85) +	{ Key::F23, VK_F23 }, // (0x86) +	{ Key::F24, VK_F24 }, // (0x87)  	// 0x88-8F are reserved for UI navigation.  	{ Key::NUMLOCK, VK_NUMLOCK }, // (0x90)  	{ Key::SCROLLLOCK, VK_SCROLL }, // (0x91) @@ -409,6 +416,14 @@ static _WinTranslatePair _scancode_to_keycode[] = {  	{ Key::F14, 0x65 },  	{ Key::F15, 0x66 },  	{ Key::F16, 0x67 }, +	{ Key::F17, 0x68 }, +	{ Key::F18, 0x69 }, +	{ Key::F19, 0x6A }, +	{ Key::F20, 0x6B }, +	{ Key::F21, 0x6C }, +	{ Key::F22, 0x6D }, +	{ Key::F23, 0x6E }, +	{ Key::F24, 0x76 },  	{ Key::UNKNOWN, 0 }  }; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 8755bc65dc..ad4be950cc 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -48,6 +48,7 @@  #include <avrt.h>  #include <bcrypt.h>  #include <direct.h> +#include <dwrite.h>  #include <knownfolders.h>  #include <process.h>  #include <regstr.h> @@ -129,9 +130,34 @@ void OS_Windows::initialize_debugging() {  	SetConsoleCtrlHandler(HandlerRoutine, TRUE);  } +#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED +static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) { +	String err_str; +	if (p_errorexp && p_errorexp[0]) { +		err_str = String::utf8(p_errorexp); +	} else { +		err_str = String::utf8(p_file) + ":" + itos(p_line) + " - " + String::utf8(p_error); +	} + +	if (p_editor_notify) { +		err_str += " (User)\n"; +	} else { +		err_str += "\n"; +	} + +	OutputDebugStringW((LPCWSTR)err_str.utf16().ptr()); +} +#endif +  void OS_Windows::initialize() {  	crash_handler.initialize(); +#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED +	error_handlers.errfunc = _error_handler; +	error_handlers.userdata = this; +	add_error_handler(&error_handlers); +#endif +  #ifndef WINDOWS_SUBSYSTEM_CONSOLE  	RedirectIOToConsole();  #endif @@ -153,7 +179,7 @@ void OS_Windows::initialize() {  	//  long as the windows scheduler resolution (~16-30ms) even for calls like Sleep(1)  	timeBeginPeriod(1); -	process_map = memnew((Map<ProcessID, ProcessInfo>)); +	process_map = memnew((HashMap<ProcessID, ProcessInfo>));  	// Add current Godot PID to the list of known PIDs  	ProcessInfo current_pi = {}; @@ -194,6 +220,10 @@ void OS_Windows::finalize_core() {  	memdelete(process_map);  	NetSocketPosix::cleanup(); + +#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED +	remove_error_handler(&error_handlers); +#endif  }  Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) { @@ -202,7 +232,7 @@ Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) {  	return OK;  } -Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { +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)) { @@ -230,6 +260,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;  } @@ -383,6 +417,31 @@ String OS_Windows::_quote_command_line_argument(const String &p_text) const {  	return p_text;  } +static void _append_to_pipe(char *p_bytes, int p_size, String *r_pipe, Mutex *p_pipe_mutex) { +	// Try to convert from default ANSI code page to Unicode. +	LocalVector<wchar_t> wchars; +	int total_wchars = MultiByteToWideChar(CP_ACP, 0, p_bytes, p_size, nullptr, 0); +	if (total_wchars > 0) { +		wchars.resize(total_wchars); +		if (MultiByteToWideChar(CP_ACP, 0, p_bytes, p_size, wchars.ptr(), total_wchars) == 0) { +			wchars.clear(); +		} +	} + +	if (p_pipe_mutex) { +		p_pipe_mutex->lock(); +	} +	if (wchars.is_empty()) { +		// Let's hope it's compatible with UTF-8. +		(*r_pipe) += String::utf8(p_bytes, p_size); +	} else { +		(*r_pipe) += String(wchars.ptr(), total_wchars); +	} +	if (p_pipe_mutex) { +		p_pipe_mutex->unlock(); +	} +} +  Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {  	String path = p_path.replace("/", "\\");  	String command = _quote_command_line_argument(path); @@ -431,21 +490,44 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,  	if (r_pipe) {  		CloseHandle(pipe[1]); // Close pipe write handle (only child process is writing). -		char buf[4096]; + +		LocalVector<char> bytes; +		int bytes_in_buffer = 0; + +		const int CHUNK_SIZE = 4096;  		DWORD read = 0;  		for (;;) { // Read StdOut and StdErr from pipe. -			bool success = ReadFile(pipe[0], buf, 4096, &read, NULL); +			bytes.resize(bytes_in_buffer + CHUNK_SIZE); +			const bool success = ReadFile(pipe[0], bytes.ptr() + bytes_in_buffer, CHUNK_SIZE, &read, NULL);  			if (!success || read == 0) {  				break;  			} -			if (p_pipe_mutex) { -				p_pipe_mutex->lock(); + +			// Assume that all possible encodings are ASCII-compatible. +			// Break at newline to allow receiving long output in portions. +			int newline_index = -1; +			for (int i = read - 1; i >= 0; i--) { +				if (bytes[bytes_in_buffer + i] == '\n') { +					newline_index = i; +					break; +				}  			} -			(*r_pipe) += String::utf8(buf, read); -			if (p_pipe_mutex) { -				p_pipe_mutex->unlock(); +			if (newline_index == -1) { +				bytes_in_buffer += read; +				continue;  			} + +			const int bytes_to_convert = bytes_in_buffer + (newline_index + 1); +			_append_to_pipe(bytes.ptr(), bytes_to_convert, r_pipe, p_pipe_mutex); + +			bytes_in_buffer = read - (newline_index + 1); +			memmove(bytes.ptr(), bytes.ptr() + bytes_to_convert, bytes_in_buffer); +		} + +		if (bytes_in_buffer > 0) { +			_append_to_pipe(bytes.ptr(), bytes_in_buffer, r_pipe, p_pipe_mutex);  		} +  		CloseHandle(pipe[0]); // Close pipe read handle.  	} else {  		WaitForSingleObject(pi.pi.hProcess, INFINITE); @@ -513,6 +595,25 @@ 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) {  		return ERR_CANT_OPEN; @@ -521,6 +622,135 @@ Error OS_Windows::set_cwd(const String &p_cwd) {  	return OK;  } +Vector<String> OS_Windows::get_system_fonts() const { +	Vector<String> ret; +	HashSet<String> font_names; + +	ComAutoreleaseRef<IDWriteFactory> dwrite_factory; +	HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&dwrite_factory.reference)); +	ERR_FAIL_COND_V(FAILED(hr) || dwrite_factory.is_null(), ret); + +	ComAutoreleaseRef<IDWriteFontCollection> font_collection; +	hr = dwrite_factory->GetSystemFontCollection(&font_collection.reference, false); +	ERR_FAIL_COND_V(FAILED(hr) || font_collection.is_null(), ret); + +	UINT32 family_count = font_collection->GetFontFamilyCount(); +	for (UINT32 i = 0; i < family_count; i++) { +		ComAutoreleaseRef<IDWriteFontFamily> family; +		hr = font_collection->GetFontFamily(i, &family.reference); +		ERR_CONTINUE(FAILED(hr) || family.is_null()); + +		ComAutoreleaseRef<IDWriteLocalizedStrings> family_names; +		hr = family->GetFamilyNames(&family_names.reference); +		ERR_CONTINUE(FAILED(hr) || family_names.is_null()); + +		UINT32 index = 0; +		BOOL exists = false; +		UINT32 length = 0; +		Char16String name; + +		hr = family_names->FindLocaleName(L"en-us", &index, &exists); +		ERR_CONTINUE(FAILED(hr)); + +		hr = family_names->GetStringLength(index, &length); +		ERR_CONTINUE(FAILED(hr)); + +		name.resize(length + 1); +		hr = family_names->GetString(index, (WCHAR *)name.ptrw(), length + 1); +		ERR_CONTINUE(FAILED(hr)); + +		font_names.insert(String::utf16(name.ptr(), length)); +	} + +	for (const String &E : font_names) { +		ret.push_back(E); +	} +	return ret; +} + +String OS_Windows::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const { +	String font_name = p_font_name; +	if (font_name.to_lower() == "sans-serif") { +		font_name = "Arial"; +	} else if (font_name.to_lower() == "serif") { +		font_name = "Times New Roman"; +	} else if (font_name.to_lower() == "monospace") { +		font_name = "Courier New"; +	} else if (font_name.to_lower() == "cursive") { +		font_name = "Comic Sans MS"; +	} else if (font_name.to_lower() == "fantasy") { +		font_name = "Gabriola"; +	} + +	ComAutoreleaseRef<IDWriteFactory> dwrite_factory; +	HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&dwrite_factory.reference)); +	ERR_FAIL_COND_V(FAILED(hr) || dwrite_factory.is_null(), String()); + +	ComAutoreleaseRef<IDWriteFontCollection> font_collection; +	hr = dwrite_factory->GetSystemFontCollection(&font_collection.reference, false); +	ERR_FAIL_COND_V(FAILED(hr) || font_collection.is_null(), String()); + +	UINT32 index = 0; +	BOOL exists = false; +	font_collection->FindFamilyName((const WCHAR *)font_name.utf16().get_data(), &index, &exists); +	if (FAILED(hr)) { +		return String(); +	} + +	ComAutoreleaseRef<IDWriteFontFamily> family; +	hr = font_collection->GetFontFamily(index, &family.reference); +	if (FAILED(hr) || family.is_null()) { +		return String(); +	} + +	ComAutoreleaseRef<IDWriteFont> dwrite_font; +	hr = family->GetFirstMatchingFont(p_bold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, p_italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL, &dwrite_font.reference); +	if (FAILED(hr) || dwrite_font.is_null()) { +		return String(); +	} + +	ComAutoreleaseRef<IDWriteFontFace> dwrite_face; +	hr = dwrite_font->CreateFontFace(&dwrite_face.reference); +	if (FAILED(hr) || dwrite_face.is_null()) { +		return String(); +	} + +	UINT32 number_of_files = 0; +	hr = dwrite_face->GetFiles(&number_of_files, nullptr); +	if (FAILED(hr)) { +		return String(); +	} +	Vector<ComAutoreleaseRef<IDWriteFontFile>> files; +	files.resize(number_of_files); +	hr = dwrite_face->GetFiles(&number_of_files, (IDWriteFontFile **)files.ptrw()); +	if (FAILED(hr)) { +		return String(); +	} + +	for (UINT32 i = 0; i < number_of_files; i++) { +		void const *reference_key = nullptr; +		UINT32 reference_key_size = 0; +		ComAutoreleaseRef<IDWriteLocalFontFileLoader> loader; + +		hr = files.write[i]->GetLoader((IDWriteFontFileLoader **)&loader.reference); +		if (FAILED(hr) || loader.is_null()) { +			continue; +		} +		hr = files.write[i]->GetReferenceKey(&reference_key, &reference_key_size); +		if (FAILED(hr)) { +			continue; +		} + +		WCHAR file_path[MAX_PATH]; +		hr = loader->GetFilePathFromKey(reference_key, reference_key_size, &file_path[0], MAX_PATH); +		if (FAILED(hr)) { +			continue; +		} +		return String::utf16((const char16_t *)&file_path[0]); +	} +	return String(); +} +  String OS_Windows::get_executable_path() const {  	WCHAR bufname[4096];  	GetModuleFileNameW(nullptr, bufname, 4096); @@ -851,9 +1081,9 @@ String OS_Windows::get_user_data_dir() const {  }  String OS_Windows::get_unique_id() const { -	HW_PROFILE_INFO HwProfInfo; -	ERR_FAIL_COND_V(!GetCurrentHwProfile(&HwProfInfo), ""); -	return String::utf16((const char16_t *)(HwProfInfo.szHwProfileGuid), HW_PROFILE_GUIDLEN); +	HW_PROFILE_INFOA HwProfInfo; +	ERR_FAIL_COND_V(!GetCurrentHwProfileA(&HwProfInfo), ""); +	return String((HwProfInfo.szHwProfileGuid), HW_PROFILE_GUIDLEN);  }  bool OS_Windows::_check_internal_feature_support(const String &p_feature) { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 370cb77fde..80fc860738 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -57,6 +57,31 @@  #include <windows.h>  #include <windowsx.h> +#ifdef DEBUG_ENABLED +// forward error messages to OutputDebugString +#define WINDOWS_DEBUG_OUTPUT_ENABLED +#endif + +template <class T> +class ComAutoreleaseRef { +public: +	T *reference = nullptr; + +	_FORCE_INLINE_ T *operator->() { return reference; } +	_FORCE_INLINE_ const T *operator->() const { return reference; } +	_FORCE_INLINE_ T *operator*() { return reference; } +	_FORCE_INLINE_ const T *operator*() const { return reference; } +	_FORCE_INLINE_ bool is_valid() const { return reference != nullptr; } +	_FORCE_INLINE_ bool is_null() const { return reference == nullptr; } +	ComAutoreleaseRef() {} +	~ComAutoreleaseRef() { +		if (reference != nullptr) { +			reference->Release(); +			reference = nullptr; +		} +	} +}; +  class JoypadWindows;  class OS_Windows : public OS {  #ifdef STDOUT_FILE @@ -81,6 +106,10 @@ class OS_Windows : public OS {  	CrashHandler crash_handler; +#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED +	ErrorHandlerList error_handlers; +#endif +  	bool force_quit;  	HWND main_window; @@ -101,14 +130,14 @@ protected:  		STARTUPINFO si;  		PROCESS_INFORMATION pi;  	}; -	Map<ProcessID, ProcessInfo> *process_map; +	HashMap<ProcessID, ProcessInfo> *process_map;  public:  	virtual void alert(const String &p_alert, const String &p_title = "ALERT!") 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) 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; @@ -132,11 +161,15 @@ 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;  	virtual bool set_environment(const String &p_var, const String &p_value) const override; +	virtual Vector<String> get_system_fonts() const override; +	virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override; +  	virtual String get_executable_path() const override;  	virtual String get_locale() const override; @@ -175,4 +208,4 @@ public:  	~OS_Windows();  }; -#endif +#endif // OS_WINDOWS_H diff --git a/platform/windows/tts_windows.cpp b/platform/windows/tts_windows.cpp index 05249934ba..e5daf602e6 100644 --- a/platform/windows/tts_windows.cpp +++ b/platform/windows/tts_windows.cpp @@ -99,7 +99,7 @@ void TTS_Windows::_update_tts() {  		UTData ut;  		ut.string = text.utf16(); -		ut.offset = pitch_tag.length(); // Substract injected <pitch> tag offset. +		ut.offset = pitch_tag.length(); // Subtract injected <pitch> tag offset.  		ut.id = message.id;  		synth->SetVolume(message.volume); diff --git a/platform/windows/tts_windows.h b/platform/windows/tts_windows.h index 5da404baf9..d84a3d273a 100644 --- a/platform/windows/tts_windows.h +++ b/platform/windows/tts_windows.h @@ -33,7 +33,7 @@  #include "core/string/ustring.h"  #include "core/templates/list.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h"  #include "core/variant/array.h"  #include "servers/display_server.h" @@ -54,7 +54,7 @@ class TTS_Windows {  		int offset;  		int id;  	}; -	Map<ULONG, UTData> ids; +	RBMap<ULONG, UTData> ids;  	static void __stdcall speech_event_callback(WPARAM wParam, LPARAM lParam);  	void _update_tts(); diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp index 07c41395fb..e62c6c1dc8 100644 --- a/platform/windows/vulkan_context_win.cpp +++ b/platform/windows/vulkan_context_win.cpp @@ -28,6 +28,8 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ +#if defined(WINDOWS_ENABLED) && defined(VULKAN_ENABLED) +  #include "vulkan_context_win.h"  #ifdef USE_VOLK  #include <volk.h> @@ -57,3 +59,5 @@ VulkanContextWindows::VulkanContextWindows() {  VulkanContextWindows::~VulkanContextWindows() {  } + +#endif diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h index e68f0125ca..d5950a129a 100644 --- a/platform/windows/vulkan_context_win.h +++ b/platform/windows/vulkan_context_win.h @@ -28,8 +28,8 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ -#ifndef VULKAN_DEVICE_WIN_H -#define VULKAN_DEVICE_WIN_H +#ifndef VULKAN_CONTEXT_WIN_H +#define VULKAN_CONTEXT_WIN_H  #include "drivers/vulkan/vulkan_context.h" @@ -46,4 +46,4 @@ public:  	~VulkanContextWindows();  }; -#endif // VULKAN_DEVICE_WIN_H +#endif // VULKAN_CONTEXT_WIN_H diff --git a/platform/windows/windows_terminal_logger.h b/platform/windows/windows_terminal_logger.h index 1045f12201..348a49c845 100644 --- a/platform/windows/windows_terminal_logger.h +++ b/platform/windows/windows_terminal_logger.h @@ -44,4 +44,4 @@ public:  #endif -#endif +#endif // WINDOWS_TERMINAL_LOGGER_H  |