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
24 changes: 18 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,17 @@ elseif(APPLE)
message(FATAL_ERROR "Cocoa framework not found")
endif()

find_library(COREVIDEO_FRAMEWORK CoreVideo)
if (NOT COREVIDEO_FRAMEWORK)
message(FATAL_ERROR "CoreVideo framework not found")
endif()

if(ENABLE_METAL)
find_library(METAL_FRAMEWORK Metal)
find_library(QUARTZCORE_FRAMEWORK QuartzCore)
if (NOT QUARTZCORE_FRAMEWORK)
message(FATAL_ERROR "QuartzCore framework not found")
endif()
endif()

if(ENABLE_OPENGL)
Expand All @@ -298,7 +307,7 @@ elseif(APPLE)
message(FATAL_ERROR "OpenGL framework not found")
endif()
endif()

find_library(UNIFORMTYPEIDENTIFIERS_FRAMEWORK UniformTypeIdentifiers)
if (NOT UNIFORMTYPEIDENTIFIERS_FRAMEWORK)
message(FATAL_ERROR "UniformTypeIdentifiers framework not found")
Expand Down Expand Up @@ -357,13 +366,13 @@ target_compile_definitions(zwidget PRIVATE ${ZWIDGET_DEFINES})
target_include_directories(zwidget PRIVATE ${ZWIDGET_INCLUDE_DIRS})
target_link_options(zwidget PRIVATE ${ZWIDGET_LINK_OPTIONS})
if(APPLE)
target_link_libraries(zwidget PRIVATE ${ZWIDGET_LIBS} ${COCOA_FRAMEWORK} ${UNIFORMTYPEIDENTIFIERS_FRAMEWORK})
target_link_libraries(zwidget PRIVATE ${ZWIDGET_LIBS} ${COCOA_FRAMEWORK} ${COREVIDEO_FRAMEWORK} ${UNIFORMTYPEIDENTIFIERS_FRAMEWORK} ${QUARTZCORE_FRAMEWORK})
if(ENABLE_OPENGL AND OPENGL_FRAMEWORK)
target_link_libraries(zwidget PRIVATE ${OPENGL_FRAMEWORK})
target_compile_definitions(zwidget PRIVATE HAVE_OPENGL)
endif()
if(ENABLE_METAL AND METAL_FRAMEWORK)
target_link_libraries(zwidget PRIVATE ${METAL_FRAMEWORK})
target_link_libraries(zwidget PRIVATE ${METAL_FRAMEWORK} ${QUARTZCORE_FRAMEWORK})
target_compile_definitions(zwidget PRIVATE HAVE_METAL)
endif()
else()
Expand All @@ -373,15 +382,15 @@ if(SDL3_FOUND)
target_include_directories(zwidget PRIVATE ${SDL3_INCLUDE_DIRS})
if(TARGET SDL3::SDL3)
target_link_libraries(zwidget PRIVATE SDL3::SDL3)
else() # needed for gzdoom compat for now
else()
target_link_libraries(zwidget PRIVATE ${SDL3_LIBRARY})
endif()
endif()
if(SDL2_FOUND AND NOT SDL3_FOUND)
target_include_directories(zwidget PRIVATE ${SDL2_INCLUDE_DIRS})
if(TARGET SDL2::SDL2)
target_link_libraries(zwidget PRIVATE SDL2::SDL2)
else() # needed for gzdoom compat for now
else()
target_link_libraries(zwidget PRIVATE ${SDL2_LIBRARY})
endif()
endif()
Expand Down Expand Up @@ -417,10 +426,13 @@ if(ZWIDGET_BUILD_EXAMPLE)
target_compile_definitions(zwidget_example PRIVATE UNICODE _UNICODE)
target_link_libraries(zwidget_example PRIVATE gdi32 user32 shell32 comdlg32)
elseif(APPLE)
target_link_libraries(zwidget_example PRIVATE "-framework Cocoa")
target_link_libraries(zwidget_example PRIVATE "-framework Cocoa" "-framework CoreVideo")
if(ENABLE_OPENGL AND OPENGL_FRAMEWORK)
target_link_libraries(zwidget_example PRIVATE "-framework OpenGL")
endif()
if(ENABLE_METAL AND METAL_FRAMEWORK)
target_link_libraries(zwidget_example PRIVATE "-framework Metal" "-framework QuartzCore")
endif()
else()
target_link_libraries(zwidget_example PRIVATE ${ZWIDGET_LIBS})
endif()
Expand Down
18 changes: 13 additions & 5 deletions src/core/resourcedata_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
}

