diff --git a/README.md b/README.md index 520073d8..d02b96fb 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ GLFW bundles a number of dependencies in the `deps/` directory. - Added `GLFW_DONT_CARE` for indicating that any value is acceptable - Added `GLFW_DOUBLEBUFFER` for controlling whether to use double buffering - Added `GLFW_INCLUDE_ES31` for including the OpenGL ES 3.1 header + - Added `GLFW_FLOATING` for creating always-on-top windowed mode windows - Added *partial and experimental* support for Wayland - Bugfix: The debug context attribute was set from `GL_ARB_debug_output` even when a debug context had not been requested diff --git a/docs/news.dox b/docs/news.dox index c45021bd..ae750ee7 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -41,6 +41,12 @@ building multi-monitor installations, where you need windows to stay in full screen despite losing focus. +@subsection news_31_floating Floating windows + +GLFW not supports floating windows, also called topmost or always on top, for +easier debugging, with the `GLFW_FLOATING` window hint. + + @section news_30 New features in version 3.0 @subsection news_30_cmake CMake build system diff --git a/docs/window.dox b/docs/window.dox index a80b698f..da822ca3 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -122,6 +122,11 @@ The `GLFW_AUTO_ICONIFY` hint specifies whether the (full screen) window will automatically iconify and restore the previous video mode on focus loss. This hint is ignored for windowed mode windows. +The `GLFW_FLOATING` hint specifies whether the window will be floating above +other regular windows, also called topmost or always-on-top. This is intended +primarily for debugging purposes and cannot be used to implement proper full +screen windows. This hint is ignored for full screen windows. + @subsection window_hints_fb Framebuffer related hints @@ -211,6 +216,7 @@ a robustness strategy. | `GLFW_VISIBLE` | `GL_TRUE` | `GL_TRUE` or `GL_FALSE` | | `GLFW_DECORATED` | `GL_TRUE` | `GL_TRUE` or `GL_FALSE` | | `GLFW_AUTO_ICONIFY` | `GL_TRUE` | `GL_TRUE` or `GL_FALSE` | +| `GLFW_FLOATING` | `GL_FALSE` | `GL_TRUE` or `GL_FALSE` | | `GLFW_RED_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` | | `GLFW_GREEN_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` | | `GLFW_BLUE_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` | @@ -445,6 +451,10 @@ The `GLFW_DECORATED` attribute indicates whether the specified window has decorations such as a border, a close widget, etc. This is controlled by the [window hint](@ref window_hints) with the same name. +The `GLFW_FLOATING` attribute indicates whether the specified window is +floating, also called topmost or always-on-top. This is controlled by the +[window hint](@ref window_hints) with the same name. + @subsection window_attribs_context Context attributes diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index e72c7db3..04dc5ca7 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -498,6 +498,7 @@ extern "C" { #define GLFW_VISIBLE 0x00020004 #define GLFW_DECORATED 0x00020005 #define GLFW_AUTO_ICONIFY 0x00020006 +#define GLFW_FLOATING 0x00020007 #define GLFW_RED_BITS 0x00021001 #define GLFW_GREEN_BITS 0x00021002 diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 723d3ff9..4c876c48 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -972,6 +972,9 @@ static GLboolean createWindow(_GLFWwindow* window, } #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + if (wndconfig->floating) + [window->ns.object setLevel:NSFloatingWindowLevel]; + [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; [window->ns.object setContentView:window->ns.view]; [window->ns.object setDelegate:window->ns.delegate]; diff --git a/src/internal.h b/src/internal.h index f0d1d60b..7ce894d1 100644 --- a/src/internal.h +++ b/src/internal.h @@ -153,6 +153,7 @@ struct _GLFWwndconfig GLboolean visible; GLboolean decorated; GLboolean autoIconify; + GLboolean floating; _GLFWmonitor* monitor; }; @@ -218,6 +219,7 @@ struct _GLFWwindow GLboolean resizable; GLboolean decorated; GLboolean autoIconify; + GLboolean floating; GLboolean visible; GLboolean closed; void* userPointer; @@ -323,6 +325,7 @@ struct _GLFWlibrary int visible; int decorated; int autoIconify; + int floating; int samples; int sRGB; int refreshRate; diff --git a/src/win32_window.c b/src/win32_window.c index 3ff59ff9..5c7c91a0 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -951,6 +951,14 @@ static int createWindow(_GLFWwindow* window, WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); } + if (wndconfig->floating && !wndconfig->monitor) + { + SetWindowPos(window->win32.handle, + HWND_TOPMOST, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + } + DragAcceptFiles(window->win32.handle, TRUE); if (!_glfwCreateContext(window, ctxconfig, fbconfig)) diff --git a/src/window.c b/src/window.c index bafe0b70..6dcc4cd2 100644 --- a/src/window.c +++ b/src/window.c @@ -167,6 +167,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, wndconfig.visible = _glfw.hints.visible ? GL_TRUE : GL_FALSE; wndconfig.decorated = _glfw.hints.decorated ? GL_TRUE : GL_FALSE; wndconfig.autoIconify = _glfw.hints.autoIconify ? GL_TRUE : GL_FALSE; + wndconfig.floating = _glfw.hints.floating ? GL_TRUE : GL_FALSE; wndconfig.monitor = (_GLFWmonitor*) monitor; // Set up desired context config @@ -205,6 +206,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->resizable = wndconfig.resizable; window->decorated = wndconfig.decorated; window->autoIconify = wndconfig.autoIconify; + window->floating = wndconfig.floating; window->cursorMode = GLFW_CURSOR_NORMAL; // Save the currently current context so it can be restored later @@ -335,6 +337,9 @@ GLFWAPI void glfwWindowHint(int target, int hint) case GLFW_AUTO_ICONIFY: _glfw.hints.autoIconify = hint; break; + case GLFW_FLOATING: + _glfw.hints.floating = hint; + break; case GLFW_VISIBLE: _glfw.hints.visible = hint; break; @@ -585,6 +590,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->resizable; case GLFW_DECORATED: return window->decorated; + case GLFW_FLOATING: + return window->floating; case GLFW_VISIBLE: return window->visible; case GLFW_CLIENT_API: diff --git a/src/x11_init.c b/src/x11_init.c index 95ea718c..b891f63a 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -419,6 +419,8 @@ static void detectEWMH(void) // See which of the atoms we support that are supported by the WM _glfw.x11.NET_WM_STATE = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE"); + _glfw.x11.NET_WM_STATE_ABOVE = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); _glfw.x11.NET_WM_STATE_FULLSCREEN = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); _glfw.x11.NET_WM_NAME = diff --git a/src/x11_platform.h b/src/x11_platform.h index 4193ba0e..103e7679 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -122,6 +122,7 @@ typedef struct _GLFWlibraryX11 Atom NET_WM_PID; Atom NET_WM_PING; Atom NET_WM_STATE; + Atom NET_WM_STATE_ABOVE; Atom NET_WM_STATE_FULLSCREEN; Atom NET_WM_BYPASS_COMPOSITOR; Atom NET_ACTIVE_WINDOW; diff --git a/src/x11_window.c b/src/x11_window.c index 9f02246e..6cb38e8b 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -108,6 +108,29 @@ static int translateChar(XKeyEvent* event) return (int) _glfwKeySym2Unicode(keysym); } +// Adds or removes an EWMH state to a window +// +static void changeWindowState(_GLFWwindow* window, Atom state, int action) +{ + XEvent event; + memset(&event, 0, sizeof(event)); + + event.type = ClientMessage; + event.xclient.window = window->x11.handle; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = _glfw.x11.NET_WM_STATE; + event.xclient.data.l[0] = action; + event.xclient.data.l[1] = state; + event.xclient.data.l[2] = 0; // No secondary property + event.xclient.data.l[3] = 1; // Sender is a normal application + + XSendEvent(_glfw.x11.display, + _glfw.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); +} + // Splits and translates a text/uri-list into separate file paths // static char** parseUriList(char* text, int* count) @@ -382,6 +405,16 @@ static GLboolean createWindow(_GLFWwindow* window, XIfEvent(_glfw.x11.display, &event, isFrameExtentsEvent, (XPointer) window); } + if (wndconfig->floating && !wndconfig->monitor) + { + if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_ABOVE) + { + changeWindowState(window, + _glfw.x11.NET_WM_STATE_ABOVE, + _NET_WM_STATE_ADD); + } + } + _glfwPlatformSetWindowTitle(window, wndconfig->title); XRRSelectInput(_glfw.x11.display, window->x11.handle, @@ -490,23 +523,9 @@ static void enterFullscreenMode(_GLFWwindow* window) // Fullscreen windows are undecorated and, when focused, are kept // on top of all other windows - XEvent event; - memset(&event, 0, sizeof(event)); - - event.type = ClientMessage; - event.xclient.window = window->x11.handle; - event.xclient.format = 32; // Data is 32-bit longs - event.xclient.message_type = _glfw.x11.NET_WM_STATE; - event.xclient.data.l[0] = _NET_WM_STATE_ADD; - event.xclient.data.l[1] = _glfw.x11.NET_WM_STATE_FULLSCREEN; - event.xclient.data.l[2] = 0; // No secondary property - event.xclient.data.l[3] = 1; // Sender is a normal application - - XSendEvent(_glfw.x11.display, - _glfw.x11.root, - False, - SubstructureNotifyMask | SubstructureRedirectMask, - &event); + changeWindowState(window, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + _NET_WM_STATE_ADD); } else if (window->x11.overrideRedirect) { @@ -557,23 +576,9 @@ static void leaveFullscreenMode(_GLFWwindow* window) // Ask the window manager to make the GLFW window a normal window // Normal windows usually have frames and other decorations - XEvent event; - memset(&event, 0, sizeof(event)); - - event.type = ClientMessage; - event.xclient.window = window->x11.handle; - event.xclient.format = 32; // Data is 32-bit longs - event.xclient.message_type = _glfw.x11.NET_WM_STATE; - event.xclient.data.l[0] = _NET_WM_STATE_REMOVE; - event.xclient.data.l[1] = _glfw.x11.NET_WM_STATE_FULLSCREEN; - event.xclient.data.l[2] = 0; // No secondary property - event.xclient.data.l[3] = 1; // Sender is a normal application - - XSendEvent(_glfw.x11.display, - _glfw.x11.root, - False, - SubstructureNotifyMask | SubstructureRedirectMask, - &event); + changeWindowState(window, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + _NET_WM_STATE_REMOVE); } }