From adc97bc1175d28d1dd534e8e4cba009e72df7b0a Mon Sep 17 00:00:00 2001 From: Jorge Silva Date: Tue, 17 Feb 2026 17:56:14 +0000 Subject: [PATCH] fix blank screen on macOS resume from sleep --- glfw/cocoa_window.m | 45 ++++++++++++++++++++++++++++++++++++++++++++ glfw/glfw.py | 1 + kitty/glfw-wrapper.c | 3 +++ kitty/glfw-wrapper.h | 8 +++++++- kitty/glfw.c | 14 ++++++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 9af15c3bf..65eb24a14 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -3818,6 +3818,51 @@ 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/glfw.py b/glfw/glfw.py index b720d2d0d..703639437 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -309,6 +309,7 @@ def generate_wrappers(glfw_header: str) -> None: GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback) uint32_t glfwGetCocoaKeyEquivalent(uint32_t glfw_key, int glfw_mods, int* cocoa_mods) void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback) + bool glfwCocoaRecreateGLDrawable(GLFWwindow *w) GLFWcocoarenderframefun glfwCocoaSetWindowResizeCallback(GLFWwindow *w, GLFWcocoarenderframefun callback) void* glfwGetX11Display(void) unsigned long glfwGetX11Window(GLFWwindow* window) diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 79a05696b..518bf6cb6 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -476,6 +476,9 @@ load_glfw(const char* path) { *(void **) (&glfwCocoaRequestRenderFrame_impl) = dlsym(handle, "glfwCocoaRequestRenderFrame"); if (glfwCocoaRequestRenderFrame_impl == NULL) dlerror(); // clear error indicator + *(void **) (&glfwCocoaRecreateGLDrawable_impl) = dlsym(handle, "glfwCocoaRecreateGLDrawable"); + if (glfwCocoaRecreateGLDrawable_impl == NULL) dlerror(); // clear error indicator + *(void **) (&glfwCocoaSetWindowResizeCallback_impl) = dlsym(handle, "glfwCocoaSetWindowResizeCallback"); if (glfwCocoaSetWindowResizeCallback_impl == NULL) dlerror(); // clear error indicator diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index bdbc37be1..f0caf3e89 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1131,7 +1131,9 @@ typedef enum { typedef struct GLFWDropEvent { GLFWDropEventType type; const char **mimes; size_t num_mimes; - double xpos, ypos; // Only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE + // Positions are only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE. + // They are in window co-ordinates same as for mouse events + double xpos, ypos; bool from_self; // Only valid upto GLFW_DROP_DROP ssize_t (*read_data)(GLFWwindow *w, struct GLFWDropEvent* ev, char *buffer, size_t sz); // Only valid for GLFW_DROP_DATA_AVAILABLE void (*finish_drop)(GLFWwindow *w, GLFWDragOperationType op); // Only valid for GLFW_DROP_DROP and GLFW_DROP_DATA_AVAILABLE @@ -2464,6 +2466,10 @@ typedef void (*glfwCocoaRequestRenderFrame_func)(GLFWwindow*, GLFWcocoarenderfra GFW_EXTERN glfwCocoaRequestRenderFrame_func glfwCocoaRequestRenderFrame_impl; #define glfwCocoaRequestRenderFrame glfwCocoaRequestRenderFrame_impl +typedef bool (*glfwCocoaRecreateGLDrawable_func)(GLFWwindow*); +GFW_EXTERN glfwCocoaRecreateGLDrawable_func glfwCocoaRecreateGLDrawable_impl; +#define glfwCocoaRecreateGLDrawable glfwCocoaRecreateGLDrawable_impl + typedef GLFWcocoarenderframefun (*glfwCocoaSetWindowResizeCallback_func)(GLFWwindow*, GLFWcocoarenderframefun); GFW_EXTERN glfwCocoaSetWindowResizeCallback_func glfwCocoaSetWindowResizeCallback_impl; #define glfwCocoaSetWindowResizeCallback glfwCocoaSetWindowResizeCallback_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index 846e51900..fc6825300 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -11,6 +11,7 @@ #include "control-codes.h" #include #include "glfw-wrapper.h" +#include "gl-wrapper.h" #ifdef __APPLE__ #include "cocoa_window.h" #else @@ -346,6 +347,19 @@ window_iconify_callback(GLFWwindow *window, int iconified) { static void cocoa_out_of_sequence_render(OSWindow *window) { make_os_window_context_current(window); + + // 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; + request_tick_callback(); + return; + } + } + + window->needs_render = true; bool rendered = false; if (window->fonts_data->sprite_map) rendered = render_os_window(window, monotonic(), true);