Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: Add support for enabling Alt+Space menu and fix borderless maximize #88329

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/config/project_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("application/run/disable_stdout", false);
GLOBAL_DEF("application/run/disable_stderr", false);
GLOBAL_DEF("application/run/print_header", true);
GLOBAL_DEF("application/run/enable_alt_space_menu", false);
GLOBAL_DEF_RST("application/config/use_hidden_project_data_directory", true);
GLOBAL_DEF("application/config/use_custom_user_dir", false);
GLOBAL_DEF("application/config/custom_user_dir_name", "");
Expand Down
5 changes: 5 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@
If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. See also [member application/run/disable_stderr].
Changes to this setting will only be applied upon restarting the application.
</member>
<member name="application/run/enable_alt_space_menu" type="bool" setter="" getter="" default="false">
If [code]true[/code], allows the [kbd]Alt + Space[/kbd] keys to display the window menu. This menu allows the user to perform various window management operations such as moving, resizing, or minimizing the window.
[b]Note:[/b] When the menu is displayed, project execution will pause until the menu is [i]fully[/i] closed due to Windows behavior. Consider this when enabling this setting in a networked multiplayer game. The menu is only considered fully closed when an option is selected, when the user clicks outside, or when [kbd]Escape[/kbd] is pressed after bringing up the window menu [i]and[/i] another key is pressed afterwards.
[b]Note:[/b] This setting is implemented only on Windows.
</member>
<member name="application/run/flush_stdout_on_print" type="bool" setter="" getter="" default="false">
If [code]true[/code], flushes the standard output stream every time a line is printed. This affects both terminal logging and file logging.
When running a project, this setting must be enabled if you want logs to be collected by service managers such as systemd/journalctl. This setting is disabled by default on release builds, since flushing on every printed line will negatively affect performance if lots of lines are printed in a rapid succession. Also, if this setting is enabled, logged files will still be written successfully if the application crashes or is otherwise killed by the user (without being closed "normally").
Expand Down
57 changes: 51 additions & 6 deletions platform/windows/display_server_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1895,7 +1895,7 @@ Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window)
return Size2();
}

void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) {
void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) {
// Windows docs for window styles:
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
Expand All @@ -1909,7 +1909,17 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre

if (p_fullscreen || p_borderless) {
r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past.
if ((p_fullscreen && p_multiwindow_fs) || p_maximized) {
if (p_maximized) {
r_style |= WS_MAXIMIZE;
}
if (!p_fullscreen) {
r_style |= WS_SYSMENU | WS_MINIMIZEBOX;

if (p_resizable) {
r_style |= WS_MAXIMIZEBOX;
}
}
if ((p_fullscreen && p_multiwindow_fs) || p_maximized_fs) {
r_style |= WS_BORDER; // Allows child windows to be displayed on top of full screen.
}
} else {
Expand Down Expand Up @@ -1945,7 +1955,7 @@ 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 || wd.is_popup, style, style_ex);
_get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, style, style_ex);

SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
Expand Down Expand Up @@ -1988,6 +1998,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
wd.pre_fs_valid = true;
}

ShowWindow(wd.hWnd, SW_RESTORE);
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);

if (restore_mouse_trails > 1) {
Expand Down Expand Up @@ -2023,7 +2034,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
}

if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) {
if (wd.minimized) {
if (wd.minimized || wd.maximized) {
ShowWindow(wd.hWnd, SW_RESTORE);
}
wd.was_maximized = wd.maximized;
Expand Down Expand Up @@ -3782,6 +3793,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x;
min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y;
}
if (windows[window_id].borderless) {
Rect2i screen_rect = screen_get_usable_rect(window_get_current_screen(window_id));

// Set the size of (borderless) maximized mode to exclude taskbar (or any other panel) if present.
min_max_info->ptMaxPosition.x = screen_rect.position.x;
min_max_info->ptMaxPosition.y = screen_rect.position.y;
min_max_info->ptMaxSize.x = screen_rect.size.x;
min_max_info->ptMaxSize.y = screen_rect.size.y;
}
return 0;
}
} break;
Expand Down Expand Up @@ -3833,9 +3853,15 @@ 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) {
Engine *engine = Engine::get_singleton();
if (((lParam >> 16) <= 0) && !engine->is_project_manager_hint() && !engine->is_editor_hint() && !GLOBAL_GET("application/run/enable_alt_space_menu")) {
return 0;
}
if (!alt_mem || !(GetAsyncKeyState(VK_SPACE) & (1 << 15))) {
return 0;
}
SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_SPACE, 0);
SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_MENU, 0);
}
} break;
case WM_INDICATOR_CALLBACK_MESSAGE: {
Expand Down Expand Up @@ -4569,10 +4595,23 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
window.minimized = true;
} else if (IsZoomed(hWnd)) {
window.maximized = true;

// If maximized_window_size == screen_size add 1px border to prevent switching to exclusive_fs.
if (!window.maximized_fs && window.borderless && window_rect.position == screen_position && window_rect.size == screen_size) {
// Window (borderless) was just maximized and the covers the entire screen.
window.maximized_fs = true;
_update_window_style(window_id, false);
}
} else if (window_rect.position == screen_position && window_rect.size == screen_size) {
window.fullscreen = true;
}

if (window.maximized_fs && !window.maximized) {
// Window (maximized and covering fullscreen) was just non-maximized.
window.maximized_fs = false;
_update_window_style(window_id, false);
}

if (!window.minimized) {
window.width = window_client_rect.size.width;
window.height = window_client_rect.size.height;
Expand Down Expand Up @@ -5051,7 +5090,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
DWORD dwExStyle;
DWORD dwStyle;

_get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);
_get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);
Copy link
Contributor Author

@ManpreetXSingh ManpreetXSingh Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maximized_fs is handled inside WM_WINDOWPOSCHANGED [This is required because maximization could occur through external means (via the menu) which bypasses our state management logic at window_set_mode👍].

In case needed the following can be used here instead of false (not needed for now because it is handled at WM_WINDOWPOSCHANGED):

bool is_maximized_fs = (p_mode == WINDOW_MODE_MAXIMIZED) && (p_flags & WINDOW_FLAG_BORDERLESS_BIT) && (screen_get_size(rq_screen) == screen_get_usable_rect(rq_screen).size) && (screen_get_position(rq_screen) == screen_get_usable_rect(rq_screen).position);


RECT WindowRect;

Expand Down Expand Up @@ -5284,6 +5323,12 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
wd.height = p_rect.size.height;
}

// Set size of maximized borderless window (by default it covers the entire screen).
if (p_mode == WINDOW_MODE_MAXIMIZED && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {
Rect2i srect = screen_get_usable_rect(rq_screen);
SetWindowPos(wd.hWnd, HWND_TOP, srect.position.x, srect.position.y, srect.size.width, srect.size.height, SWP_NOZORDER | SWP_NOACTIVATE);
}

window_id_counter++;
}

Expand Down
3 changes: 2 additions & 1 deletion platform/windows/display_server_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ class DisplayServerWindows : public DisplayServer {
bool pre_fs_valid = false;
RECT pre_fs_rect;
bool maximized = false;
bool maximized_fs = false;
bool minimized = false;
bool fullscreen = false;
bool multiwindow_fs = false;
Expand Down Expand Up @@ -472,7 +473,7 @@ class DisplayServerWindows : public DisplayServer {
HashMap<IndicatorID, IndicatorData> indicators;

void _send_window_event(const WindowData &wd, WindowEvent p_event);
void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);

MouseMode mouse_mode;
int restore_mouse_trails = 0;
Expand Down
Loading