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