From a84cb609948acb01452e91b639c6547b47fe91db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Fri, 5 Jan 2018 20:10:52 +0100 Subject: [PATCH 1/3] Unify X11 fullscreen setup --- platform/x11/os_x11.cpp | 51 +++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index d68a9154c55..f6ced030c9a 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -263,33 +263,7 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au // borderless fullscreen window mode if (current_videomode.fullscreen) { - // needed for lxde/openbox, possibly others - Hints hints; - Atom property; - hints.flags = 2; - hints.decorations = 0; - property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); - XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); - XMapRaised(x11_display, x11_window); - XWindowAttributes xwa; - XGetWindowAttributes(x11_display, DefaultRootWindow(x11_display), &xwa); - XMoveResizeWindow(x11_display, x11_window, 0, 0, xwa.width, xwa.height); - - // code for netwm-compliants - XEvent xev; - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = 1; - xev.xclient.data.l[1] = fullscreen; - xev.xclient.data.l[2] = 0; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureNotifyMask, &xev); + set_wm_fullscreen(true); } // disable resizable window @@ -702,21 +676,34 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) { XFree(xsh); } - // Using EWMH -- Extened Window Manager Hints + // needed for lxde/openbox, possibly others + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = 0; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + XMapRaised(x11_display, x11_window); + XWindowAttributes xwa; + XGetWindowAttributes(x11_display, DefaultRootWindow(x11_display), &xwa); + XMoveResizeWindow(x11_display, x11_window, 0, 0, xwa.width, xwa.height); + + // code for netwm-compliants XEvent xev; Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); + Atom fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); memset(&xev, 0, sizeof(xev)); xev.type = ClientMessage; xev.xclient.window = x11_window; xev.xclient.message_type = wm_state; xev.xclient.format = 32; - xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; - xev.xclient.data.l[1] = wm_fullscreen; + xev.xclient.data.l[0] = 1; + xev.xclient.data.l[1] = fullscreen; xev.xclient.data.l[2] = 0; - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureNotifyMask, &xev); + XFlush(x11_display); if (!p_enabled && !is_window_resizable()) { From 554ffdcde7544c64f6634be8fd002b3232b75115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Fri, 5 Jan 2018 20:19:27 +0100 Subject: [PATCH 2/3] Add new window setting: always on top Implemented for Windows and Linux. --- core/bind/core_bind.cpp | 10 ++++++ core/bind/core_bind.h | 2 ++ core/os/os.h | 6 +++- main/main.cpp | 12 ++++++- platform/windows/os_windows.cpp | 19 +++++++++++ platform/windows/os_windows.h | 2 ++ platform/x11/os_x11.cpp | 57 ++++++++++++++++++++++++++++++++- platform/x11/os_x11.h | 3 ++ 8 files changed, 108 insertions(+), 3 deletions(-) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index ae6ee898253..da010c6f912 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -297,6 +297,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); } @@ -1031,6 +1039,8 @@ void _OS::_bind_methods() { ObjectTypeDB::bind_method(_MD("is_window_minimized"), &_OS::is_window_minimized); ObjectTypeDB::bind_method(_MD("set_window_maximized", "enabled"), &_OS::set_window_maximized); ObjectTypeDB::bind_method(_MD("is_window_maximized"), &_OS::is_window_maximized); + ObjectTypeDB::bind_method(_MD("set_window_always_on_top", "enabled"), &_OS::set_window_always_on_top); + ObjectTypeDB::bind_method(_MD("is_window_always_on_top"), &_OS::is_window_always_on_top); ObjectTypeDB::bind_method(_MD("request_attention"), &_OS::request_attention); ObjectTypeDB::bind_method(_MD("set_borderless_window", "borderless"), &_OS::set_borderless_window); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 23798944c38..6410a6dcb32 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -151,6 +151,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/core/os/os.h b/core/os/os.h index 07f9c2ef3f6..d02717a3e61 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -78,13 +78,15 @@ public: bool fullscreen; bool resizable; bool borderless_window; + bool always_on_top; 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) { + VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_always_on_top = false) { width = p_width; height = p_height; fullscreen = p_fullscreen; resizable = p_resizable; borderless_window = p_borderless_window; + always_on_top = p_always_on_top; } }; @@ -177,6 +179,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 set_borderless_window(int p_borderless) {} diff --git a/main/main.cpp b/main/main.cpp index 317757b6056..4961761c96f 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -93,6 +93,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; @@ -153,6 +154,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print("\t-f : Request fullscreen.\n"); OS::get_singleton()->print("\t-mx : Request maximized.\n"); OS::get_singleton()->print("\t-w : Request windowed.\n"); + OS::get_singleton()->print("\t-t : Request always-on-top.\n"); OS::get_singleton()->print("\t-vd : Video driver ("); for (int i = 0; i < OS::get_singleton()->get_video_driver_count(); i++) { @@ -425,8 +427,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "-f") { // fullscreen - //video_mode.fullscreen=false; init_fullscreen = true; + } else if (I->get() == "-t") { // always-on-top + + init_always_on_top = true; } else if (I->get() == "-e" || I->get() == "-editor") { // fonud editor editor = true; @@ -690,6 +694,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph video_mode.resizable = globals->get("display/resizable"); if (use_custom_res && globals->has("display/borderless_window")) video_mode.borderless_window = globals->get("display/borderless_window"); + if (use_custom_res && globals->has("display/always_on_top")) + video_mode.always_on_top = globals->get("display/always_on_top"); if (!force_res && use_custom_res && globals->has("display/test_width") && globals->has("display/test_height")) { int tw = globals->get("display/test_width"); @@ -706,6 +712,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF("display/fullscreen", video_mode.fullscreen); GLOBAL_DEF("display/resizable", video_mode.resizable); GLOBAL_DEF("display/borderless_window", video_mode.borderless_window); + GLOBAL_DEF("display/always_on_top", video_mode.always_on_top); use_vsync = GLOBAL_DEF("display/use_vsync", use_vsync); GLOBAL_DEF("display/test_width", 0); GLOBAL_DEF("display/test_height", 0); @@ -882,6 +889,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); + } MAIN_PRINT("Main: Load Remaps"); path_remap->load_remaps(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 4b290d04c18..f5b64114865 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1040,6 +1040,10 @@ void 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) || defined(GLES2_ENABLED) || defined(LEGACYGL_ENABLED) gl_context = memnew(ContextGL_Win(hWnd, false)); gl_context->initialize(); @@ -1644,6 +1648,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(int p_borderless) { if (video_mode.borderless_window == p_borderless) return; @@ -1668,6 +1685,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 0798bbfd812..4277baff0cd 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -224,6 +224,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(int p_borderless); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index f6ced030c9a..f55372ed7d7 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -263,7 +263,13 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au // borderless fullscreen window mode if (current_videomode.fullscreen) { - set_wm_fullscreen(true); + current_videomode.fullscreen = false; + set_window_fullscreen(true); + } + + if (current_videomode.always_on_top) { + current_videomode.always_on_top = false; + set_window_always_on_top(true); } // disable resizable window @@ -723,6 +729,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; + 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; @@ -886,7 +908,19 @@ void OS_X11::set_window_size(const Size2 p_size) { } void OS_X11::set_window_fullscreen(bool p_enabled) { + if (p_enabled == current_videomode.fullscreen) + 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; } @@ -1040,6 +1074,27 @@ bool OS_X11::is_window_maximized() const { return false; } +void OS_X11::set_window_always_on_top(bool p_enabled) { + if (current_videomode.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::request_attention() { // Using EWMH -- Extended Window Manager Hints // diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index e52a39e5960..2e16fe2a5c6 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -192,6 +192,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); @@ -267,6 +268,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 move_window_to_foreground(); From f43981e9a635ece7b517e7fbec1f32e0a539194a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Tue, 30 Jan 2018 20:40:12 +0100 Subject: [PATCH 3/3] Implement always-on-top for MacOS Courtesy of @bruvzg. --- platform/osx/os_osx.h | 2 ++ platform/osx/os_osx.mm | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 59b359e366e..22630a860d7 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -209,6 +209,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 String get_joy_guid(int p_device) const; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 3a21fe03799..fa0b615759e 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -1696,6 +1696,20 @@ void OS_OSX::move_window_to_foreground() { [window_object orderFrontRegardless]; } +void OS_OSX::set_window_always_on_top(bool p_enabled) { + if (is_window_always_on_top() == p_enabled) + return; + + if (p_enabled) + [window_object setLevel:NSFloatingWindowLevel]; + else + [window_object setLevel:NSNormalWindowLevel]; +} + +bool OS_OSX::is_window_always_on_top() const { + return [window_object level] == NSFloatingWindowLevel; +} + void OS_OSX::request_attention() { [NSApp requestUserAttention:NSCriticalRequest];