18#import <Foundation/Foundation.h>
21#import <Cocoa/Cocoa.h>
23#import <UIKit/UIKit.h>
26#import <Metal/Metal.h>
27#import <MetalKit/MetalKit.h>
28#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
49#include "imgui/backends/imgui_impl_sdl2.h"
50#include "imgui/backends/imgui_impl_sdlrenderer2.h"
51#include "imgui/imgui.h"
67- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
68 bundle:(nullable NSBundle *)nibBundleOrNil {
69 self = [
super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
71 _device = MTLCreateSystemDefaultDevice();
72 _commandQueue = [_device newCommandQueue];
75 NSLog(
@"Metal is not supported");
81#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
82 SDL_SetHint(SDL_HINT_AUDIO_CATEGORY,
"ambient");
83 if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) {
84 NSLog(
@"SDL_Init failed: %s", SDL_GetError());
87#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
88 SDL_iOSSetEventPump(SDL_TRUE);
92 int argc = NSProcessInfo.processInfo.arguments.count;
93 char **argv =
new char *[argc];
94 for (
int i = 0; i < argc; i++) {
95 NSString *arg = NSProcessInfo.processInfo.arguments[i];
96 const char *cString = [arg UTF8String];
97 argv[i] =
new char[strlen(cString) + 1];
98 strcpy(argv[i], cString);
101 std::string rom_filename =
"";
103 rom_filename = argv[1];
107 for (
int i = 0; i < argc; i++) {
112#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
113 SDL_iOSSetEventPump(SDL_FALSE);
117 SDL_SetHint(SDL_HINT_IME_SHOW_UI,
"1");
122 app_config_ = config;
126 _hoverGestureRecognizer =
127 [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(HoverGesture:)];
128 _hoverGestureRecognizer.cancelsTouchesInView = NO;
129 _hoverGestureRecognizer.delegate = self;
130 [
self.view addGestureRecognizer:_hoverGestureRecognizer];
132 _pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
133 action:@selector(HandlePinch:)];
134 _pinchRecognizer.cancelsTouchesInView = NO;
135 _pinchRecognizer.delegate = self;
136 [
self.view addGestureRecognizer:_pinchRecognizer];
138 _longPressRecognizer =
139 [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
140 _longPressRecognizer.cancelsTouchesInView = NO;
141 _longPressRecognizer.delegate = self;
142 [
self.view addGestureRecognizer:_longPressRecognizer];
144 _swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
145 action:@selector(HandleSwipe:)];
146 _swipeRecognizer.direction =
147 UISwipeGestureRecognizerDirectionRight | UISwipeGestureRecognizerDirectionLeft;
148 _swipeRecognizer.cancelsTouchesInView = NO;
149 _swipeRecognizer.delegate = self;
150 [
self.view addGestureRecognizer:_swipeRecognizer];
155- (MTKView *)mtkView {
156 return (MTKView *)self.view;
160 self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 720)];
166 self.view.multipleTouchEnabled = YES;
168 self.mtkView.device = self.device;
169 self.mtkView.delegate = self;
177 NSLog(
@"Failed to initialize iOS host: %s",
178 std::string(status.message()).c_str());
183 if (!self.controller) {
184 NSLog(
@"Failed to initialize application controller");
191- (void)drawInMTKView:(MTKView *)view {
202 ImGuiIO &io = ImGui::GetIO();
203 io.DisplaySize.x = view.bounds.size.width;
204 io.DisplaySize.y = view.bounds.size.height;
206 CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale;
207 io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale);
212- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
226- (void)UpdateIOWithTouchEvent:(UIEvent *)event {
227 ImGuiIO &io = ImGui::GetIO();
228 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
230 UITouch *active_touch = nil;
239 for (UITouch *touch in event.allTouches) {
240 if (touch.phase != UITouchPhaseEnded &&
241 touch.phase != UITouchPhaseCancelled) {
242 active_touch = touch;
250 CGPoint touchLocation = [active_touch locationInView:self.view];
251 io.AddMousePosEvent(touchLocation.x, touchLocation.y);
252 io.AddMouseButtonEvent(0,
true);
255 io.AddMouseButtonEvent(0,
false);
259- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
260 [
self UpdateIOWithTouchEvent:event];
262- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
263 [
self UpdateIOWithTouchEvent:event];
265- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
266 [
self UpdateIOWithTouchEvent:event];
268- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
269 [
self UpdateIOWithTouchEvent:event];
272- (void)HoverGesture:(UIHoverGestureRecognizer *)gesture {
273 ImGuiIO &io = ImGui::GetIO();
274 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
276 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
277 if (gesture.zOffset < 0.50) {
278 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
279 [gestureRecognizer locationInView:self.view].y);
283- (void)HandlePinch:(UIPinchGestureRecognizer *)gesture {
284 ImGuiIO &io = ImGui::GetIO();
285 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
286 io.AddMouseWheelEvent(0.0f, gesture.scale);
287 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
288 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
289 [gestureRecognizer locationInView:self.view].y);
292- (void)HandleSwipe:(UISwipeGestureRecognizer *)gesture {
293 ImGuiIO &io = ImGui::GetIO();
294 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
295 if (gesture.direction == UISwipeGestureRecognizerDirectionRight) {
296 io.AddMouseWheelEvent(1.0f, 0.0f);
297 }
else if (gesture.direction == UISwipeGestureRecognizerDirectionLeft) {
298 io.AddMouseWheelEvent(-1.0f, 0.0f);
300 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
301 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
302 [gestureRecognizer locationInView:self.view].y);
305- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
306 ImGuiIO &io = ImGui::GetIO();
307 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
308 io.AddMouseButtonEvent(1, gesture.state == UIGestureRecognizerStateBegan);
309 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
310 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
311 [gestureRecognizer locationInView:self.view].y);
314- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
315 shouldRecognizeSimultaneouslyWithGestureRecognizer:
316 (UIGestureRecognizer *)otherGestureRecognizer {
317 (void)gestureRecognizer;
318 (void)otherGestureRecognizer;
333@property(nonatomic, strong) UIWindow *
window;
338- (void)scene:(UIScene *)scene
339 willConnectToSession:(UISceneSession *)session
340 options:(UISceneConnectionOptions *)connectionOptions {
341 if (![scene isKindOfClass:[UIWindowScene class]]) {
344 UIWindowScene *windowScene = (UIWindowScene *)scene;
346 self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
347 self.window.rootViewController = rootViewController;
348 [
self.window makeKeyAndVisible];
361@interface AppDelegate : NSObject <NSApplicationDelegate>
362@property(nonatomic, strong) NSWindow *window;
367- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
371- (instancetype)init {
372 if (self = [super init]) {
373 NSViewController *rootViewController = [[
AppViewController alloc] initWithNibName:nil
375 self.window = [[NSWindow alloc]
376 initWithContentRect:NSZeroRect
377 styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
378 NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable
379 backing:NSBackingStoreBuffered
381 self.window.contentViewController = rootViewController;
382 [
self.window center];
383 [
self.window makeKeyAndOrderFront:self];
392@interface AppDelegate : UIResponder <UIApplicationDelegate, UIDocumentPickerDelegate>
393@property(nonatomic, strong) UIWindow *window;
394@property(nonatomic, copy)
void (^completionHandler)(NSString *selectedFile);
395@property(nonatomic, strong) UIDocumentPickerViewController *documentPicker;
400- (BOOL)application:(UIApplication *)application
401 didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,
id> *)launchOptions {
402 if (@available(iOS 13.0, *)) {
406 self.
window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
407 self.window.rootViewController = rootViewController;
408 [
self.window makeKeyAndVisible];
412- (UISceneConfiguration *)application:(UIApplication *)application
413 configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession
414 options:(UISceneConnectionOptions *)options {
415 if (@available(iOS 13.0, *)) {
416 UISceneConfiguration *configuration =
417 [[UISceneConfiguration alloc] initWithName:@"Default Configuration"
418 sessionRole:connectingSceneSession.role];
420 configuration.sceneClass = [UIWindowScene class];
421 return configuration;
426- (void)applicationWillTerminate:(UIApplication *)application {
430- (UIViewController *)RootViewControllerForPresenting {
431 if (@available(iOS 13.0, *)) {
432 for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
433 if (scene.activationState != UISceneActivationStateForegroundActive) {
436 if (![scene isKindOfClass:[UIWindowScene class]]) {
439 UIWindowScene *windowScene = (UIWindowScene *)scene;
440 for (UIWindow *window in windowScene.windows) {
441 if (window.isKeyWindow && window.rootViewController) {
442 return window.rootViewController;
445 if (windowScene.windows.count > 0 &&
446 windowScene.windows.firstObject.rootViewController) {
447 return windowScene.windows.firstObject.rootViewController;
452 return self.window.rootViewController;
455- (void)PresentDocumentPickerWithCompletionHandler:
456 (
void (^)(NSString *selectedFile))completionHandler
457 allowedTypes:(NSArray<UTType*> *)allowedTypes {
458 self.completionHandler = completionHandler;
460 NSArray<UTType*>* documentTypes = allowedTypes;
461 if (!documentTypes || documentTypes.count == 0) {
462 documentTypes = @[ UTTypeData ];
464 UIViewController *rootViewController = [
self RootViewControllerForPresenting];
465 if (!rootViewController) {
466 if (self.completionHandler) {
467 self.completionHandler(
@"");
469 self.completionHandler = nil;
473 [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:documentTypes];
474 _documentPicker.delegate = self;
475 _documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
477 [rootViewController presentViewController:_documentPicker animated:YES completion:nil];
480- (void)documentPicker:(UIDocumentPickerViewController *)controller
481 didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
482 NSURL *selectedFileURL = [urls firstObject];
484 if (self.completionHandler) {
485 if (selectedFileURL) {
487 NSString *tempDir = NSTemporaryDirectory();
488 NSString *fileName = [selectedFileURL lastPathComponent];
489 NSString *tempPath = [tempDir stringByAppendingPathComponent:fileName];
490 NSURL *tempURL = [NSURL fileURLWithPath:tempPath];
493 NSError *error = nil;
494 [[NSFileManager defaultManager] removeItemAtURL:tempURL error:nil];
496 [selectedFileURL startAccessingSecurityScopedResource];
497 BOOL success = [[NSFileManager defaultManager] copyItemAtURL:selectedFileURL toURL:tempURL error:&error];
498 [selectedFileURL stopAccessingSecurityScopedResource];
501 std::string cppPath = std::string([tempPath UTF8String]);
502 NSLog(
@"File copied to temporary path: %s", cppPath.c_str());
503 self.completionHandler(tempPath);
505 NSLog(
@"Failed to copy ROM to temp directory: %@", error);
506 self.completionHandler(
@"");
509 self.completionHandler(
@"");
512 self.completionHandler = nil;
513 [controller dismissViewControllerAnimated:YES completion:nil];
516- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
517 if (self.completionHandler) {
518 self.completionHandler(
@"");
520 self.completionHandler = nil;
521 [controller dismissViewControllerAnimated:YES completion:nil];
533int main(
int argc,
const char *argv[]) {
return NSApplicationMain(argc, argv); }
536int main(
int argc,
char *argv[]) {
538 return UIApplicationMain(argc, argv, nil, NSStringFromClass([
AppDelegate class]));
Controller * GetController()
static Application & Instance()
absl::Status Initialize(const IOSHostConfig &config)
void SetMetalView(void *view)
int main(int argc, char **argv)
yaze::ios::IOSHost g_ios_host
SDL2/SDL3 compatibility layer.
Configuration options for the application startup.