diff --git a/docs/changelog.rst b/docs/changelog.rst index d89fe5f9d..ad295169c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -235,6 +235,9 @@ Detailed list of changes - macOS: A new option :opt:`macos_dock_badge_on_bell` to show a badge on the kitty dock icon when a bell occurs (:pull:`9529`) +- macOS: Workaround for yet another Tahoe bug causing rendering to fail + (:pull:`9520`) + 0.45.0 [2025-12-24] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 619a73162..80e006394 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -3621,51 +3621,6 @@ GLFWAPI void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun requestRenderFrame((_GLFWwindow*)w, callback); } -GLFWAPI bool glfwCocoaRecreateGLDrawable(GLFWwindow *w) { - _GLFWwindow* window = (_GLFWwindow*)w; - if (window->context.client == GLFW_NO_API) return false; - @try { - // Save current state - NSOpenGLPixelFormat *pixelFormat = window->context.nsgl.pixelFormat; - NSOpenGLContext *oldContext = window->context.nsgl.object; - - // Create a new context sharing resources with the old one - NSOpenGLContext *newContext = [[NSOpenGLContext alloc] - initWithFormat:pixelFormat - shareContext:oldContext]; - if (newContext == nil) return false; - - // Copy settings from old context - GLint opacity = 0; - [oldContext getValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity]; - [newContext setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity]; - GLint interval = 0; - [newContext setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval]; - - // Detach old context - [NSOpenGLContext clearCurrentContext]; - [oldContext clearDrawable]; - - // Attach new context to the view - [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [newContext setView:window->ns.view]; -#pragma clang diagnostic pop - [newContext makeCurrentContext]; - [newContext update]; - - // Replace context - window->context.nsgl.object = newContext; - [oldContext release]; - - } @catch (NSException *e) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to recreate NSGL context: %s (%s)", [[e name] UTF8String], [[e reason] UTF8String]); - return false; - } - return true; -} - GLFWAPI GLFWcocoarenderframefun glfwCocoaSetWindowResizeCallback(GLFWwindow *w, GLFWcocoarenderframefun cb) { _GLFWwindow* window = (_GLFWwindow*)w; GLFWcocoarenderframefun current = window->ns.resizeCallback; diff --git a/glfw/nsgl_context.m b/glfw/nsgl_context.m index d65678093..70627e2ac 100644 --- a/glfw/nsgl_context.m +++ b/glfw/nsgl_context.m @@ -345,3 +345,48 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) return window->context.nsgl.object; } + +GLFWAPI bool +glfwCocoaRecreateGLDrawable(GLFWwindow *w) { + _GLFWwindow* window = (_GLFWwindow*)w; + if (window->context.client == GLFW_NO_API) return false; + @try { + // Save current state + NSOpenGLPixelFormat *pixelFormat = window->context.nsgl.pixelFormat; + NSOpenGLContext *oldContext = window->context.nsgl.object; + + // Create a new context sharing resources with the old one + NSOpenGLContext *newContext = [[NSOpenGLContext alloc] + initWithFormat:pixelFormat + shareContext:oldContext]; + if (newContext == nil) return false; + + // Copy settings from old context + GLint opacity = 0; + [oldContext getValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity]; + [newContext setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity]; + GLint interval = 0; + [newContext setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval]; + + // Detach old context + [NSOpenGLContext clearCurrentContext]; + [oldContext clearDrawable]; + + // Attach new context to the view + [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [newContext setView:window->ns.view]; +#pragma clang diagnostic pop + [newContext makeCurrentContext]; + [newContext update]; + + // Replace context + window->context.nsgl.object = newContext; + [oldContext release]; + } @catch (NSException *e) { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to recreate NSGL context: %s (%s)", [[e name] UTF8String], [[e reason] UTF8String]); + return false; + } + return true; +} diff --git a/kitty/glfw.c b/kitty/glfw.c index d6c34b837..0d71bc747 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -11,7 +11,6 @@ #include "control-codes.h" #include #include "glfw-wrapper.h" -#include "gl-wrapper.h" #ifdef __APPLE__ #include "cocoa_window.h" #else @@ -347,20 +346,18 @@ window_iconify_callback(GLFWwindow *window, int iconified) { static void cocoa_out_of_sequence_render(OSWindow *window) { make_os_window_context_current(window); + window->needs_render = true; // On macOS Tahoe, the default framebuffer can become undefined during - // screen change events. Try to recover by recreating the drawable. See #9463 - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - if (!glfwCocoaRecreateGLDrawable(window->handle) || - glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - window->needs_render = true; + // screen change events. Try to recover by recreating the drawable. + // See https://github.com/kovidgoyal/kitty/issues/9463 + if (!current_framebuffer_is_ok()) { + if (!glfwCocoaRecreateGLDrawable(window->handle) || !current_framebuffer_is_ok()) { request_tick_callback(); return; } } - - window->needs_render = true; bool rendered = false; if (window->fonts_data->sprite_map) rendered = render_os_window(window, monotonic(), true); if (!rendered) { diff --git a/kitty/shaders.c b/kitty/shaders.c index 2abaa1e08..df7d669bb 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -1086,6 +1086,11 @@ screen_needs_rendering_in_layers(OSWindow *os_window, Window *w, Screen *screen) return has_ui || grman_has_images(grman); } +bool +current_framebuffer_is_ok(void) { + return check_framebuffer_status() == NULL; +} + // }}} enum { DRAW_NEITHER_BG = 0, DRAW_DEFAULT_BG = 1, DRAW_NON_DEFAULT_BG = 2, DRAW_BOTH_BG = 3}; diff --git a/kitty/state.h b/kitty/state.h index 75000bf89..b33f53694 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -503,3 +503,4 @@ bool screen_needs_rendering_in_layers(OSWindow *os_window, Window *w, Screen *sc void setup_os_window_for_rendering(OSWindow*, Tab*, Window*, bool); void swap_window_buffers(OSWindow *w); void take_screenshot_of_rectangular_region(OSWindow *os_window, Region region, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h); +bool current_framebuffer_is_ok(void);