CFRelease(fontURL);
// fontPath is __bridge_transfer, so it's autoreleased(ARC)
}
return { fontData };
}
Expand All @@ -59,16 +58,26 @@
SingleFontData fontData;
@autoreleasepool
{
NSFont* systemFont = [NSFont monospacedSystemFontOfSize:13.0 weight:NSFontWeightRegular]; // Use a default size
if (!systemFont)
NSFont* systemFont = nil;
if (@available(macOS 10.15, *)) {
systemFont = [NSFont monospacedSystemFontOfSize:13.0 weight:NSFontWeightRegular]; // Use a default size
}


if (!systemFont) {
// Fallback for older macOS versions
systemFont = [NSFont systemFontOfSize:13.0];
}

if (!systemFont)
// Double check after fallback
throw std::runtime_error("Failed to get system font");

CTFontRef ctFont = (__bridge CTFontRef)systemFont;
CFURLRef fontURL = (CFURLRef)CTFontCopyAttribute(ctFont, kCTFontURLAttribute);
if (!fontURL)
throw std::runtime_error("Failed to get font URL from system font");

// __bridge_transfer transfers ownership to ARC, so no manual CFRelease is needed
NSString* fontPath = (NSString*)CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle);
if (!fontPath)
throw std::runtime_error("Failed to convert font URL to file path");
Expand All @@ -90,7 +99,6 @@
}

CFRelease(fontURL);
// fontPath is __bridge_transfer, so it's autoreleased(ARC)
}
return { std::move(fontData) };
}
Expand Down
68 changes: 50 additions & 18 deletions src/window/cocoa/cocoa_display_backend.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,36 @@

#include "AppKitWrapper.h"

// Helper struct to hold timer callbacks without requiring a Widget owner
struct CocoaTimerData
{
std::function<void()> callback;
NSTimer* nstimer;
};

@interface ZWidgetTimerTarget : NSObject
{
Timer* timer;
CocoaTimerData* timerData;
}
- (id)initWithTimer:(Timer*)t;
- (id)initWithTimerData:(CocoaTimerData*)data;
- (void)timerFired:(NSTimer*)nstimer;
@end

@implementation ZWidgetTimerTarget
- (id)initWithTimer:(Timer*)t;
- (id)initWithTimerData:(CocoaTimerData*)data
{
self = [super init];
if (self)
{
timer = (Timer*)t;
timerData = data;
}
return self;
}

- (void)timerFired:(NSTimer*)nstimer
{
if (timer->FuncExpired)
timer->FuncExpired();
if (timerData && timerData->callback)
timerData->callback();
}
@end

Expand All @@ -42,6 +49,10 @@ - (void)timerFired:(NSTimer*)nstimer

CocoaDisplayBackend::CocoaDisplayBackend()
{
// CRITICAL: Initialize NSApp and set activation policy for keyboard events
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp finishLaunching];
}

CocoaDisplayBackend::~CocoaDisplayBackend()
Expand Down Expand Up @@ -70,26 +81,47 @@ - (void)timerFired:(NSTimer*)nstimer
void CocoaDisplayBackend::ExitLoop()
{
[NSApp stop:nil];

// Post a dummy event to wake up the event loop so stop: takes effect
NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
location:NSMakePoint(0, 0)
modifierFlags:0
timestamp:0
windowNumber:0
context:nil
subtype:0
data1:0
data2:0];
[NSApp postEvent:event atStart:YES];
}

void* CocoaDisplayBackend::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
{
Timer* timer = new Timer(nullptr);
timer->FuncExpired = onTimer;
ZWidgetTimerTarget* target = [[ZWidgetTimerTarget alloc] initWithTimer:timer];
NSTimer* nstimer = [NSTimer scheduledTimerWithTimeInterval:timeoutMilliseconds / 1000.0 target:target selector:@selector(timerFired:) userInfo:nil repeats:YES];
timer->SetTimerId((__bridge void*)nstimer); // No retain needed with ARC
// [target release]; // No release needed with ARC
return timer;
CocoaTimerData* timerData = new CocoaTimerData();
timerData->callback = onTimer;

ZWidgetTimerTarget* target = [[ZWidgetTimerTarget alloc] initWithTimerData:timerData];
NSTimer* nstimer = [NSTimer scheduledTimerWithTimeInterval:timeoutMilliseconds / 1000.0
target:target
selector:@selector(timerFired:)
userInfo:nil
repeats:YES];
timerData->nstimer = nstimer;

return timerData;
}

void CocoaDisplayBackend::StopTimer(void* timerID)
{
Timer* timer = static_cast<Timer*>(timerID);
NSTimer* nstimer = (__bridge NSTimer*)timer->GetTimerId();
[nstimer invalidate];
// [nstimer release]; // No release needed with ARC
delete timer;
CocoaTimerData* timerData = static_cast<CocoaTimerData*>(timerID);
if (timerData)
{
if (timerData->nstimer)
{
[timerData->nstimer invalidate];
}
delete timerData;
}
}

Size CocoaDisplayBackend::GetScreenSize()
Expand Down
Loading