Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
363 changes: 363 additions & 0 deletions Sources/COpenSwiftUI/CoreVideo/DisplayLink.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,363 @@
//
// DisplayLink.c
// COpenSwiftUI
//
// Audited for 6.5.4
// Status: Complete

#include "DisplayLink.h"

#if OPENSWIFTUI_TARGET_OS_OSX

#include <os/lock.h>
#include <QuartzCore/QuartzCore.h>
#include <stdatomic.h>

extern void CVDisplayLinkSetPaused(CVDisplayLinkRef displayLink, bool paused);
static void remove_link(DisplayLinkRef displayLink);

typedef struct DisplayLinkManager {
struct DisplayLinkManager *nextManager;

CGDirectDisplayID displayID;

uint32_t _padding1;

CVDisplayLinkRef displayLink;

struct DisplayLink *listHead;

uint32_t totalDisplayLinks;

uint32_t pausedDisplayLinks;

uint32_t state;

uint32_t _padding2;

_Atomic uint64_t timestamp;
} DisplayLinkManager;

struct DisplayLink {
uint32_t unknown_field_1;

uint32_t _padding1;

struct DisplayLinkManager *manager;

struct DisplayLink *nextDisplayLink;

DisplayLinkCallback callback;

double nextTime;

uint32_t state;

struct {
uint8_t isScheduled;
uint8_t isDestroyed;
uint16_t _padding2;
} flags;
};

static os_unfair_lock link_lock = OS_UNFAIR_LOCK_INIT;
static dispatch_queue_t link_queue = NULL;
static DisplayLinkManager *link_list = NULL;

static void dispatch_items(void *context) {
DisplayLinkManager *manager = (DisplayLinkManager *)context;

double callback_time = *(double*)&manager->timestamp;

os_unfair_lock_lock(&link_lock);

DisplayLinkRef *links_to_process = NULL;

size_t num_links = 0;

struct DisplayLink *current = manager->listHead;
size_t count = 0;
while (current) {
count++;
current = current->nextDisplayLink;
}

if (count > 0) {
links_to_process = (DisplayLinkRef *)alloca(count * sizeof(DisplayLinkRef));
Copy link

Copilot AI Sep 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using alloca can cause stack overflow with large counts. Consider using malloc/free or setting a reasonable upper bound on the allocation size to prevent stack exhaustion.

Copilot uses AI. Check for mistakes.

current = manager->listHead;
while (current) {
os_unfair_lock_lock((os_unfair_lock_t)current);

if (!current->flags.isDestroyed) {
if (current->nextTime == INFINITY) {
if (current->state != 0) {
current->state--;
if (current->state == 0) {
manager->pausedDisplayLinks++;
manager->state = 5;
}
}
} else {
links_to_process[num_links++] = current;
current->nextTime = INFINITY;
current->flags.isScheduled = 1;
}
}

os_unfair_lock_unlock((os_unfair_lock_t)current);
current = current->nextDisplayLink;
}
}

os_unfair_lock_unlock(&link_lock);

for (size_t i = 0; i < num_links; i++) {
DisplayLinkRef link = links_to_process[i];
if (link->callback) {
link->callback(link, callback_time);
}
}

os_unfair_lock_lock(&link_lock);

for (size_t i = 0; i < num_links; i++) {
DisplayLinkRef link = links_to_process[i];

os_unfair_lock_lock((os_unfair_lock_t)link);
link->flags.isScheduled = 0;
bool should_free = link->flags.isDestroyed;
os_unfair_lock_unlock((os_unfair_lock_t)link);

if (should_free) {
remove_link(link);
Block_release(link->callback);
free(link);
}
}

bool should_pause = false;
bool should_destroy = false;

if (manager->pausedDisplayLinks == manager->totalDisplayLinks) {
if (manager->state != 0) {
manager->state--;
if (manager->state == 0 && manager->pausedDisplayLinks != 0) {
should_pause = true;
}
}
}

if (manager->pausedDisplayLinks == 0 &&
manager->totalDisplayLinks == 0) {
should_destroy = true;

DisplayLinkManager **current_mgr = &link_list;
while (*current_mgr) {
if (*current_mgr == manager) {
*current_mgr = manager->nextManager;
break;
}
current_mgr = &(*current_mgr)->nextManager;
}
}

os_unfair_lock_unlock(&link_lock);

if (should_pause) {
CVDisplayLinkSetPaused(manager->displayLink, true);
}

atomic_store(&manager->timestamp, 0);

if (should_destroy) {
CVDisplayLinkRelease(manager->displayLink);
free(manager);
}

}

static CVReturn link_callback(
CVDisplayLinkRef displayLink,
const CVTimeStamp* inNow,
const CVTimeStamp* inOutputTime,
CVOptionFlags flagsIn,
CVOptionFlags* flagsOut,
void* displayLinkContext
) {
DisplayLinkManager* manager = (DisplayLinkManager*)displayLinkContext;

double current_time = CACurrentMediaTime();
uint64_t time_bits = *(uint64_t*)&current_time;

uint64_t expected = 0;
if (atomic_compare_exchange_strong(&manager->timestamp, &expected, time_bits)) {
dispatch_async_f(link_queue, manager, dispatch_items);
}

return kCVReturnSuccess;
}

