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>
47#include "imgui/backends/imgui_impl_sdl2.h"
48#include "imgui/backends/imgui_impl_sdlrenderer2.h"
49#include "imgui/imgui.h"
57- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
58 bundle:(nullable NSBundle *)nibBundleOrNil {
59 self = [
super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
61 _device = MTLCreateSystemDefaultDevice();
62 _commandQueue = [_device newCommandQueue];
65 NSLog(
@"Metal is not supported");
71#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
72 SDL_iOSSetEventPump(SDL_TRUE);
76 int argc = NSProcessInfo.processInfo.arguments.count;
77 char **argv =
new char *[argc];
78 for (
int i = 0; i < argc; i++) {
79 NSString *arg = NSProcessInfo.processInfo.arguments[i];
80 const char *cString = [arg UTF8String];
81 argv[i] =
new char[strlen(cString) + 1];
82 strcpy(argv[i], cString);
85 std::string rom_filename =
"";
87 rom_filename = argv[1];
91 for (
int i = 0; i < argc; i++) {
96#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
97 SDL_iOSSetEventPump(SDL_FALSE);
101 SDL_SetHint(SDL_HINT_IME_SHOW_UI,
"1");
105 auto init_status = _controller->OnEntry(rom_filename);
106 if (!init_status.ok()) {
107 NSLog(
@"Failed to initialize controller: %s", init_status.message().data());
112 _hoverGestureRecognizer =
113 [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(HoverGesture:)];
114 [
self.view addGestureRecognizer:_hoverGestureRecognizer];
116 _pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
117 action:@selector(HandlePinch:)];
118 [
self.view addGestureRecognizer:_pinchRecognizer];
120 _longPressRecognizer =
121 [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
122 [
self.view addGestureRecognizer:_longPressRecognizer];
124 _swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
125 action:@selector(HandleSwipe:)];
126 _swipeRecognizer.direction =
127 UISwipeGestureRecognizerDirectionRight | UISwipeGestureRecognizerDirectionLeft;
128 [
self.view addGestureRecognizer:_swipeRecognizer];
133- (MTKView *)mtkView {
134 return (MTKView *)self.view;
138 self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 720)];
144 self.mtkView.device = self.device;
145 self.mtkView.delegate = self;
148- (void)drawInMTKView:(MTKView *)view {
149 if (!_controller->IsActive())
return;
152 _controller->OnInput();
155 ImGuiIO &io = ImGui::GetIO();
156 io.DisplaySize.x = view.bounds.size.width;
157 io.DisplaySize.y = view.bounds.size.height;
159 CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale;
160 io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale);
163 auto load_status = _controller->OnLoad();
164 if (!load_status.ok()) {
165 NSLog(
@"Controller OnLoad failed: %s", load_status.message().data());
169 _controller->DoRender();
172- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
186- (void)UpdateIOWithTouchEvent:(UIEvent *)event {
187 UITouch *anyTouch =
event.allTouches.anyObject;
188 CGPoint touchLocation = [anyTouch locationInView:self.view];
189 ImGuiIO &io = ImGui::GetIO();
190 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
191 io.AddMousePosEvent(touchLocation.x, touchLocation.y);
193 BOOL hasActiveTouch = NO;
194 for (UITouch *touch in event.allTouches) {
195 if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled) {
196 hasActiveTouch = YES;
200 io.AddMouseButtonEvent(0, hasActiveTouch);
203- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
204 [
self UpdateIOWithTouchEvent:event];
206- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
207 [
self UpdateIOWithTouchEvent:event];
209- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
210 [
self UpdateIOWithTouchEvent:event];
212- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
213 [
self UpdateIOWithTouchEvent:event];
216- (void)HoverGesture:(UIHoverGestureRecognizer *)gesture {
217 ImGuiIO &io = ImGui::GetIO();
218 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
220 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
221 if (gesture.zOffset < 0.50) {
222 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
223 [gestureRecognizer locationInView:self.view].y);
227- (void)HandlePinch:(UIPinchGestureRecognizer *)gesture {
228 ImGuiIO &io = ImGui::GetIO();
229 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
230 io.AddMouseWheelEvent(0.0f, gesture.scale);
231 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
232 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
233 [gestureRecognizer locationInView:self.view].y);
236- (void)HandleSwipe:(UISwipeGestureRecognizer *)gesture {
237 ImGuiIO &io = ImGui::GetIO();
238 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
239 if (gesture.direction == UISwipeGestureRecognizerDirectionRight) {
240 io.AddMouseWheelEvent(1.0f, 0.0f);
241 }
else if (gesture.direction == UISwipeGestureRecognizerDirectionLeft) {
242 io.AddMouseWheelEvent(-1.0f, 0.0f);
244 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
245 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
246 [gestureRecognizer locationInView:self.view].y);
249- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
250 ImGuiIO &io = ImGui::GetIO();
251 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
252 io.AddMouseButtonEvent(1, gesture.state == UIGestureRecognizerStateBegan);
253 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
254 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
255 [gestureRecognizer locationInView:self.view].y);
268@interface AppDelegate : NSObject <NSApplicationDelegate>
269@property(nonatomic, strong) NSWindow *window;
274- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
278- (instancetype)init {
279 if (self = [super init]) {
280 NSViewController *rootViewController = [[
AppViewController alloc] initWithNibName:nil
282 self.window = [[NSWindow alloc]
283 initWithContentRect:NSZeroRect
284 styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
285 NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable
286 backing:NSBackingStoreBuffered
288 self.window.contentViewController = rootViewController;
289 [
self.window center];
290 [
self.window makeKeyAndOrderFront:self];
301- (BOOL)application:(UIApplication *)application
302 didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,
id> *)launchOptions {
304 self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
305 self.window.rootViewController = rootViewController;
306 [
self.window makeKeyAndVisible];
310- (void)applicationWillTerminate:(UIApplication *)application {
313 if (viewController.controller) {
314 viewController.controller->OnExit();
315 delete viewController.controller;
316 viewController.controller =
nullptr;
320- (void)PresentDocumentPickerWithCompletionHandler:
321 (
void (^)(NSString *selectedFile))completionHandler {
322 self.completionHandler = completionHandler;
324 NSArray *documentTypes = @[ [UTType typeWithIdentifier:@"org.halext.sfc"] ];
325 UIViewController *rootViewController = self.window.rootViewController;
327 [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:documentTypes];
328 _documentPicker.delegate = self;
329 _documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
331 [rootViewController presentViewController:_documentPicker animated:YES completion:nil];
334- (void)documentPicker:(UIDocumentPickerViewController *)controller
335 didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
336 NSURL *selectedFileURL = [urls firstObject];
338 if (self.completionHandler) {
339 if (selectedFileURL) {
340 self.completionHandler(selectedFileURL.path);
341 std::string fileName = std::string([selectedFileURL.path UTF8String]);
344 [selectedFileURL startAccessingSecurityScopedResource];
346 auto data = [NSData dataWithContentsOfURL:selectedFileURL];
347 uint8_t *bytes = (uint8_t *)[data bytes];
348 size_t size = [data length];
350 std::vector<uint8_t> rom_data;
351 rom_data.resize(size);
352 std::copy(bytes, bytes + size, rom_data.begin());
357 if (viewController && viewController.controller) {
359 auto* current_rom = viewController.controller->GetCurrentRom();
361 auto load_status = current_rom->LoadFromData(rom_data);
362 if (load_status.ok()) {
363 current_rom->set_filename(fileName);
364 NSLog(
@"ROM loaded successfully from %s", fileName.c_str());
366 NSLog(
@"Failed to load ROM: %s", load_status.message().data());
369 NSLog(
@"No ROM instance available");
372 NSLog(
@"Controller not available");
375 [selectedFileURL stopAccessingSecurityScopedResource];
377 self.completionHandler(
@"");
380 self.completionHandler = nil;
381 [controller dismissViewControllerAnimated:YES completion:nil];
384- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
385 if (self.completionHandler) {
386 self.completionHandler(
@"");
388 self.completionHandler = nil;
389 [controller dismissViewControllerAnimated:YES completion:nil];
401int main(
int argc,
const char *argv[]) {
return NSApplicationMain(argc, argv); }
404int main(
int argc,
char *argv[]) {
406 return UIApplicationMain(argc, argv, nil, NSStringFromClass([
AppDelegate class]));
Main controller for the application.