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