From 1c25e5049023acb0540f4bb7716cf77560136f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Wed, 27 Dec 2017 20:51:19 +0100 Subject: [PATCH] Add new window setting: always on top Implemented for Windows and Linux. (cherry picked from commit ee2c31d306e9333b520e0668f26807d876905241) --- core/bind/core_bind.cpp | 10 ++++++ core/bind/core_bind.h | 2 ++ core/os/os.h | 6 +++- main/main.cpp | 10 ++++++ platform/windows/os_windows.cpp | 19 ++++++++++++ platform/windows/os_windows.h | 2 ++ platform/x11/os_x11.cpp | 54 +++++++++++++++++++++++++++++++++ platform/x11/os_x11.h | 3 ++ 8 files changed, 105 insertions(+), 1 deletion(-) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 8f1a57e3638..b57b24ee7d0 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -304,6 +304,14 @@ bool _OS::is_window_maximized() const { return OS::get_singleton()->is_window_maximized(); } +void _OS::set_window_always_on_top(bool p_enabled) { + OS::get_singleton()->set_window_always_on_top(p_enabled); +} + +bool _OS::is_window_always_on_top() const { + return OS::get_singleton()->is_window_always_on_top(); +} + void _OS::set_borderless_window(bool p_borderless) { OS::get_singleton()->set_borderless_window(p_borderless); } @@ -1025,6 +1033,8 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("is_window_minimized"), &_OS::is_window_minimized); ClassDB::bind_method(D_METHOD("set_window_maximized", "enabled"), &_OS::set_window_maximized); ClassDB::bind_method(D_METHOD("is_window_maximized"), &_OS::is_window_maximized); + ClassDB::bind_method(D_METHOD("set_window_always_on_top", "enabled"), &_OS::set_window_always_on_top); + ClassDB::bind_method(D_METHOD("is_window_always_on_top"), &_OS::is_window_always_on_top); ClassDB::bind_method(D_METHOD("request_attention"), &_OS::request_attention); ClassDB::bind_method(D_METHOD("get_real_window_size"), &_OS::get_real_window_size); ClassDB::bind_method(D_METHOD("center_window"), &_OS::center_window); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 6b688a65f81..734b57937a3 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -165,6 +165,8 @@ public: virtual bool is_window_minimized() const; virtual void set_window_maximized(bool p_enabled); virtual bool is_window_maximized() const; + virtual void set_window_always_on_top(bool p_enabled); + virtual bool is_window_always_on_top() const; virtual void request_attention(); virtual void center_window(); diff --git a/core/os/os.h b/core/os/os.h index 1ec488df062..38e55fa3b7e 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -94,15 +94,17 @@ public: bool resizable; bool borderless_window; bool maximized; + bool always_on_top; bool use_vsync; float get_aspect() const { return (float)width / (float)height; } - VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_use_vsync = false) { + VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false) { width = p_width; height = p_height; fullscreen = p_fullscreen; resizable = p_resizable; borderless_window = p_borderless_window; maximized = p_maximized; + always_on_top = p_always_on_top; use_vsync = p_use_vsync; } }; @@ -192,6 +194,8 @@ public: virtual bool is_window_minimized() const { return false; } virtual void set_window_maximized(bool p_enabled) {} virtual bool is_window_maximized() const { return true; } + virtual void set_window_always_on_top(bool p_enabled) {} + virtual bool is_window_always_on_top() const { return false; } virtual void request_attention() {} virtual void center_window(); diff --git a/main/main.cpp b/main/main.cpp index 290ab7e65df..b6866e6198c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -106,6 +106,7 @@ static OS::VideoMode video_mode; static bool init_maximized = false; static bool init_windowed = false; static bool init_fullscreen = false; +static bool init_always_on_top = false; static bool init_use_custom_pos = false; #ifdef DEBUG_ENABLED static bool debug_collisions = false; @@ -233,6 +234,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" -f, --fullscreen Request fullscreen mode.\n"); OS::get_singleton()->print(" -m, --maximized Request a maximized window.\n"); OS::get_singleton()->print(" -w, --windowed Request windowed mode.\n"); + OS::get_singleton()->print(" -t, --always-on-top Request an always-on-top window.\n"); OS::get_singleton()->print(" --resolution x Request window resolution.\n"); OS::get_singleton()->print(" --position , Request window position.\n"); OS::get_singleton()->print(" --low-dpi Force low-DPI mode (macOS and Windows only).\n"); @@ -441,6 +443,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "-w" || I->get() == "--windowed") { // force windowed window init_windowed = true; + } else if (I->get() == "-t" || I->get() == "--always-on-top") { // force always-on-top window + + init_always_on_top = true; } else if (I->get() == "--profiling") { // enable profiling use_debug_profiler = true; @@ -851,6 +856,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF("display/window/size/resizable", true); GLOBAL_DEF("display/window/size/borderless", false); GLOBAL_DEF("display/window/size/fullscreen", false); + GLOBAL_DEF("display/window/size/always_on_top", false); GLOBAL_DEF("display/window/size/test_width", 0); GLOBAL_DEF("display/window/size/test_height", 0); @@ -873,6 +879,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph video_mode.resizable = GLOBAL_GET("display/window/size/resizable"); video_mode.borderless_window = GLOBAL_GET("display/window/size/borderless"); video_mode.fullscreen = GLOBAL_GET("display/window/size/fullscreen"); + video_mode.always_on_top = GLOBAL_GET("display/window/size/always_on_top"); } if (!force_lowdpi) { @@ -1078,6 +1085,9 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } else if (init_fullscreen) { OS::get_singleton()->set_window_fullscreen(true); } + if (init_always_on_top) { + OS::get_singleton()->set_window_always_on_top(true); + } register_server_types(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index ee86a594bcf..5770a67ba84 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1084,6 +1084,10 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int } }; + if (video_mode.always_on_top) { + SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + #if defined(OPENGL_ENABLED) gl_context = memnew(ContextGL_Win(hWnd, true)); gl_context->initialize(); @@ -1623,6 +1627,19 @@ bool OS_Windows::is_window_maximized() const { return maximized; } +void OS_Windows::set_window_always_on_top(bool p_enabled) { + if (video_mode.always_on_top == p_enabled) + return; + + video_mode.always_on_top = p_enabled; + + _update_window_style(); +} + +bool OS_Windows::is_window_always_on_top() const { + return video_mode.always_on_top; +} + void OS_Windows::set_borderless_window(bool p_borderless) { if (video_mode.borderless_window == p_borderless) return; @@ -1647,6 +1664,8 @@ void OS_Windows::_update_window_style(bool repaint) { } } + SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + if (repaint) { RECT rect; GetWindowRect(hWnd, &rect); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 70d269adb6e..479055facfa 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -211,6 +211,8 @@ public: virtual bool is_window_minimized() const; virtual void set_window_maximized(bool p_enabled); virtual bool is_window_maximized() const; + virtual void set_window_always_on_top(bool p_enabled); + virtual bool is_window_always_on_top() const; virtual void request_attention(); virtual void set_borderless_window(bool p_borderless); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 448a1d58336..c26aaee94cf 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -334,6 +334,11 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a XFree(xsh); } + if (current_videomode.always_on_top) { + current_videomode.always_on_top = false; + set_window_always_on_top(true); + } + AudioDriverManager::initialize(p_audio_driver); ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); @@ -781,6 +786,22 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) { } } +void OS_X11::set_wm_above(bool p_enabled) { + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False); + + XClientMessageEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.window = x11_window; + xev.message_type = wm_state; + xev.format = 32; + xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.data.l[1] = wm_above; + xev.data.l[3] = 1; + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev); +} + int OS_X11::get_screen_count() const { // Using Xinerama Extension int event_base, error_base; @@ -975,7 +996,19 @@ void OS_X11::set_window_size(const Size2 p_size) { } void OS_X11::set_window_fullscreen(bool p_enabled) { + if (current_videomode.fullscreen == p_enabled) + return; + + if (p_enabled && current_videomode.always_on_top) { + // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity) + set_window_maximized(true); + } set_wm_fullscreen(p_enabled); + if (!p_enabled && !current_videomode.always_on_top) { + // Restore + set_window_maximized(false); + } + current_videomode.fullscreen = p_enabled; } @@ -1185,6 +1218,27 @@ bool OS_X11::is_window_maximized() const { return retval; } +void OS_X11::set_window_always_on_top(bool p_enabled) { + if (is_window_always_on_top() == p_enabled) + return; + + if (p_enabled && current_videomode.fullscreen) { + // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity) + set_window_maximized(true); + } + set_wm_above(p_enabled); + if (!p_enabled && !current_videomode.fullscreen) { + // Restore + set_window_maximized(false); + } + + current_videomode.always_on_top = p_enabled; +} + +bool OS_X11::is_window_always_on_top() const { + return current_videomode.always_on_top; +} + void OS_X11::set_borderless_window(bool p_borderless) { if (current_videomode.borderless_window == p_borderless) diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 3347b37f47d..4600d3fd76b 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -178,6 +178,7 @@ class OS_X11 : public OS_Unix { bool maximized; //void set_wm_border(bool p_enabled); void set_wm_fullscreen(bool p_enabled); + void set_wm_above(bool p_enabled); typedef xrr_monitor_info *(*xrr_get_monitors_t)(Display *dpy, Window window, Bool get_active, int *nmonitors); typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors); @@ -261,6 +262,8 @@ public: virtual bool is_window_minimized() const; virtual void set_window_maximized(bool p_enabled); virtual bool is_window_maximized() const; + virtual void set_window_always_on_top(bool p_enabled); + virtual bool is_window_always_on_top() const; virtual void request_attention(); virtual void set_borderless_window(bool p_borderless);