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>
48#include "imgui/backends/imgui_impl_sdl2.h"
49#include "imgui/backends/imgui_impl_sdlrenderer2.h"
50#include "imgui/imgui.h"
58- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
59 bundle:(nullable NSBundle *)nibBundleOrNil {
60 self = [
super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
62 _device = MTLCreateSystemDefaultDevice();
63 _commandQueue = [_device newCommandQueue];
66 NSLog(
@"Metal is not supported");
72#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
73 SDL_iOSSetEventPump(SDL_TRUE);
77 int argc = NSProcessInfo.processInfo.arguments.count;
78 char **argv =
new char *[argc];
79 for (
int i = 0; i < argc; i++) {
80 NSString *arg = NSProcessInfo.processInfo.arguments[i];
81 const char *cString = [arg UTF8String];
82 argv[i] =
new char[strlen(cString) + 1];
83 strcpy(argv[i], cString);
86 std::string rom_filename =
"";
88 rom_filename = argv[1];
92 for (
int i = 0; i < argc; i++) {
97#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
98 SDL_iOSSetEventPump(SDL_FALSE);
102 SDL_SetHint(SDL_HINT_IME_SHOW_UI,
"1");
112 if (!self.controller) {
113 NSLog(
@"Failed to initialize application controller");
118 _hoverGestureRecognizer =
119 [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(HoverGesture:)];
120 [
self.view addGestureRecognizer:_hoverGestureRecognizer];
122 _pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
123 action:@selector(HandlePinch:)];
124 [
self.view addGestureRecognizer:_pinchRecognizer];
126 _longPressRecognizer =
127 [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
128 [
self.view addGestureRecognizer:_longPressRecognizer];
130 _swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
131 action:@selector(HandleSwipe:)];
132 _swipeRecognizer.direction =
133 UISwipeGestureRecognizerDirectionRight | UISwipeGestureRecognizerDirectionLeft;
134 [
self.view addGestureRecognizer:_swipeRecognizer];
139- (MTKView *)mtkView {
140 return (MTKView *)self.view;
144 self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 720)];
150 self.mtkView.device = self.device;
151 self.mtkView.delegate = self;
154- (void)drawInMTKView:(MTKView *)view {
156 if (!app.IsReady() || !app.GetController()->IsActive())
return;
163 ImGuiIO &io = ImGui::GetIO();
164 io.DisplaySize.x = view.bounds.size.width;
165 io.DisplaySize.y = view.bounds.size.height;
167 CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale;
168 io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale);
173- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
187- (void)UpdateIOWithTouchEvent:(UIEvent *)event {
188 UITouch *anyTouch =
event.allTouches.anyObject;
189 CGPoint touchLocation = [anyTouch locationInView:self.view];
190 ImGuiIO &io = ImGui::GetIO();
191 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
192 io.AddMousePosEvent(touchLocation.x, touchLocation.y);
194 BOOL hasActiveTouch = NO;
195 for (UITouch *touch in event.allTouches) {
196 if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled) {
197 hasActiveTouch = YES;
201 io.AddMouseButtonEvent(0, hasActiveTouch);
204- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
205 [
self UpdateIOWithTouchEvent:event];
207- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
208 [
self UpdateIOWithTouchEvent:event];
210- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
211 [
self UpdateIOWithTouchEvent:event];
213- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
214 [
self UpdateIOWithTouchEvent:event];
217- (void)HoverGesture:(UIHoverGestureRecognizer *)gesture {
218 ImGuiIO &io = ImGui::GetIO();
219 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
221 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
222 if (gesture.zOffset < 0.50) {
223 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
224 [gestureRecognizer locationInView:self.view].y);
228- (void)HandlePinch:(UIPinchGestureRecognizer *)gesture {
229 ImGuiIO &io = ImGui::GetIO();
230 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
231 io.AddMouseWheelEvent(0.0f, gesture.scale);
232 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
233 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
234 [gestureRecognizer locationInView:self.view].y);
237- (void)HandleSwipe:(UISwipeGestureRecognizer *)gesture {
238 ImGuiIO &io = ImGui::GetIO();
239 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
240 if (gesture.direction == UISwipeGestureRecognizerDirectionRight) {
241 io.AddMouseWheelEvent(1.0f, 0.0f);
242 }
else if (gesture.direction == UISwipeGestureRecognizerDirectionLeft) {
243 io.AddMouseWheelEvent(-1.0f, 0.0f);
245 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
246 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
247 [gestureRecognizer locationInView:self.view].y);
250- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
251 ImGuiIO &io = ImGui::GetIO();
252 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
253 io.AddMouseButtonEvent(1, gesture.state == UIGestureRecognizerStateBegan);
254 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
255 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
256 [gestureRecognizer locationInView:self.view].y);
269@interface AppDelegate : NSObject <NSApplicationDelegate>
270@property(nonatomic, strong) NSWindow *window;
275- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
279- (instancetype)init {
280 if (self = [super init]) {
281 NSViewController *rootViewController = [[
AppViewController alloc] initWithNibName:nil
283 self.window = [[NSWindow alloc]
284 initWithContentRect:NSZeroRect
285 styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
286 NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable
287 backing:NSBackingStoreBuffered
289 self.window.contentViewController = rootViewController;
290 [
self.window center];
291 [
self.window makeKeyAndOrderFront:self];
302- (BOOL)application:(UIApplication *)application
303 didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,
id> *)launchOptions {
305 self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
306 self.window.rootViewController = rootViewController;
307 [
self.window makeKeyAndVisible];
311- (void)applicationWillTerminate:(UIApplication *)application {
315- (void)PresentDocumentPickerWithCompletionHandler:
316 (
void (^)(NSString *selectedFile))completionHandler {
317 self.completionHandler = completionHandler;
319 NSArray *documentTypes = @[ [UTType typeWithIdentifier:@"org.halext.sfc"] ];
320 UIViewController *rootViewController = self.window.rootViewController;
322 [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:documentTypes];
323 _documentPicker.delegate = self;
324 _documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
326 [rootViewController presentViewController:_documentPicker animated:YES completion:nil];
329- (void)documentPicker:(UIDocumentPickerViewController *)controller
330 didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
331 NSURL *selectedFileURL = [urls firstObject];
333 if (self.completionHandler) {
334 if (selectedFileURL) {
335 self.completionHandler(selectedFileURL.path);
338 NSString *tempDir = NSTemporaryDirectory();
339 NSString *fileName = [selectedFileURL lastPathComponent];
340 NSString *tempPath = [tempDir stringByAppendingPathComponent:fileName];
341 NSURL *tempURL = [NSURL fileURLWithPath:tempPath];
344 NSError *error = nil;
345 [[NSFileManager defaultManager] removeItemAtURL:tempURL error:nil];
347 [selectedFileURL startAccessingSecurityScopedResource];
348 BOOL success = [[NSFileManager defaultManager] copyItemAtURL:selectedFileURL toURL:tempURL error:&error];
349 [selectedFileURL stopAccessingSecurityScopedResource];
352 std::string cppPath = std::string([tempPath UTF8String]);
353 NSLog(
@"ROM copied to temporary path: %s", cppPath.c_str());
359 NSLog(
@"Failed to copy ROM to temp directory: %@", error);
362 self.completionHandler(
@"");
365 self.completionHandler = nil;
366 [controller dismissViewControllerAnimated:YES completion:nil];
369- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
370 if (self.completionHandler) {
371 self.completionHandler(
@"");
373 self.completionHandler = nil;
374 [controller dismissViewControllerAnimated:YES completion:nil];
386int main(
int argc,
const char *argv[]) {
return NSApplicationMain(argc, argv); }
389int main(
int argc,
char *argv[]) {
391 return UIApplicationMain(argc, argv, nil, NSStringFromClass([
AppDelegate class]));
Controller * GetController()
static Application & Instance()
void Initialize(const AppConfig &config)
void LoadRom(const std::string &path)
int main(int argc, char **argv)
SDL2/SDL3 compatibility layer.
Configuration options for the application startup.