diff --git a/docs/changelog.rst b/docs/changelog.rst index bde7fb038..6ba2d2d52 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -25,6 +25,11 @@ Changelog on macs thicker, which makes it similar to the result of sub-pixel antialiasing (:pull:`950`) +- macOS: Make full screening of kitty much faster by using the "traditional full + screen" mode of cocoa, similar to iTerm and MacVim (:iss:`911`). Also should + hopefully workaround a bug in glfw that causes crashes when unplugging + monitors with full screen windows. (:iss:`898`) + - Fix drag-scrolling not working when the mouse leaves the window confines (:iss:`917`) diff --git a/glfw/cocoa_platform.h b/glfw/cocoa_platform.h index 692cc4603..620d25153 100644 --- a/glfw/cocoa_platform.h +++ b/glfw/cocoa_platform.h @@ -39,6 +39,7 @@ typedef void* id; typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; typedef int (* GLFWcocoatextinputfilterfun)(int,int,int); typedef int (* GLFWapplicationshouldhandlereopenfun)(int); +typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*); typedef struct VkMacOSSurfaceCreateInfoMVK { @@ -102,6 +103,8 @@ typedef struct _GLFWwindowNS // The text input filter callback GLFWcocoatextinputfilterfun textInputFilterCallback; + // The toggle fullscreen intercept callback + GLFWcocoatogglefullscreenfun toggleFullscreenCallback; // Dead key state UInt32 deadKeyState; } _GLFWwindowNS; diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 5f8b355a4..a6d13acbe 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -563,6 +563,10 @@ static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL; [super dealloc]; } +- (_GLFWwindow*)glfwWindow { + return window; +} + - (BOOL)isOpaque { return [window->ns.object isOpaque]; @@ -1040,6 +1044,18 @@ is_ascii_control_char(char x) { return YES; } +- (void)toggleFullScreen:(nullable id)sender +{ + GLFWContentView *view = [self contentView]; + if (view) + { + _GLFWwindow *window = [view glfwWindow]; + if (window && window->ns.toggleFullscreenCallback && window->ns.toggleFullscreenCallback((GLFWwindow*)window) == 1) + return; + } + [super toggleFullScreen:sender]; +} + @end @@ -2114,6 +2130,14 @@ GLFWAPI GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow *hand return previous; } +GLFWAPI GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWwindow *handle, GLFWcocoatogglefullscreenfun callback) { + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(nil); + GLFWcocoatogglefullscreenfun previous = window->ns.toggleFullscreenCallback; + window->ns.toggleFullscreenCallback = callback; + return previous; +} + GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) { GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback; handle_reopen_callback = callback; diff --git a/glfw/glfw.py b/glfw/glfw.py index bed27d2d1..e34cb0ae4 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -194,6 +194,7 @@ def generate_wrappers(glfw_header, glfw_native_header): void* glfwGetCocoaWindow(GLFWwindow* window) uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor) GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback) + GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWwindow *window, GLFWcocoatogglefullscreenfun callback) GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, void* cocoa_key, void* cocoa_mods) void* glfwGetX11Display(void) @@ -213,11 +214,13 @@ def generate_wrappers(glfw_header, glfw_native_header): #pragma once #include #include -typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int); -typedef int (* GLFWapplicationshouldhandlereopenfun)(int); {} +typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int); +typedef int (* GLFWapplicationshouldhandlereopenfun)(int); +typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*); + {} const char* load_glfw(const char* path); diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index 5cd18090a..0f26ec0a0 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -224,6 +224,24 @@ cocoa_focus_window(void *w) { [window makeKeyWindow]; } +bool +cocoa_toggle_fullscreen(void *w) { + NSWindow *window = (NSWindow*)w; + NSWindowStyleMask sm = [window styleMask]; + bool made_fullscreen; + if (!(sm & NSWindowStyleMaskFullScreen)) { + made_fullscreen = true; + sm |= NSWindowStyleMaskBorderless | NSWindowStyleMaskFullScreen; + [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock]; + } else { + made_fullscreen = false; + sm &= ~(NSWindowStyleMaskBorderless | NSWindowStyleMaskFullScreen); + [[NSApplication sharedApplication] setPresentationOptions: NSApplicationPresentationDefault]; + } + [window setStyleMask: sm]; + return made_fullscreen; +} + static PyObject* cocoa_get_lang(PyObject UNUSED *self) { NSString* locale = nil; diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 78377181d..2e0d257f9 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -362,6 +362,8 @@ load_glfw(const char* path) { *(void **) (&glfwSetCocoaTextInputFilter_impl) = dlsym(handle, "glfwSetCocoaTextInputFilter"); + *(void **) (&glfwSetCocoaToggleFullscreenIntercept_impl) = dlsym(handle, "glfwSetCocoaToggleFullscreenIntercept"); + *(void **) (&glfwSetApplicationShouldHandleReopen_impl) = dlsym(handle, "glfwSetApplicationShouldHandleReopen"); *(void **) (&glfwGetCocoaKeyEquivalent_impl) = dlsym(handle, "glfwGetCocoaKeyEquivalent"); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 986ba529a..500a88099 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1,8 +1,6 @@ #pragma once #include #include -typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int); -typedef int (* GLFWapplicationshouldhandlereopenfun)(int); @@ -1386,6 +1384,10 @@ typedef struct GLFWgamepadstate */ +typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int); +typedef int (* GLFWapplicationshouldhandlereopenfun)(int); +typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*); + typedef int (*glfwInit_func)(); glfwInit_func glfwInit_impl; #define glfwInit glfwInit_impl @@ -1854,6 +1856,10 @@ typedef GLFWcocoatextinputfilterfun (*glfwSetCocoaTextInputFilter_func)(GLFWwind glfwSetCocoaTextInputFilter_func glfwSetCocoaTextInputFilter_impl; #define glfwSetCocoaTextInputFilter glfwSetCocoaTextInputFilter_impl +typedef GLFWcocoatogglefullscreenfun (*glfwSetCocoaToggleFullscreenIntercept_func)(GLFWwindow*, GLFWcocoatogglefullscreenfun); +glfwSetCocoaToggleFullscreenIntercept_func glfwSetCocoaToggleFullscreenIntercept_impl; +#define glfwSetCocoaToggleFullscreenIntercept glfwSetCocoaToggleFullscreenIntercept_impl + typedef GLFWapplicationshouldhandlereopenfun (*glfwSetApplicationShouldHandleReopen_func)(GLFWapplicationshouldhandlereopenfun); glfwSetApplicationShouldHandleReopen_func glfwSetApplicationShouldHandleReopen_impl; #define glfwSetApplicationShouldHandleReopen glfwSetApplicationShouldHandleReopen_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index 6a32b817c..cec4cea05 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -10,6 +10,7 @@ #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); 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); @@ -355,11 +356,55 @@ 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) { + int width, height, x, y; + glfwGetWindowSize(w->handle, &width, &height); + glfwGetWindowPos(w->handle, &x, &y); +#ifdef __APPLE__ + if (cocoa_toggle_fullscreen(glfwGetCocoaWindow(w->handle))) { + 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 + 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); + 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); +#ifdef __APPLE__ + if (glfwGetCocoaWindow) cocoa_make_window_resizable(glfwGetCocoaWindow(w->handle), OPT(macos_window_resizable)); +#endif + return false; + } +#endif +} + + #ifdef __APPLE__ static int filter_option(int key UNUSED, int mods, unsigned int scancode UNUSED) { return ((mods == GLFW_MOD_ALT) || (mods == (GLFW_MOD_ALT | GLFW_MOD_SHIFT))) ? 1 : 0; } + static GLFWwindow *application_quit_canary = NULL; static int @@ -370,6 +415,14 @@ on_application_reopen(int has_visible_windows) { unjam_event_loop(); return false; } + +static int +intercept_cocoa_fullscreen(GLFWwindow *w) { + if (!set_callback_window(w)) return 0; + toggle_fullscreen_for_os_window(global_state.callback_os_window); + global_state.callback_os_window = NULL; + return 1; +} #endif void @@ -504,6 +557,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { glfwSwapInterval(OPT(sync_to_monitor) ? 1 : 0); #ifdef __APPLE__ if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option); + glfwSetCocoaToggleFullscreenIntercept(glfw_window, intercept_cocoa_fullscreen); #endif send_prerendered_sprites_for_window(w); if (logo.pixels && logo.width && logo.height) glfwSetWindowIcon(glfw_window, 1, &logo); @@ -702,29 +756,10 @@ set_clipboard_string(PyObject UNUSED *self, PyObject *args) { static PyObject* toggle_fullscreen(PYNOARG) { - GLFWmonitor *monitor; OSWindow *w = current_os_window(); - if (!w || !w->handle) Py_RETURN_NONE; - if ((monitor = glfwGetWindowMonitor(w->handle)) == NULL) { - // make fullscreen - monitor = current_monitor(w->handle); - if (monitor == NULL) return NULL; - const GLFWvidmode* mode = glfwGetVideoMode(monitor); - w->before_fullscreen.is_set = true; - 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); - Py_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); -#ifdef __APPLE__ - if (glfwGetCocoaWindow) cocoa_make_window_resizable(glfwGetCocoaWindow(w->handle), OPT(macos_window_resizable)); -#endif - Py_RETURN_FALSE; - } + if (!w) Py_RETURN_NONE; + if (toggle_fullscreen_for_os_window(w)) { Py_RETURN_TRUE; } + Py_RETURN_FALSE; } static PyObject*