yaze 0.2.0
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
main.mm
Go to the documentation of this file.
1// yaze iOS Application
2// Uses SDL2 and ImGui
3
4#import <Foundation/Foundation.h>
5
6#if TARGET_OS_OSX
7#import <Cocoa/Cocoa.h>
8#else
9#import <UIKit/UIKit.h>
10#endif
11
12#import <Metal/Metal.h>
13#import <MetalKit/MetalKit.h>
14
15#import <MobileCoreServices/MobileCoreServices.h>
16#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
17
18#include "app/core/controller.h"
20
21#include <SDL.h>
22
23#ifdef main
24#undef main
25#endif
26
28#include "imgui/backends/imgui_impl_metal.h"
29#include "imgui/imgui.h"
30
31// ----------------------------------------------------------------------------
32// AppViewController
33// ----------------------------------------------------------------------------
34
35@implementation AppViewController
36
37- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
38 bundle:(nullable NSBundle *)nibBundleOrNil {
39 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
40
41 _device = MTLCreateSystemDefaultDevice();
42 _commandQueue = [_device newCommandQueue];
43
44 if (!self.device) {
45 NSLog(@"Metal is not supported");
46 abort();
47 }
48
49 _controller = new yaze::app::core::Controller();
50
51 SDL_SetMainReady();
52 SDL_iOSSetEventPump(SDL_TRUE);
53 int argc = NSProcessInfo.processInfo.arguments.count;
54 char **argv = new char *[argc];
55 for (int i = 0; i < argc; i++) {
56 NSString *arg = NSProcessInfo.processInfo.arguments[i];
57 const char *cString = [arg UTF8String];
58 argv[i] = new char[strlen(cString) + 1];
59 strcpy(argv[i], cString);
60 }
61
62 std::string rom_filename = "";
63 if (argc > 1) {
64 rom_filename = argv[1];
65 }
66 SDL_iOSSetEventPump(SDL_FALSE);
67
68 // Enable native IME.
69 SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
70 if (!_controller->CreateWindow().ok()) {
71 printf("Error creating window: %s\n", SDL_GetError());
72 abort();
73 }
74 if (!_controller->CreateRenderer().ok()) {
75 printf("Error creating renderer: %s\n", SDL_GetError());
76 abort();
77 }
78
79 // Setup Dear ImGui context
80 IMGUI_CHECKVERSION();
81 ImGui::CreateContext();
82 ImGuiIO &io = ImGui::GetIO();
83 (void)io;
84 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
85 io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
86
88
89 ImGui_ImplSDL2_InitForSDLRenderer(_controller->window(), _controller->renderer());
90 ImGui_ImplSDLRenderer2_Init(_controller->renderer());
91
92 if (!_controller->LoadFontFamilies().ok()) {
93 abort();
94 }
95 _controller->SetupScreen(rom_filename);
96 _controller->set_active(true);
97
98 _hoverGestureRecognizer =
99 [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(HoverGesture:)];
100 [self.view addGestureRecognizer:_hoverGestureRecognizer];
101
102 _pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
103 action:@selector(HandlePinch:)];
104 [self.view addGestureRecognizer:_pinchRecognizer];
105
106 _longPressRecognizer =
107 [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
108 [self.view addGestureRecognizer:_longPressRecognizer];
109
110 _swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
111 action:@selector(HandleSwipe:)];
112 _swipeRecognizer.direction =
113 UISwipeGestureRecognizerDirectionRight | UISwipeGestureRecognizerDirectionLeft;
114 [self.view addGestureRecognizer:_swipeRecognizer];
115 return self;
116}
117
118- (MTKView *)mtkView {
119 return (MTKView *)self.view;
120}
121
122- (void)loadView {
123 self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 720)];
124}
125
126- (void)viewDidLoad {
127 [super viewDidLoad];
128
129 self.mtkView.device = self.device;
130 self.mtkView.delegate = self;
131
132#if TARGET_OS_OSX
133 ImGui_ImplOSX_Init(self.view);
134 [NSApp activateIgnoringOtherApps:YES];
135#endif
136}
137
138- (void)drawInMTKView:(MTKView *)view {
139 ImGuiIO &io = ImGui::GetIO();
140 io.DisplaySize.x = view.bounds.size.width;
141 io.DisplaySize.y = view.bounds.size.height;
142
143#if TARGET_OS_OSX
144 CGFloat framebufferScale =
145 view.window.screen.backingScaleFactor ?: NSScreen.mainScreen.backingScaleFactor;
146#else
147 CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale;
148#endif
149 io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale);
150
151 ImGui_ImplSDLRenderer2_NewFrame();
152 ImGui_ImplSDL2_NewFrame();
153#if TARGET_OS_OSX
154 ImGui_ImplOSX_NewFrame(view);
155#endif
156
157 ImGui::NewFrame();
158 ImGui::SetNextWindowPos(ImVec2(0, 0));
159 ImVec2 dimensions(io.DisplaySize.x, io.DisplaySize.y);
160 ImGui::SetNextWindowSize(dimensions, ImGuiCond_Always);
161 if (ImGui::Begin("##YazeMain", nullptr,
162 ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse |
163 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar |
164 ImGuiWindowFlags_NoBringToFrontOnFocus)) {
165 auto controller_status = _controller->OnLoad();
166 if (!controller_status.ok()) {
167 abort();
168 }
169 ImGui::End();
170 }
171 _controller->DoRender();
172}
173
174- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
175}
176
177// ----------------------------------------------------------------------------
178// Input processing
179// ----------------------------------------------------------------------------
180
181#if TARGET_OS_OSX
182
183- (void)viewWillAppear {
184 [super viewWillAppear];
185 self.view.window.delegate = self;
186}
187
188- (void)windowWillClose:(NSNotification *)notification {
189 ImGui_ImplMetal_Shutdown();
190 ImGui_ImplOSX_Shutdown();
191 ImGui::DestroyContext();
192}
193
194#else
195
196// This touch mapping is super cheesy/hacky. We treat any touch on the screen
197// as if it were a depressed left mouse button, and we don't bother handling
198// multitouch correctly at all. This causes the "cursor" to behave very erratically
199// when there are multiple active touches. But for demo purposes, single-touch
200// interaction actually works surprisingly well.
201- (void)UpdateIOWithTouchEvent:(UIEvent *)event {
202 UITouch *anyTouch = event.allTouches.anyObject;
203 CGPoint touchLocation = [anyTouch locationInView:self.view];
204 ImGuiIO &io = ImGui::GetIO();
205 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
206 io.AddMousePosEvent(touchLocation.x, touchLocation.y);
207
208 BOOL hasActiveTouch = NO;
209 for (UITouch *touch in event.allTouches) {
210 if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled) {
211 hasActiveTouch = YES;
212 break;
213 }
214 }
215 io.AddMouseButtonEvent(0, hasActiveTouch);
216}
217
218- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
219 [self UpdateIOWithTouchEvent:event];
220}
221- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
222 [self UpdateIOWithTouchEvent:event];
223}
224- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
225 [self UpdateIOWithTouchEvent:event];
226}
227- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
228 [self UpdateIOWithTouchEvent:event];
229}
230
231- (void)HoverGesture:(UIHoverGestureRecognizer *)gesture {
232 ImGuiIO &io = ImGui::GetIO();
233 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
234 // Cast to UIGestureRecognizer to UIGestureRecognizer to get locationInView
235 UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
236 if (gesture.zOffset < 0.50) {
237 io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
238 [gestureRecognizer locationInView:self.view].y);
239 }
240}
241
242- (void)HandlePinch:(UIPinchGestureRecognizer *)gesture {
243 ImGuiIO &io = ImGui::GetIO();
244 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
245 io.AddMouseWheelEvent(0.0f, gesture.scale);
246}
247
248- (void)HandleSwipe:(UISwipeGestureRecognizer *)gesture {
249 ImGuiIO &io = ImGui::GetIO();
250 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
251 if (gesture.direction == UISwipeGestureRecognizerDirectionRight) {
252 io.AddMouseWheelEvent(1.0f, 0.0f); // Swipe Right
253 } else if (gesture.direction == UISwipeGestureRecognizerDirectionLeft) {
254 io.AddMouseWheelEvent(-1.0f, 0.0f); // Swipe Left
255 }
256}
257
258- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
259 ImGuiIO &io = ImGui::GetIO();
260 io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
261 io.AddMouseButtonEvent(1, gesture.state == UIGestureRecognizerStateBegan);
262}
263
264#endif
265
266@end
267
268// ----------------------------------------------------------------------------
269// AppDelegate
270// ----------------------------------------------------------------------------
271
272#if TARGET_OS_OSX
273
274@interface AppDelegate : NSObject <NSApplicationDelegate>
275@property(nonatomic, strong) NSWindow *window;
276@end
277
278@implementation AppDelegate
279
280- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
281 return YES;
282}
283
284- (instancetype)init {
285 if (self = [super init]) {
286 NSViewController *rootViewController = [[AppViewController alloc] initWithNibName:nil
287 bundle:nil];
288 self.window = [[NSWindow alloc]
289 initWithContentRect:NSZeroRect
290 styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
291 NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable
292 backing:NSBackingStoreBuffered
293 defer:NO];
294 self.window.contentViewController = rootViewController;
295 [self.window center];
296 [self.window makeKeyAndOrderFront:self];
297 }
298 return self;
299}
300
301@end
302
303#else
304
305@implementation AppDelegate
306
307- (BOOL)application:(UIApplication *)application
308 didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
309 UIViewController *rootViewController = [[AppViewController alloc] init];
310 self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
311 self.window.rootViewController = rootViewController;
312 [self.window makeKeyAndVisible];
313 return YES;
314}
315
316- (void)applicationWillTerminate:(UIApplication *)application {
317 ImGui_ImplSDLRenderer2_Shutdown();
318 ImGui_ImplSDL2_Shutdown();
319 ImGui::DestroyContext();
320 SDL_Quit();
321}
322
323- (void)PresentDocumentPickerWithCompletionHandler:
324 (void (^)(NSString *selectedFile))completionHandler {
325 self.completionHandler = completionHandler;
326
327 NSArray *documentTypes = @[ [UTType typeWithIdentifier:@"org.halext.sfc"] ];
328 UIViewController *rootViewController = self.window.rootViewController;
329 _documentPicker =
330 [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:documentTypes];
331 _documentPicker.delegate = self;
332 _documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
333
334 [rootViewController presentViewController:_documentPicker animated:YES completion:nil];
335}
336
337- (void)documentPicker:(UIDocumentPickerViewController *)controller
338 didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
339 NSURL *selectedFileURL = [urls firstObject];
340
341 if (self.completionHandler) {
342 if (selectedFileURL) {
343 self.completionHandler(selectedFileURL.path);
344 std::string fileName = std::string([selectedFileURL.path UTF8String]);
345
346 // Extract the data from the file
347 [selectedFileURL startAccessingSecurityScopedResource];
348
349 auto data = [NSData dataWithContentsOfURL:selectedFileURL];
350 // Cast NSData* to uint8_t*
351 uchar *bytes = (uchar *)[data bytes];
352 // Size of the data
353 size_t size = [data length];
354
355 PRINT_IF_ERROR(yaze::app::SharedRom::shared_rom_->LoadFromPointer(bytes, size));
356 std::string filename = std::string([selectedFileURL.path UTF8String]);
357 yaze::app::SharedRom::shared_rom_->set_filename(filename);
358 [selectedFileURL stopAccessingSecurityScopedResource];
359
360 } else {
361 self.completionHandler(@"");
362 }
363 }
364 self.completionHandler = nil;
365 [controller dismissViewControllerAnimated:YES completion:nil];
366}
367
368- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
369 if (self.completionHandler) {
370 self.completionHandler(@"");
371 }
372 self.completionHandler = nil;
373 [controller dismissViewControllerAnimated:YES completion:nil];
374}
375
376@end
377
378#endif
379
380// ----------------------------------------------------------------------------
381// Application main() function
382// ----------------------------------------------------------------------------
383
384#if TARGET_OS_OSX
385int main(int argc, const char *argv[]) { return NSApplicationMain(argc, argv); }
386#else
387
388int main(int argc, char *argv[]) {
389 @autoreleasepool {
390 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
391 }
392}
393
394#endif
static std::shared_ptr< Rom > shared_rom_
Definition rom.h:597
Main controller for the application.
Definition controller.h:30
#define PRINT_IF_ERROR(expression)
Definition constants.h:36
unsigned char uchar
Definition constants.h:114
int main(int argc, char **argv)
Main entry point for the application.
Definition emu.cc:33
void ColorsYaze()
Definition style.cc:431