static void remove_link(DisplayLinkRef displayLink) {
DisplayLinkManager *manager = displayLink->manager;

struct DisplayLink **current = &manager->listHead;
while (*current) {
if (*current == displayLink) {
*current = displayLink->nextDisplayLink;

manager->totalDisplayLinks--;
break;
}
current = &(*current)->nextDisplayLink;
}

if (displayLink->state == 0) {
manager->pausedDisplayLinks--;
}
}

DisplayLinkRef DisplayLinkCreate(CGDirectDisplayID displayID, DisplayLinkCallback callback) {
DisplayLinkManager *manager = NULL;
DisplayLinkRef displayLink = NULL;

os_unfair_lock_lock(&link_lock);

if (!link_queue) {
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0
);
link_queue = dispatch_queue_create("com.apple.SwiftUI.DisplayLink", attr);
}

manager = link_list;
while (manager) {
if (manager->displayID == displayID) {
break;
}
manager = manager->nextManager;
}

if (!manager) {
manager = (DisplayLinkManager *)calloc(1, sizeof(DisplayLinkManager));
if (!manager) goto cleanup_and_fail;

manager->displayID = displayID;
manager->state = 5;

CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &manager->displayLink);

if (status != kCVReturnSuccess) {
free(manager);
goto cleanup_and_fail;
}

manager->nextManager = link_list;
link_list = manager;

CVDisplayLinkSetOutputCallback(manager->displayLink, link_callback, manager);
CVDisplayLinkStart(manager->displayLink);
}

displayLink = (DisplayLinkRef)calloc(1, sizeof(struct DisplayLink));

if (!displayLink) goto cleanup_and_fail;

displayLink->manager = manager;
displayLink->callback = Block_copy(callback);
displayLink->nextTime = INFINITY;
displayLink->state = 0;

displayLink->nextDisplayLink = manager->listHead;
manager->listHead = displayLink;

manager->pausedDisplayLinks++;
manager->totalDisplayLinks++;

os_unfair_lock_unlock(&link_lock);
return displayLink;
cleanup_and_fail:
os_unfair_lock_unlock(&link_lock);
return NULL;
}

void DisplayLinkDestroy(DisplayLinkRef displayLink) {
if (!displayLink) return;

os_unfair_lock_lock((os_unfair_lock_t)displayLink);

bool wasScheduled = displayLink->flags.isScheduled;
displayLink->flags.isDestroyed = true;

os_unfair_lock_unlock((os_unfair_lock_t)displayLink);

if (wasScheduled) return;

os_unfair_lock_lock(&link_lock);

DisplayLinkManager *manager = displayLink->manager;

remove_link(displayLink);

bool shouldDestroyManager = (manager->totalDisplayLinks == 0 &&
manager->state == 0);

if (shouldDestroyManager) {
DisplayLinkManager **current = &link_list;
while (*current) {
if (*current == manager) {
*current = manager->nextManager;
break;
}
current = &(*current)->nextManager;
}
}

os_unfair_lock_unlock(&link_lock);

if (shouldDestroyManager) {
CVDisplayLinkRelease(manager->displayLink);
free(manager);
}

Block_release(displayLink->callback);
free(displayLink);
}

void DisplayLinkSetNextTime(DisplayLinkRef displayLink, double nextTime) {
if (!displayLink) return;

os_unfair_lock_lock(&link_lock);

if (displayLink->nextTime != nextTime) {
if (nextTime != INFINITY && displayLink->state == 0) {

DisplayLinkManager *manager = displayLink->manager;

manager->pausedDisplayLinks--;

if (manager->state == 0) {
CVDisplayLinkSetPaused(manager->displayLink, false);
}

manager->state = 5;
displayLink->state = 5;
}

displayLink->nextTime = nextTime;
}

os_unfair_lock_unlock(&link_lock);
}

double DisplayLinkGetNextTime(DisplayLinkRef displayLink) {
if (!displayLink) return NAN;

os_unfair_lock_lock(&link_lock);
double time = displayLink->nextTime;
os_unfair_lock_unlock(&link_lock);

return time;
}

#endif
32 changes: 32 additions & 0 deletions Sources/COpenSwiftUI/CoreVideo/DisplayLink.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// DisplayLink.h
// COpenSwiftUI
//
// Audited for 6.5.4
// Status: Complete

#ifndef DisplayLink_h
#define DisplayLink_h

#include "OpenSwiftUIBase.h"

#if OPENSWIFTUI_TARGET_OS_OSX

#include <CoreFoundation/CoreFoundation.h>
#include <CoreVideo/CoreVideo.h>

typedef struct DisplayLink * OPENSWIFTUI_SWIFT_STRUCT DisplayLinkRef OPENSWIFTUI_SWIFT_NAME(DisplayLink);

typedef void(^ DisplayLinkCallback)(DisplayLinkRef __nonnull, double);

extern DisplayLinkRef __nullable DisplayLinkCreate(CGDirectDisplayID displayID, DisplayLinkCallback __nonnull callback);

extern void DisplayLinkDestroy(DisplayLinkRef __nonnull displayLink);

extern void DisplayLinkSetNextTime(DisplayLinkRef __nonnull displayLink, double nextTime);

extern double DisplayLinkGetNextTime(DisplayLinkRef __nonnull displayLink);

#endif

#endif /* DisplayLink_h */
Loading