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
6 changes: 5 additions & 1 deletion Engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ option(TEMPEST_BUILD_SHARED "Build shared Tempest." ON)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
if(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(CMAKE_OSX_DEPLOYMENT_TARGET 15.0)
else()
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
endif()
endif()

if(MSVC)
Expand Down
176 changes: 128 additions & 48 deletions Engine/system/api/iosapi.mm
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ static uintptr_t alignDown(uintptr_t val, uintptr_t align) {

static void drawFrame();

@class TempestWindow;

static TempestWindow* mainWindow = nil;
static UIWindowScene* windowScene = nil;
static std::atomic_bool isRunning{true};
static bool isApplicationActive = false;

@interface TempestWindow : UIWindow {
@public Tempest::Window* owner;
@public CADisplayLink* displayLink;
Expand Down Expand Up @@ -141,7 +148,7 @@ - (void)displayLinkDidFire:(CADisplayLink*)sender {
TempestWindow* const window = self;
// Let UIKit unwind the display-link callback before resuming the engine.
dispatch_async(dispatch_get_main_queue(), ^{
if(window->owner==nullptr || window->displayLink!=sender)
if(window->owner==nullptr || window->displayLink!=sender || !isApplicationActive)
return;
swapContext();
});
Expand Down Expand Up @@ -229,9 +236,6 @@ static void discardPendingEvent(TempestWindow* window) {
window->curentEvent = Event::NoEvent;
}

static TempestWindow* mainWindow = nullptr;


@interface ViewController:UIViewController{}
-(id)init;
@end
Expand Down Expand Up @@ -284,59 +288,107 @@ -(bool)isFullscreen {
}
@end

@interface AppDelegate : NSObject <UIApplicationDelegate> {
static void createDisplayLink(TempestWindow* window) {
if(window==nil || window->owner==nullptr)
return;
if(window->displayLink==nil) {
window->displayLink = [CADisplayLink displayLinkWithTarget:window
selector:@selector(displayLinkDidFire:)];
[window->displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
window->displayLink.paused = !isApplicationActive;
window->hasPendingFrame.store(true);
}
@end

static bool isApplicationActive = false;
static void configureWindowForScene(TempestWindow* window, UIWindowScene* scene) {
if(@available(iOS 26.0, *)) {
window.frame = scene.effectiveGeometry.coordinateSpace.bounds;
} else {
window.frame = scene.coordinateSpace.bounds;
}
window.contentScaleFactor = scene.screen.scale;
}

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
(void)application;
(void)launchOptions;

CGRect frame = [ [ UIScreen mainScreen ] bounds ];
TempestWindow * window = [ [ TempestWindow alloc ] initWithFrame: frame];
window.contentScaleFactor = [UIScreen mainScreen].scale;
ViewController* controller = [ViewController new];
[window setRootViewController:controller];
[controller release];
window.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
window.backgroundColor = [ UIColor blackColor ];

window->owner = nullptr;
window->displayLink = nullptr;
static void activateWindow(TempestWindow* window) {
isApplicationActive = true;
createDisplayLink(window);
}

static void deactivateWindow(TempestWindow* window) {
isApplicationActive = false;
if(window==nil)
return;
window->hasPendingFrame.store(false);
window->curentEvent = Event::Type::NoEvent;

mainWindow = window;
[ window makeKeyAndVisible ]; // possible switch here
return YES;
if(window->displayLink!=nil)
window->displayLink.paused = YES;
}

- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
@interface TempestSceneDelegate : UIResponder <UIWindowSceneDelegate> {
UIWindow* window;
}
@property(nonatomic, retain) UIWindow* window;
@end

- (void)applicationWillResignActive:(UIApplication *)application {
(void)application;
isApplicationActive = false;
@implementation TempestSceneDelegate
@synthesize window;

- (void)scene:(UIScene *)scene
willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions {
(void)session;
(void)connectionOptions;
if(![scene isKindOfClass:[UIWindowScene class]])
return;
windowScene = (UIWindowScene*)scene;
deactivateWindow(mainWindow);
}

- (void)sceneDidBecomeActive:(UIScene *)scene {
(void)scene;
activateWindow(mainWindow);
swapContext();
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
(void)application;
- (void)sceneWillResignActive:(UIScene *)scene {
(void)scene;
deactivateWindow(mainWindow);
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
(void)application;
- (void)sceneDidDisconnect:(UIScene *)scene {
(void)scene;
deactivateWindow(mainWindow);
isRunning.store(false);
// Let UIKit return before the engine performs its teardown.
dispatch_async(dispatch_get_main_queue(), ^{
swapContext();
});
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
- (void)dealloc {
[window release];
[super dealloc];
}
@end

@interface AppDelegate : NSObject <UIApplicationDelegate> {
}
@end

@implementation AppDelegate
- (UISceneConfiguration *)application:(UIApplication *)application
configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession
options:(UISceneConnectionOptions *)options {
(void)application;
isApplicationActive = true;
swapContext();
(void)options;
UISceneConfiguration* configuration = connectingSceneSession.configuration;
configuration.delegateClass = [TempestSceneDelegate class];
return configuration;
}

- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
}

- (void)applicationWillTerminate:(UIApplication *)application {
Expand All @@ -349,12 +401,13 @@ - (void)applicationWillTerminate:(UIApplication *)application {
jmp_buf jmp = {};
};

static std::atomic_bool isRunning{true};
static Fiber mainContext;
static Fiber appleContext;
static Fiber* currentContext = nullptr;
alignas(16) static char appleStack[1*1024*1024]={};
static void appleMain(void*);
// createAppleSubContext changes sp before this call. Keep appleMain separate so
// its locals are allocated below the top of appleStack, not above the buffer.
__attribute__((noinline)) static void appleMain(void*);

inline static void createAppleSubContext() {
if(_setjmp(mainContext.jmp) == 0) {
Expand Down Expand Up @@ -410,12 +463,39 @@ static void appleMain(void*) {

static SystemApi::Window* createWindow(Tempest::Window *owner, uint32_t w, uint32_t h, SystemApi::ShowMode mode) {
auto window = mainWindow;

if(window!=nil && window->owner!=nullptr)
return nullptr;

if(window==nil) {
if(windowScene==nil)
return nullptr;
window = [[TempestWindow alloc] initWithWindowScene:windowScene];

if(window==nil)
return nullptr;

configureWindowForScene(window,windowScene);

ViewController* controller = [ViewController new];
[window setRootViewController:controller];
[controller release];
window.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
window.backgroundColor = [UIColor blackColor];
window->owner = nullptr;
window->displayLink = nil;
window->hasPendingFrame.store(false);
window->curentEvent = Event::Type::NoEvent;
mainWindow = window;
}

auto delegate = (TempestSceneDelegate*)windowScene.delegate;
delegate.window = window;

[window makeKeyAndVisible];

window->owner = owner;
window->displayLink = [CADisplayLink displayLinkWithTarget:window selector:@selector(displayLinkDidFire:)];
//by adding the display link to the run loop our draw method will be called 60 times per second
[window->displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
window->hasPendingFrame.store(true);
createDisplayLink(window);

return reinterpret_cast<SystemApi::Window*>(window);
}
Expand Down
Loading