X11: use the window manager's native full-screen implementation when making windows full-screen

This matches the behavior on Cocoa and Wayland.
Fixes #1605
This commit is contained in:
Kovid Goyal 2019-05-12 15:53:06 +05:30
parent c27cf31a7c
commit 1fa9b8f102
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
12 changed files with 158 additions and 147 deletions

View File

@ -133,6 +133,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- 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]
------------------------------

View File

@ -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];

1
glfw/glfw3.h vendored
View File

@ -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.
*

1
glfw/internal.h vendored
View File

@ -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);

6
glfw/window.c vendored
View File

@ -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;

3
glfw/wl_platform.h vendored
View File

@ -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;

54
glfw/wl_window.c vendored
View File

@ -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);
}

93
glfw/x11_window.c vendored
View File

@ -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);
}
}
}

View File

@ -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 {

3
kitty/glfw-wrapper.c generated
View File

@ -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());

4
kitty/glfw-wrapper.h generated
View File

@ -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

View File

@ -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;
}