diff --git a/docs/changelog.rst b/docs/changelog.rst index eaeaa8e37..26d717995 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -133,6 +133,9 @@ To update |kitty|, :doc:`follow the instructions `. - When launching child processes set the :code:`PWD` environment variable (:iss:`1595`) +- X11: use the window manager's native full-screen implementation when + making windows full-screen (:iss:`1605`) + 0.13.3 [2019-01-19] ------------------------------ diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 61913c8c0..f41c96115 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -2001,6 +2001,29 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) updateCursorImage(window); } +bool _glfwPlatformToggleFullscreen(_GLFWwindow* w, unsigned int flags) { + NSWindow *window = w->ns.object; + bool made_fullscreen = true; + bool traditional = flags & 1; + NSWindowStyleMask sm = [window styleMask]; + bool in_fullscreen = sm & NSWindowStyleMaskFullScreen; + if (traditional) { + if (!(in_fullscreen)) { + sm |= NSWindowStyleMaskBorderless | NSWindowStyleMaskFullScreen; + [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock]; + } else { + made_fullscreen = false; + sm &= ~(NSWindowStyleMaskBorderless | NSWindowStyleMaskFullScreen); + [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationDefault]; + } + [window setStyleMask: sm]; + } else { + if (in_fullscreen) made_fullscreen = false; + [window toggleFullScreen: nil]; + } + return made_fullscreen; +} + void _glfwPlatformSetClipboardString(const char* string) { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 7e77e5bd9..7d6bbd75f 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -2521,6 +2521,7 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * @ingroup window */ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); +GLFWAPI bool glfwToggleFullscreen(GLFWwindow *window, unsigned int flags); /*! @brief Destroys the specified window and its context. * diff --git a/glfw/internal.h b/glfw/internal.h index 5d58acc69..7f037d1cb 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -673,6 +673,7 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window); void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); +bool _glfwPlatformToggleFullscreen(_GLFWwindow *w, unsigned int flags); int _glfwPlatformWindowFocused(_GLFWwindow* window); int _glfwPlatformWindowOccluded(_GLFWwindow* window); int _glfwPlatformWindowIconified(_GLFWwindow* window); diff --git a/glfw/window.c b/glfw/window.c index 91de8ad92..5af23c510 100644 --- a/glfw/window.c +++ b/glfw/window.c @@ -1017,6 +1017,12 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, refreshRate); } +GLFWAPI bool glfwToggleFullscreen(GLFWwindow* wh, unsigned int flags) { + _GLFWwindow* window = (_GLFWwindow*) wh; + if (window) return _glfwPlatformToggleFullscreen(window, flags); + return false; +} + GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 66793a02a..3cf37cf5a 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -167,8 +167,7 @@ typedef struct _GLFWwindowWayland struct zwp_idle_inhibitor_v1* idleInhibitor; - // This is a hack to prevent auto-iconification on creation. - GLFWbool wasFullScreen; + GLFWbool fullscreened; struct { GLFWbool serverSide; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index bfb2ee6bd..3239c6940 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -475,17 +475,32 @@ static GLFWbool createSurface(_GLFWwindow* window, return GLFW_TRUE; } -static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, int refreshRate) +static void +setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on) { if (window->wl.xdg.toplevel) { - xdg_toplevel_set_fullscreen( - window->wl.xdg.toplevel, - monitor->wl.output); + if (on) { + xdg_toplevel_set_fullscreen( + window->wl.xdg.toplevel, + monitor ? monitor->wl.output : NULL); + if (!window->wl.decorations.serverSide) + destroyDecorations(window); + } else { + xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); + if (!_glfw.wl.decorationManager) + createDecorations(window); + } } - setIdleInhibitor(window, GLFW_TRUE); - if (!window->wl.decorations.serverSide) - destroyDecorations(window); + setIdleInhibitor(window, on); +} + +bool +_glfwPlatformToggleFullscreen(_GLFWwindow *window, unsigned int flags) { + (void) flags; + bool already_fullscreen = window->wl.fullscreened; + setFullscreen(window, NULL, !already_fullscreen); + return !already_fullscreen; } static void xdgToplevelHandleConfigure(void* data, @@ -535,18 +550,8 @@ static void xdgToplevelHandleConfigure(void* data, } } } + window->wl.fullscreened = fullscreen; dispatchChangesAfterConfigure(window, width, height); - - if (window->wl.wasFullScreen && window->autoIconify) - { - if (!activated || !fullscreen) - { - _glfwPlatformIconifyWindow(window); - window->wl.wasFullScreen = GLFW_FALSE; - } - } - if (fullscreen && activated) - window->wl.wasFullScreen = GLFW_TRUE; _glfwInputWindowFocus(window, activated); } @@ -1121,18 +1126,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, int width, int height, int refreshRate) { - if (monitor) - { - setFullscreen(window, monitor, refreshRate); - } - else - { - if (window->wl.xdg.toplevel) - xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); - setIdleInhibitor(window, GLFW_FALSE); - if (!_glfw.wl.decorationManager) - createDecorations(window); - } + setFullscreen(window, monitor, monitor != NULL); _glfwInputWindowMonitor(window, monitor); } diff --git a/glfw/x11_window.c b/glfw/x11_window.c index 04d14c39f..9c231e522 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -265,6 +265,76 @@ static void updateNormalHints(_GLFWwindow* window, int width, int height) XFree(hints); } +static inline bool +is_window_fullscreen(_GLFWwindow* window) +{ + Atom* states; + unsigned long i; + GLFWbool ans = GLFW_FALSE; + if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_FULLSCREEN) + return ans; + const unsigned long count = + _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_FULLSCREEN) + { + ans = GLFW_TRUE; + break; + } + } + + if (states) + XFree(states); + + return ans; +} + +static inline void +set_fullscreen(_GLFWwindow *window, bool on) { + if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + on ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + // Enable compositor bypass + if (!window->x11.transparent) + { + if (on) { + const unsigned long value = 1; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*) &value, 1); + } else { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_BYPASS_COMPOSITOR); + } + } + + } else { + static bool warned = false; + if (!warned) { + warned = true; + _glfwInputErrorX11(GLFW_PLATFORM_ERROR, + "X11: Failed to toggle fullscreen, the window manager does not support it"); + } + } +} + +bool +_glfwPlatformToggleFullscreen(_GLFWwindow *window, unsigned int flags) { + (void) flags; + bool already_fullscreen = is_window_fullscreen(window); + set_fullscreen(window, !already_fullscreen); + return !already_fullscreen; +} + // Updates the full screen status of the window // static void updateWindowMode(_GLFWwindow* window) @@ -285,11 +355,7 @@ static void updateWindowMode(_GLFWwindow* window) if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) { - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_ADD, - _glfw.x11.NET_WM_STATE_FULLSCREEN, - 0, 1, 0); + set_fullscreen(window, true); } else { @@ -311,15 +377,6 @@ static void updateWindowMode(_GLFWwindow* window) window->x11.overrideRedirect = GLFW_TRUE; } - // Enable compositor bypass - if (!window->x11.transparent) - { - const unsigned long value = 1; - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, - PropModeReplace, (unsigned char*) &value, 1); - } } else { @@ -332,11 +389,7 @@ static void updateWindowMode(_GLFWwindow* window) if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) { - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_REMOVE, - _glfw.x11.NET_WM_STATE_FULLSCREEN, - 0, 1, 0); + set_fullscreen(window, false); } else { @@ -353,8 +406,6 @@ static void updateWindowMode(_GLFWwindow* window) // Disable compositor bypass if (!window->x11.transparent) { - XDeleteProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_BYPASS_COMPOSITOR); } } } diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index fa878092f..527e81400 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -392,29 +392,6 @@ cocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t array_sz) { return ans; } -bool -cocoa_toggle_fullscreen(void *w, bool traditional) { - NSWindow *window = (NSWindow*)w; - bool made_fullscreen = true; - NSWindowStyleMask sm = [window styleMask]; - bool in_fullscreen = sm & NSWindowStyleMaskFullScreen; - if (traditional) { - if (!(in_fullscreen)) { - sm |= NSWindowStyleMaskBorderless | NSWindowStyleMaskFullScreen; - [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock]; - } else { - made_fullscreen = false; - sm &= ~(NSWindowStyleMaskBorderless | NSWindowStyleMaskFullScreen); - [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationDefault]; - } - [window setStyleMask: sm]; - } else { - if (in_fullscreen) made_fullscreen = false; - [window toggleFullScreen: nil]; - } - return made_fullscreen; -} - static PyObject* cocoa_get_lang(PyObject UNUSED *self) { @autoreleasepool { diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 5970e085a..8b5cd08e1 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -110,6 +110,9 @@ load_glfw(const char* path) { *(void **) (&glfwCreateWindow_impl) = dlsym(handle, "glfwCreateWindow"); if (glfwCreateWindow_impl == NULL) fail("Failed to load glfw function glfwCreateWindow with error: %s", dlerror()); + *(void **) (&glfwToggleFullscreen_impl) = dlsym(handle, "glfwToggleFullscreen"); + if (glfwToggleFullscreen_impl == NULL) fail("Failed to load glfw function glfwToggleFullscreen with error: %s", dlerror()); + *(void **) (&glfwDestroyWindow_impl) = dlsym(handle, "glfwDestroyWindow"); if (glfwDestroyWindow_impl == NULL) fail("Failed to load glfw function glfwDestroyWindow with error: %s", dlerror()); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 06c48bc85..a9c8ecb59 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1536,6 +1536,10 @@ typedef GLFWwindow* (*glfwCreateWindow_func)(int, int, const char*, GLFWmonitor* glfwCreateWindow_func glfwCreateWindow_impl; #define glfwCreateWindow glfwCreateWindow_impl +typedef bool (*glfwToggleFullscreen_func)(GLFWwindow*, unsigned int); +glfwToggleFullscreen_func glfwToggleFullscreen_impl; +#define glfwToggleFullscreen glfwToggleFullscreen_impl + typedef void (*glfwDestroyWindow_func)(GLFWwindow*); glfwDestroyWindow_func glfwDestroyWindow_impl; #define glfwDestroyWindow glfwDestroyWindow_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index 2f1317323..1ce29a726 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -10,7 +10,6 @@ #include "glfw-wrapper.h" extern bool cocoa_make_window_resizable(void *w, bool); extern void cocoa_focus_window(void *w); -extern bool cocoa_toggle_fullscreen(void *w, bool); extern void cocoa_create_global_menu(void); extern void cocoa_set_hide_from_tasks(void); extern void cocoa_set_titlebar_color(void *w, color_type color); @@ -330,43 +329,6 @@ make_os_window_context_current(OSWindow *w) { } -#ifndef __APPLE__ -static GLFWmonitor* -current_monitor(GLFWwindow *window) { - // Find the monitor that has the maximum overlap with this window - int nmonitors, i; - int wx, wy, ww, wh; - int mx, my, mw, mh; - int overlap = 0, bestoverlap = 0; - GLFWmonitor *bestmonitor = NULL; - GLFWmonitor **monitors = NULL; - const GLFWvidmode *mode; - - glfwGetWindowPos(window, &wx, &wy); - glfwGetWindowSize(window, &ww, &wh); - monitors = glfwGetMonitors(&nmonitors); - if (monitors == NULL || nmonitors < 1) { PyErr_SetString(PyExc_ValueError, "No monitors connected"); return NULL; } - - for (i = 0; i < nmonitors; i++) { - mode = glfwGetVideoMode(monitors[i]); - glfwGetMonitorPos(monitors[i], &mx, &my); - mw = mode->width; - mh = mode->height; - - overlap = - MAX(0, MIN(wx + ww, mx + mw) - MAX(wx, mx)) * - MAX(0, MIN(wy + wh, my + mh) - MAX(wy, my)); - - if (bestoverlap < overlap || bestmonitor == NULL) { - bestoverlap = overlap; - bestmonitor = monitors[i]; - } - } - - return bestmonitor; -} -#endif - static inline void get_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xdpi, double *ydpi) { if (w) glfwGetWindowContentScale(w, xscale, yscale); @@ -397,47 +359,34 @@ set_os_window_dpi(OSWindow *w) { get_window_dpi(w->handle, &w->logical_dpi_x, &w->logical_dpi_y); } -static bool -toggle_fullscreen_for_os_window(OSWindow *w) { +static inline bool +do_toggle_fullscreen(OSWindow *w) { int width, height, x, y; glfwGetWindowSize(w->handle, &width, &height); glfwGetWindowPos(w->handle, &x, &y); -#ifdef __APPLE__ - if (OPT(macos_traditional_fullscreen)) { - if (cocoa_toggle_fullscreen(glfwGetCocoaWindow(w->handle), true)) { - w->before_fullscreen.is_set = true; - w->before_fullscreen.w = width; w->before_fullscreen.h = height; w->before_fullscreen.x = x; w->before_fullscreen.y = y; - return true; - } - if (w->before_fullscreen.is_set) { - glfwSetWindowSize(w->handle, w->before_fullscreen.w, w->before_fullscreen.h); - glfwSetWindowPos(w->handle, w->before_fullscreen.x, w->before_fullscreen.y); - } - return false; - } else { - return cocoa_toggle_fullscreen(glfwGetCocoaWindow(w->handle), false); - } -#else - GLFWmonitor *monitor; - if ((monitor = glfwGetWindowMonitor(w->handle)) == NULL) { - // make fullscreen - monitor = current_monitor(w->handle); - if (monitor == NULL) { PyErr_Print(); return false; } - const GLFWvidmode* mode = glfwGetVideoMode(monitor); + if (glfwToggleFullscreen(w->handle, 1)) { w->before_fullscreen.is_set = true; w->before_fullscreen.w = width; w->before_fullscreen.h = height; w->before_fullscreen.x = x; w->before_fullscreen.y = y; - glfwGetWindowSize(w->handle, &w->before_fullscreen.w, &w->before_fullscreen.h); - glfwGetWindowPos(w->handle, &w->before_fullscreen.x, &w->before_fullscreen.y); - glfwSetWindowMonitor(w->handle, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); return true; - } else { - // make windowed - const GLFWvidmode* mode = glfwGetVideoMode(monitor); - if (w->before_fullscreen.is_set) glfwSetWindowMonitor(w->handle, NULL, w->before_fullscreen.x, w->before_fullscreen.y, w->before_fullscreen.w, w->before_fullscreen.h, mode->refreshRate); - else glfwSetWindowMonitor(w->handle, NULL, 0, 0, 600, 400, mode->refreshRate); - return false; } + if (w->before_fullscreen.is_set) { + glfwSetWindowSize(w->handle, w->before_fullscreen.w, w->before_fullscreen.h); + glfwSetWindowPos(w->handle, w->before_fullscreen.x, w->before_fullscreen.y); + } + return false; +} + +static bool +toggle_fullscreen_for_os_window(OSWindow *w) { + if (w && w->handle) { +#ifdef __APPLE__ + if (!OPT(macos_traditional_fullscreen) return glfwToggleFullscreen(w->handle, 0); + return do_toggle_fullscreen(w); +#else + return do_toggle_fullscreen(w); #endif + } + return false; }