From c0f04231eda5bceffc50649279af7668ad58f428 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 27 Sep 2024 12:12:12 +0530 Subject: [PATCH] Move CVDisplayLink code into its own module Apple has deprecated CVDisplayLink in favor of CADisplayLink which has different semantics. So turn off deprecation warnings for CVDisplayLink related code only until I can be bothered to port it to CADisplayLink. --- glfw/cocoa_displaylink.m | 160 +++++++++++++++++++++++++++++++++++++++ glfw/cocoa_monitor.m | 53 +------------ glfw/cocoa_platform.h | 25 ++---- glfw/cocoa_window.m | 82 +------------------- glfw/source-info.json | 1 + 5 files changed, 172 insertions(+), 149 deletions(-) create mode 100644 glfw/cocoa_displaylink.m diff --git a/glfw/cocoa_displaylink.m b/glfw/cocoa_displaylink.m new file mode 100644 index 00000000000..47011965cc1 --- /dev/null +++ b/glfw/cocoa_displaylink.m @@ -0,0 +1,160 @@ +/* + * cocoa_displaylink.m + * Copyright (C) 2024 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +// CVDisplayLink is deprecated +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +#include "internal.h" +#include + +#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll) + +typedef struct _GLFWDisplayLinkNS +{ + CVDisplayLinkRef displayLink; + CGDirectDisplayID displayID; + monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at; +} _GLFWDisplayLinkNS; + +static struct { + _GLFWDisplayLinkNS entries[256]; + size_t count; +} displayLinks = {0}; + +static CGDirectDisplayID +displayIDForWindow(_GLFWwindow *w) { + NSWindow *nw = w->ns.object; + NSDictionary *dict = [nw.screen deviceDescription]; + NSNumber *displayIDns = dict[@"NSScreenNumber"]; + if (displayIDns) return [displayIDns unsignedIntValue]; + return (CGDirectDisplayID)-1; +} + +void +_glfwClearDisplayLinks(void) { + for (size_t i = 0; i < displayLinks.count; i++) { + if (displayLinks.entries[i].displayLink) { + CVDisplayLinkStop(displayLinks.entries[i].displayLink); + CVDisplayLinkRelease(displayLinks.entries[i].displayLink); + } + } + memset(displayLinks.entries, 0, sizeof(_GLFWDisplayLinkNS) * displayLinks.count); + displayLinks.count = 0; +} + +static CVReturn +displayLinkCallback( + CVDisplayLinkRef displayLink UNUSED, + const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED, + CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo) { + CGDirectDisplayID displayID = (uintptr_t)userInfo; + NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID]; + [NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO]; + [arg release]; + return kCVReturnSuccess; +} + +static void +_glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) { + CVDisplayLinkCreateWithCGDisplay(entry->displayID, &entry->displayLink); + CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)entry->displayID); +} + +unsigned +_glfwCreateDisplayLink(CGDirectDisplayID displayID) { + if (displayLinks.count >= arraysz(displayLinks.entries) - 1) { + _glfwInputError(GLFW_PLATFORM_ERROR, "Too many monitors cannot create display link"); + return displayLinks.count; + } + for (unsigned i = 0; i < displayLinks.count; i++) { + // already created in this run + if (displayLinks.entries[i].displayID == displayID) return i; + } + _GLFWDisplayLinkNS *entry = &displayLinks.entries[displayLinks.count++]; + memset(entry, 0, sizeof(_GLFWDisplayLinkNS)); + entry->displayID = displayID; + _glfw_create_cv_display_link(entry); + return displayLinks.count - 1; +} + +static unsigned long long display_link_shutdown_timer = 0; + +static void +_glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data UNUSED) { + display_link_shutdown_timer = 0; + for (size_t i = 0; i < displayLinks.count; i++) { + _GLFWDisplayLinkNS *dl = &displayLinks.entries[i]; + if (dl->displayLink) CVDisplayLinkStop(dl->displayLink); + dl->lastRenderFrameRequestedAt = 0; + dl->first_unserviced_render_frame_request_at = 0; + } +} + +void +_glfwRequestRenderFrame(_GLFWwindow *w) { + CGDirectDisplayID displayID = displayIDForWindow(w); + if (display_link_shutdown_timer) { + _glfwPlatformUpdateTimer(display_link_shutdown_timer, DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, true); + } else { + display_link_shutdown_timer = _glfwPlatformAddTimer(DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, false, _glfwShutdownCVDisplayLink, NULL, NULL); + } + monotonic_t now = glfwGetTime(); + bool found_display_link = false; + _GLFWDisplayLinkNS *dl = NULL; + for (size_t i = 0; i < displayLinks.count; i++) { + dl = &displayLinks.entries[i]; + if (dl->displayID == displayID) { + found_display_link = true; + dl->lastRenderFrameRequestedAt = now; + if (!dl->first_unserviced_render_frame_request_at) dl->first_unserviced_render_frame_request_at = now; + if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink); + else if (now - dl->first_unserviced_render_frame_request_at > s_to_monotonic_t(1ll)) { + // display link is stuck need to recreate it because Apple can't even + // get a simple timer right + CVDisplayLinkRelease(dl->displayLink); dl->displayLink = nil; + dl->first_unserviced_render_frame_request_at = now; + _glfw_create_cv_display_link(dl); + _glfwInputError(GLFW_PLATFORM_ERROR, + "CVDisplayLink stuck possibly because of sleep/screensaver + Apple's incompetence, recreating."); + if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink); + } + } else if (dl->displayLink && dl->lastRenderFrameRequestedAt && now - dl->lastRenderFrameRequestedAt >= DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL) { + CVDisplayLinkStop(dl->displayLink); + dl->lastRenderFrameRequestedAt = 0; + dl->first_unserviced_render_frame_request_at = 0; + } + } + if (!found_display_link) { + unsigned idx = _glfwCreateDisplayLink(displayID); + if (idx < displayLinks.count) { + dl = &displayLinks.entries[idx]; + dl->lastRenderFrameRequestedAt = now; + dl->first_unserviced_render_frame_request_at = now; + if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink); + } + } +} + +void +_glfwDispatchRenderFrame(CGDirectDisplayID displayID) { + _GLFWwindow *w = _glfw.windowListHead; + while (w) { + if (w->ns.renderFrameRequested && displayID == displayIDForWindow(w)) { + w->ns.renderFrameRequested = false; + w->ns.renderFrameCallback((GLFWwindow*)w); + } + w = w->next; + } + for (size_t i = 0; i < displayLinks.count; i++) { + _GLFWDisplayLinkNS *dl = &displayLinks.entries[i]; + if (dl->displayID == displayID) { + dl->first_unserviced_render_frame_request_at = 0; + } + } +} + + diff --git a/glfw/cocoa_monitor.m b/glfw/cocoa_monitor.m index 795c5dd35fa..70d64e11621 100644 --- a/glfw/cocoa_monitor.m +++ b/glfw/cocoa_monitor.m @@ -27,7 +27,6 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== -#pragma clang diagnostic ignored "-Wdeprecated-declarations" #include "internal.h" #include @@ -36,7 +35,6 @@ #include #include -#include #include @@ -324,54 +322,7 @@ static double getFallbackRefreshRate(CGDirectDisplayID displayID) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwClearDisplayLinks(void) { - for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { - if (_glfw.ns.displayLinks.entries[i].displayLink) { - CVDisplayLinkStop(_glfw.ns.displayLinks.entries[i].displayLink); - CVDisplayLinkRelease(_glfw.ns.displayLinks.entries[i].displayLink); - } - } - memset(_glfw.ns.displayLinks.entries, 0, sizeof(_GLFWDisplayLinkNS) * _glfw.ns.displayLinks.count); - _glfw.ns.displayLinks.count = 0; -} - -static CVReturn displayLinkCallback( - CVDisplayLinkRef displayLink UNUSED, - const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED, - CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo) -{ - CGDirectDisplayID displayID = (uintptr_t)userInfo; - NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID]; - [NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO]; - [arg release]; - return kCVReturnSuccess; -} - -void -_glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) { - CVDisplayLinkCreateWithCGDisplay(entry->displayID, &entry->displayLink); - CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)entry->displayID); -} - -_GLFWDisplayLinkNS* -_glfw_create_display_link(CGDirectDisplayID displayID) { - if (_glfw.ns.displayLinks.count >= arraysz(_glfw.ns.displayLinks.entries) - 1) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Too many monitors cannot create display link"); - return NULL; - } - for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { - // already created in this run - if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return _glfw.ns.displayLinks.entries + i; - } - _GLFWDisplayLinkNS *entry = &_glfw.ns.displayLinks.entries[_glfw.ns.displayLinks.count++]; - memset(entry, 0, sizeof(_GLFWDisplayLinkNS)); - entry->displayID = displayID; - _glfw_create_cv_display_link(entry); - return entry; -} - // Poll for changes in the set of connected monitors -// void _glfwPollMonitorsNS(void) { uint32_t displayCount; @@ -427,7 +378,7 @@ void _glfwPollMonitorsNS(void) { disconnected[j]->ns.displayID = displays[i]; disconnected[j]->ns.screen = screen; - _glfw_create_display_link(displays[i]); + _glfwCreateDisplayLink(displays[i]); disconnected[j] = NULL; break; } @@ -448,7 +399,7 @@ void _glfwPollMonitorsNS(void) monitor->ns.displayID = displays[i]; monitor->ns.unitNumber = unitNumber; monitor->ns.screen = screen; - _glfw_create_display_link(monitor->ns.displayID); + _glfwCreateDisplayLink(monitor->ns.displayID); free(name); diff --git a/glfw/cocoa_platform.h b/glfw/cocoa_platform.h index cead7c54515..e90ba405033 100644 --- a/glfw/cocoa_platform.h +++ b/glfw/cocoa_platform.h @@ -30,10 +30,8 @@ #include #if defined(__OBJC__) #import -#import #else typedef void* id; -typedef void* CVDisplayLinkRef; #endif // NOTE: Many Cocoa enum values have been renamed and we need to build across @@ -160,13 +158,6 @@ typedef struct _GLFWwindowNS GLFWcocoarenderframefun resizeCallback; } _GLFWwindowNS; -typedef struct _GLFWDisplayLinkNS -{ - CVDisplayLinkRef displayLink; - CGDirectDisplayID displayID; - monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at; -} _GLFWDisplayLinkNS; - // Cocoa-specific global data // typedef struct _GLFWlibraryNS @@ -199,10 +190,6 @@ typedef struct _GLFWlibraryNS CFStringRef kPropertyUnicodeKeyLayoutData; } tis; - struct { - _GLFWDisplayLinkNS entries[256]; - size_t count; - } displayLinks; // the callback to handle url open events GLFWhandleurlopen url_open_callback; @@ -244,12 +231,16 @@ float _glfwTransformYNS(float y); void* _glfwLoadLocalVulkanLoaderNS(void); + +// display links void _glfwClearDisplayLinks(void); void _glfwRestartDisplayLinks(void); -void _glfwDispatchTickCallback(void); +unsigned _glfwCreateDisplayLink(CGDirectDisplayID); void _glfwDispatchRenderFrame(CGDirectDisplayID); -void _glfwShutdownCVDisplayLink(unsigned long long, void*); +void _glfwRequestRenderFrame(_GLFWwindow *w); + +// event loop +void _glfwDispatchTickCallback(void); void _glfwCocoaPostEmptyEvent(void); -void _glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry); -_GLFWDisplayLinkNS* _glfw_create_display_link(CGDirectDisplayID); + uint32_t vk_to_unicode_key_with_current_layout(uint16_t keycode); diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 9acb4938fd5..bd897a812f8 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -26,7 +26,6 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== -#pragma clang diagnostic ignored "-Wdeprecated-declarations" #include "../kitty/monotonic.h" #include "glfw3.h" #include "internal.h" @@ -307,28 +306,6 @@ static NSUInteger getStyleMask(_GLFWwindow* window) } -CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) { - NSWindow *nw = w->ns.object; - NSDictionary *dict = [nw.screen deviceDescription]; - NSNumber *displayIDns = dict[@"NSScreenNumber"]; - if (displayIDns) return [displayIDns unsignedIntValue]; - return (CGDirectDisplayID)-1; -} - -static unsigned long long display_link_shutdown_timer = 0; -#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll) - -void -_glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data UNUSED) { - display_link_shutdown_timer = 0; - for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { - _GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i]; - if (dl->displayLink) CVDisplayLinkStop(dl->displayLink); - dl->lastRenderFrameRequestedAt = 0; - dl->first_unserviced_render_frame_request_at = 0; - } -} - static void requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) { if (!callback) { @@ -338,46 +315,7 @@ CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) { } w->ns.renderFrameCallback = callback; w->ns.renderFrameRequested = true; - CGDirectDisplayID displayID = displayIDForWindow(w); - if (display_link_shutdown_timer) { - _glfwPlatformUpdateTimer(display_link_shutdown_timer, DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, true); - } else { - display_link_shutdown_timer = _glfwPlatformAddTimer(DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, false, _glfwShutdownCVDisplayLink, NULL, NULL); - } - monotonic_t now = glfwGetTime(); - bool found_display_link = false; - _GLFWDisplayLinkNS *dl = NULL; - for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { - dl = &_glfw.ns.displayLinks.entries[i]; - if (dl->displayID == displayID) { - found_display_link = true; - dl->lastRenderFrameRequestedAt = now; - if (!dl->first_unserviced_render_frame_request_at) dl->first_unserviced_render_frame_request_at = now; - if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink); - else if (now - dl->first_unserviced_render_frame_request_at > s_to_monotonic_t(1ll)) { - // display link is stuck need to recreate it because Apple can't even - // get a simple timer right - CVDisplayLinkRelease(dl->displayLink); dl->displayLink = nil; - dl->first_unserviced_render_frame_request_at = now; - _glfw_create_cv_display_link(dl); - _glfwInputError(GLFW_PLATFORM_ERROR, - "CVDisplayLink stuck possibly because of sleep/screensaver + Apple's incompetence, recreating."); - if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink); - } - } else if (dl->displayLink && dl->lastRenderFrameRequestedAt && now - dl->lastRenderFrameRequestedAt >= DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL) { - CVDisplayLinkStop(dl->displayLink); - dl->lastRenderFrameRequestedAt = 0; - dl->first_unserviced_render_frame_request_at = 0; - } - } - if (!found_display_link) { - dl = _glfw_create_display_link(displayID); - if (dl) { - dl->lastRenderFrameRequestedAt = now; - dl->first_unserviced_render_frame_request_at = now; - if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink); - } - } + _glfwRequestRenderFrame(w); } void @@ -2338,24 +2276,6 @@ bool _glfwPlatformRawMouseMotionSupported(void) return false; } -void -_glfwDispatchRenderFrame(CGDirectDisplayID displayID) { - _GLFWwindow *w = _glfw.windowListHead; - while (w) { - if (w->ns.renderFrameRequested && displayID == displayIDForWindow(w)) { - w->ns.renderFrameRequested = false; - w->ns.renderFrameCallback((GLFWwindow*)w); - } - w = w->next; - } - for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { - _GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i]; - if (dl->displayID == displayID) { - dl->first_unserviced_render_frame_request_at = 0; - } - } -} - void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { const NSRect contentRect = [window->ns.view frame]; diff --git a/glfw/source-info.json b/glfw/source-info.json index 3700f046fb2..1c61979f318 100644 --- a/glfw/source-info.json +++ b/glfw/source-info.json @@ -13,6 +13,7 @@ "cocoa_joystick.m", "cocoa_monitor.m", "cocoa_window.m", + "cocoa_displaylink.m", "posix_thread.c", "nsgl_context.m", "egl_context.c",