mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 22:28:24 +02:00
GLFW: Add support for render frames on Cocoa (CVDisplayLink)
Allows vsync to work again since Apple broke OpenGL swap intervals on Mojave
This commit is contained in:
@@ -375,6 +375,7 @@ int _glfwPlatformInit(void)
|
||||
if (!initializeTIS())
|
||||
return GLFW_FALSE;
|
||||
|
||||
_glfw.ns.displayLinks.lock = [NSLock new];
|
||||
_glfwInitTimerNS();
|
||||
_glfwInitJoysticksNS();
|
||||
|
||||
@@ -384,6 +385,12 @@ int _glfwPlatformInit(void)
|
||||
|
||||
void _glfwPlatformTerminate(void)
|
||||
{
|
||||
if (_glfw.ns.displayLinks.lock) {
|
||||
_glfwClearDisplayLinks();
|
||||
[_glfw.ns.displayLinks.lock release];
|
||||
_glfw.ns.displayLinks.lock = nil;
|
||||
}
|
||||
|
||||
if (_glfw.ns.inputSource)
|
||||
{
|
||||
CFRelease(_glfw.ns.inputSource);
|
||||
|
||||
@@ -217,6 +217,60 @@ static void endFadeReservation(CGDisplayFadeReservationToken token)
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwClearDisplayLinks() {
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
if (_glfw.ns.displayLinks.entries[i].displayLinkStarted) {
|
||||
CVDisplayLinkStop(_glfw.ns.displayLinks.entries[i].displayLink);
|
||||
_glfw.ns.displayLinks.entries[i].displayLinkStarted = GLFW_FALSE;
|
||||
}
|
||||
if (_glfw.ns.displayLinks.entries[i].displayLink) {
|
||||
CVDisplayLinkRelease(_glfw.ns.displayLinks.entries[i].displayLink);
|
||||
_glfw.ns.displayLinks.entries[i].displayLink = nil;
|
||||
}
|
||||
}
|
||||
_glfw.ns.displayLinks.count = 0;
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
}
|
||||
|
||||
static CVReturn displayLinkCallback(
|
||||
CVDisplayLinkRef displayLink,
|
||||
const CVTimeStamp* now, const CVTimeStamp* outputTime,
|
||||
CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* userInfo)
|
||||
{
|
||||
CGDirectDisplayID displayID = (CGDirectDisplayID)userInfo;
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
GLFWbool notify = GLFW_FALSE;
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
if (_glfw.ns.displayLinks.entries[i].displayID == displayID) {
|
||||
if (_glfw.ns.displayLinks.entries[i].renderFrameRequested) {
|
||||
notify = GLFW_TRUE;
|
||||
_glfw.ns.displayLinks.entries[i].renderFrameRequested = GLFW_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
if (notify) {
|
||||
_glfwCocoaPostEmptyEvent(RENDER_FRAME_REQUEST_EVENT_TYPE, displayID);
|
||||
}
|
||||
return kCVReturnSuccess;
|
||||
}
|
||||
|
||||
static inline void createDisplayLink(CGDirectDisplayID displayID) {
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
if (_glfw.ns.displayLinks.count >= sizeof(_glfw.ns.displayLinks.entries)/sizeof(_glfw.ns.displayLinks.entries[0]) - 1) return;
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return;
|
||||
}
|
||||
_GLFWDisplayLinkNS *entry = &_glfw.ns.displayLinks.entries[_glfw.ns.displayLinks.count++];
|
||||
memset(entry, 0, sizeof(_GLFWDisplayLinkNS));
|
||||
entry->displayID = displayID;
|
||||
CVDisplayLinkCreateWithCGDisplay(displayID, &entry->displayLink);
|
||||
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)displayID);
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
}
|
||||
|
||||
// Poll for changes in the set of connected monitors
|
||||
//
|
||||
void _glfwPollMonitorsNS(void)
|
||||
@@ -228,6 +282,7 @@ void _glfwPollMonitorsNS(void)
|
||||
CGGetOnlineDisplayList(0, NULL, &displayCount);
|
||||
displays = calloc(displayCount, sizeof(CGDirectDisplayID));
|
||||
CGGetOnlineDisplayList(displayCount, displays, &displayCount);
|
||||
_glfwClearDisplayLinks();
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; i++)
|
||||
_glfw.monitors[i]->ns.screen = nil;
|
||||
@@ -269,6 +324,7 @@ void _glfwPollMonitorsNS(void)
|
||||
monitor = _glfwAllocMonitor(name, size.width, size.height);
|
||||
monitor->ns.displayID = displays[i];
|
||||
monitor->ns.unitNumber = unitNumber;
|
||||
createDisplayLink(monitor->ns.displayID);
|
||||
|
||||
free(name);
|
||||
|
||||
|
||||
24
glfw/cocoa_platform.h
vendored
24
glfw/cocoa_platform.h
vendored
@@ -30,14 +30,19 @@
|
||||
#include <Carbon/Carbon.h>
|
||||
#if defined(__OBJC__)
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <CoreVideo/CoreVideo.h>
|
||||
#else
|
||||
typedef void* id;
|
||||
typedef void* CVDisplayLinkRef;
|
||||
#endif
|
||||
|
||||
#define RENDER_FRAME_REQUEST_EVENT_TYPE 1
|
||||
|
||||
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long);
|
||||
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
|
||||
typedef struct VkMacOSSurfaceCreateInfoMVK
|
||||
{
|
||||
@@ -105,8 +110,19 @@ typedef struct _GLFWwindowNS
|
||||
GLFWcocoatogglefullscreenfun toggleFullscreenCallback;
|
||||
// Dead key state
|
||||
UInt32 deadKeyState;
|
||||
// Whether a render frame has been requested for this window
|
||||
GLFWbool renderFrameRequested;
|
||||
GLFWcocoarenderframefun renderFrameCallback;
|
||||
} _GLFWwindowNS;
|
||||
|
||||
typedef struct _GLFWDisplayLinkNS
|
||||
{
|
||||
CVDisplayLinkRef displayLink;
|
||||
CGDirectDisplayID displayID;
|
||||
GLFWbool displayLinkStarted;
|
||||
GLFWbool renderFrameRequested;
|
||||
} _GLFWDisplayLinkNS;
|
||||
|
||||
// Cocoa-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryNS
|
||||
@@ -141,6 +157,12 @@ typedef struct _GLFWlibraryNS
|
||||
CFStringRef kPropertyUnicodeKeyLayoutData;
|
||||
} tis;
|
||||
|
||||
struct {
|
||||
_GLFWDisplayLinkNS entries[256];
|
||||
size_t count;
|
||||
id lock;
|
||||
} displayLinks;
|
||||
|
||||
} _GLFWlibraryNS;
|
||||
|
||||
// Cocoa-specific per-monitor data
|
||||
@@ -176,3 +198,5 @@ void _glfwInitTimerNS(void);
|
||||
void _glfwPollMonitorsNS(void);
|
||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
||||
void _glfwClearDisplayLinks();
|
||||
void _glfwCocoaPostEmptyEvent(short subtype, long data1);
|
||||
|
||||
@@ -1744,6 +1744,55 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
|
||||
[window->ns.object setAlphaValue:opacity];
|
||||
}
|
||||
|
||||
static inline CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) {
|
||||
NSWindow *nw = w->ns.object;
|
||||
NSDictionary *dict = [nw.screen deviceDescription];
|
||||
NSNumber *displayIDns = [dict objectForKey:@"NSScreenNumber"];
|
||||
if (displayIDns) return [displayIDns unsignedIntValue];
|
||||
return (CGDirectDisplayID)-1;
|
||||
}
|
||||
|
||||
static inline void sendEvent(NSEvent *event) {
|
||||
if (event.type == NSEventTypeApplicationDefined) {
|
||||
if (event.subtype == RENDER_FRAME_REQUEST_EVENT_TYPE) {
|
||||
CGDirectDisplayID displayID = (CGDirectDisplayID)event.data1;
|
||||
_GLFWwindow *w = _glfw.windowListHead;
|
||||
while (w) {
|
||||
if (w->ns.renderFrameRequested && displayID == displayIDForWindow(w)) {
|
||||
w->ns.renderFrameRequested = GLFW_FALSE;
|
||||
w->ns.renderFrameCallback((GLFWwindow*)w);
|
||||
}
|
||||
w = w->next;
|
||||
}
|
||||
}
|
||||
} else [NSApp sendEvent:event];
|
||||
}
|
||||
|
||||
static inline void
|
||||
requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
|
||||
if (!callback) {
|
||||
w->ns.renderFrameRequested = GLFW_FALSE;
|
||||
w->ns.renderFrameCallback = NULL;
|
||||
return;
|
||||
}
|
||||
w->ns.renderFrameCallback = callback;
|
||||
w->ns.renderFrameRequested = GLFW_TRUE;
|
||||
CGDirectDisplayID displayID = displayIDForWindow(w);
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
|
||||
if (dl->displayID == displayID) {
|
||||
dl->renderFrameRequested = GLFW_TRUE;
|
||||
if (!dl->displayLinkStarted) {
|
||||
CVDisplayLinkStart(dl->displayLink);
|
||||
dl->displayLinkStarted = GLFW_TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
}
|
||||
|
||||
void _glfwPlatformPollEvents(void)
|
||||
{
|
||||
for (;;)
|
||||
@@ -1755,7 +1804,7 @@ void _glfwPlatformPollEvents(void)
|
||||
if (event == nil)
|
||||
break;
|
||||
|
||||
if ([event type] != NSEventTypeApplicationDefined) [NSApp sendEvent:event];
|
||||
sendEvent(event);
|
||||
}
|
||||
|
||||
[_glfw.ns.autoreleasePool drain];
|
||||
@@ -1771,7 +1820,7 @@ void _glfwPlatformWaitEvents(void)
|
||||
untilDate:[NSDate distantFuture]
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
if ([event type] != NSEventTypeApplicationDefined) [NSApp sendEvent:event];
|
||||
sendEvent(event);
|
||||
|
||||
_glfwPlatformPollEvents();
|
||||
}
|
||||
@@ -1783,12 +1832,12 @@ void _glfwPlatformWaitEventsTimeout(double timeout)
|
||||
untilDate:date
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
if (event && [event type] != NSEventTypeApplicationDefined) [NSApp sendEvent:event];
|
||||
if (event) sendEvent(event);
|
||||
|
||||
_glfwPlatformPollEvents();
|
||||
}
|
||||
|
||||
void _glfwPlatformPostEmptyEvent(void)
|
||||
void _glfwCocoaPostEmptyEvent(short subtype, long data1)
|
||||
{
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
|
||||
@@ -1797,13 +1846,18 @@ void _glfwPlatformPostEmptyEvent(void)
|
||||
timestamp:0
|
||||
windowNumber:0
|
||||
context:nil
|
||||
subtype:0
|
||||
data1:0
|
||||
subtype:subtype
|
||||
data1:data1
|
||||
data2:0];
|
||||
[NSApp postEvent:event atStart:YES];
|
||||
[pool drain];
|
||||
}
|
||||
|
||||
void _glfwPlatformPostEmptyEvent(void)
|
||||
{
|
||||
_glfwCocoaPostEmptyEvent(0, 0);
|
||||
}
|
||||
|
||||
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
|
||||
{
|
||||
const NSRect contentRect = [window->ns.view frame];
|
||||
@@ -2101,6 +2155,10 @@ GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReope
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback) {
|
||||
requestRenderFrame((_GLFWwindow*)w, callback);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, unsigned short *cocoa_key, int *cocoa_mods) {
|
||||
*cocoa_key = 0;
|
||||
*cocoa_mods = 0;
|
||||
|
||||
@@ -172,6 +172,7 @@ def generate_wrappers(glfw_header):
|
||||
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 glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback)
|
||||
void* glfwGetX11Display(void)
|
||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||
void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)
|
||||
@@ -199,6 +200,7 @@ const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callbac
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);
|
||||
typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);
|
||||
typedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, const char*);
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101400)
|
||||
#define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval
|
||||
#define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity
|
||||
#endif
|
||||
|
||||
@@ -50,11 +49,10 @@ static void swapBuffersNSGL(_GLFWwindow* window)
|
||||
|
||||
static void swapIntervalNSGL(int interval)
|
||||
{
|
||||
_GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
|
||||
GLint sync = interval;
|
||||
[window->context.nsgl.object setValues:&sync
|
||||
forParameter:NSOpenGLContextParameterSwapInterval];
|
||||
// As of Mojave this does not work so we use CVDisplayLink instead
|
||||
(void)(interval);
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"NSGL: Swap intervals do not work on macOS");
|
||||
}
|
||||
|
||||
static int extensionSupportedNSGL(const char* extension)
|
||||
|
||||
2
kitty/glfw-wrapper.c
generated
2
kitty/glfw-wrapper.c
generated
@@ -376,6 +376,8 @@ load_glfw(const char* path) {
|
||||
|
||||
*(void **) (&glfwGetCocoaKeyEquivalent_impl) = dlsym(handle, "glfwGetCocoaKeyEquivalent");
|
||||
|
||||
*(void **) (&glfwCocoaRequestRenderFrame_impl) = dlsym(handle, "glfwCocoaRequestRenderFrame");
|
||||
|
||||
*(void **) (&glfwGetX11Display_impl) = dlsym(handle, "glfwGetX11Display");
|
||||
|
||||
*(void **) (&glfwGetX11Window_impl) = dlsym(handle, "glfwGetX11Window");
|
||||
|
||||
5
kitty/glfw-wrapper.h
generated
5
kitty/glfw-wrapper.h
generated
@@ -1416,6 +1416,7 @@ typedef struct GLFWgamepadstate
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);
|
||||
typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);
|
||||
typedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, const char*);
|
||||
@@ -1911,6 +1912,10 @@ typedef void (*glfwGetCocoaKeyEquivalent_func)(int, int, void*, void*);
|
||||
glfwGetCocoaKeyEquivalent_func glfwGetCocoaKeyEquivalent_impl;
|
||||
#define glfwGetCocoaKeyEquivalent glfwGetCocoaKeyEquivalent_impl
|
||||
|
||||
typedef void (*glfwCocoaRequestRenderFrame_func)(GLFWwindow*, GLFWcocoarenderframefun);
|
||||
glfwCocoaRequestRenderFrame_func glfwCocoaRequestRenderFrame_impl;
|
||||
#define glfwCocoaRequestRenderFrame glfwCocoaRequestRenderFrame_impl
|
||||
|
||||
typedef void* (*glfwGetX11Display_func)();
|
||||
glfwGetX11Display_func glfwGetX11Display_impl;
|
||||
#define glfwGetX11Display glfwGetX11Display_impl
|
||||
|
||||
Reference in New Issue
Block a user