Files
panopainter/PanoPainter/GameViewController.m
2019-10-19 23:44:55 +02:00

526 lines
17 KiB
Objective-C

//
// GameViewController.m
// PanoPainter
//
// Created by Omar Mohamed Ali Mudhir on 07/05/17.
// Copyright © 2017 Omar Mohamed Ali Mudhir. All rights reserved.
//
#include "pch.h"
#import "GameViewController.h"
#import <OpenGLES/ES3/glext.h>
#include "app.h"
#include "settings.h"
#import "objc_utils.h"
#import <MobileCoreServices/MobileCoreServices.h>
std::mutex render_mutex;
std::condition_variable render_cv;
@interface GameImagePicker : UIImagePickerController
{
@public std::promise<std::string> promise;
@public std::function<void(std::string)> callback;
}
@end
@implementation GameImagePicker
@end
@interface GameFilePicker : UIDocumentPickerViewController
{
@public std::promise<std::string> promise;
@public std::function<void(std::string)> callback;
}
@end
@implementation GameFilePicker
@end
//std::map<
bool pen_down = false;
int t_count = 0;
glm::vec2 t_pos;
int lock_count = 0;
NSThread* lock_thread;
std::recursive_mutex lock_mutex;
@implementation GameViewController
- (std::string)clipboard_get_string
{
NSString* ns = [[UIPasteboard generalPasteboard] string];
const char* ptr = [ns cStringUsingEncoding:NSUTF8StringEncoding];
return std::string(ptr);
}
- (bool)clipboard_set_string:(const std::string &)s
{
NSString* ns = [NSString stringWithUTF8String:s.c_str()];
[[UIPasteboard generalPasteboard] setString:ns];
return true;
}
- (void)crash
{
AppDelegate* app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[app hockeyapp_crash];
}
- (void)async_lock
{
lock_mutex.lock();
[EAGLContext setCurrentContext:context];
// GameView* view = (GameView*)self.view;
// [view bindDrawable];
}
- (void)async_unlock
{
lock_mutex.unlock();
}
- (void)async_swap
{
[context presentRenderbuffer:GL_FRAMEBUFFER];
[glview display];
}
- (void)share_file:(NSString *)file_path
{
NSURL *url = [NSURL fileURLWithPath:file_path];
NSArray *objectsToShare = @[url];
UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
controller.excludedActivityTypes = @[];
controller.popoverPresentationController.sourceView = self.view;
controller.popoverPresentationController.sourceRect = CGRectMake(self.view.bounds.size.width/2, self.view.bounds.size.height/4, 0, 0);
// Present the controller
[self presentViewController:controller animated:YES completion:nil];
}
-(void)display_file:(std::string)filename
{
NSString* filePath = [NSString stringWithUTF8String:filename.c_str()];
NSURL *url = [NSURL fileURLWithPath:filePath];
UIDocumentInteractionController *popup = [UIDocumentInteractionController interactionControllerWithURL:url];
[popup setDelegate:self];
[popup presentPreviewAnimated:YES];
}
-(UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller
{
return self;
}
- (void)init_dirs
{
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* docpath = (NSString*)[paths objectAtIndex:0];
App::I->data_path = [docpath cStringUsingEncoding:NSASCIIStringEncoding];
NSError* err = nil;
NSString* recpath = [docpath stringByAppendingString:@"/rec"];
App::I->rec_path = [recpath cStringUsingEncoding:NSASCIIStringEncoding];
if (![[NSFileManager defaultManager] createDirectoryAtPath:recpath withIntermediateDirectories:YES attributes:nil error:&err])
{
LOG("error creating rec path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
}
// brushes
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes"] withIntermediateDirectories:YES attributes:nil error:&err])
{
LOG("error creating brushes path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
}
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes/thumbs"] withIntermediateDirectories:YES attributes:nil error:&err])
{
LOG("error creating brushes thumbs path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
}
// patterns
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/patterns"] withIntermediateDirectories:YES attributes:nil error:&err])
{
LOG("error creating patterns path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
}
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/patterns/thumbs"] withIntermediateDirectories:YES attributes:nil error:&err])
{
LOG("error creating patterns thumbs path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
}
// settings
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/settings"] withIntermediateDirectories:YES attributes:nil error:&err])
{
LOG("error creating settings path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
}
}
- (void)pick_photo:(std::function<void(std::string)>) callback
{
GameImagePicker *picker = [[GameImagePicker alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker->callback = callback;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *chosenImage = [info[UIImagePickerControllerImageURL] path];
GameImagePicker* p = static_cast<GameImagePicker*>(picker);
std::string path = [chosenImage cStringUsingEncoding:NSUTF8StringEncoding];
p->callback(path);
[picker dismissViewControllerAnimated:YES completion:NULL];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[picker dismissViewControllerAnimated:YES completion:NULL];
}
- (void)pick_file:(NSArray<NSString*>*)types then:(std::function<void(std::string)>)callback
{
// convert extension into UTIs from the system database
NSMutableArray* UTIs = [NSMutableArray arrayWithCapacity:types.count];
for (NSString* ext : types)
{
NSString *typeForExt = (__bridge NSString*)
UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
(__bridge CFStringRef)ext, NULL);
[UTIs addObject:typeForExt];
}
GameFilePicker *picker = [[GameFilePicker alloc] initWithDocumentTypes:UTIs inMode:UIDocumentPickerModeImport];
picker.delegate = self;
picker->callback = callback;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)pick_file_save:(std::string)path
{
NSURL* url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path.c_str()]];
GameFilePicker *picker = [[GameFilePicker alloc] initWithURL:url inMode:UIDocumentPickerModeMoveToService];
picker.delegate = self;
[self presentViewController:picker animated:YES completion:NULL];
}
-(void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
{
NSString *chosenImage = [url path];
GameFilePicker* p = static_cast<GameFilePicker*>(controller);
if (p->callback)
{
std::string path = [chosenImage cStringUsingEncoding:NSUTF8StringEncoding];
p->callback(path);
}
[controller dismissViewControllerAnimated:YES completion:NULL];
}
-(void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller
{
[controller dismissViewControllerAnimated:YES completion:NULL];
}
- (void)insertText:(NSString *)text
{
if (const char* cstr = [text cStringUsingEncoding:NSISOLatin1StringEncoding])
{
char c = cstr[0];
App::I->ui_task_async([=]
{
App::I->key_char(c);
});
}
NSLog(@"%@", text);
// Do something with the typed character
}
- (void)deleteBackward {
App::I->ui_task_async([=]
{
App::I->key_char('\b');
});
// Handle the delete key
}
- (BOOL)hasText {
// Return whether there's any text present
return YES;
}
- (BOOL)canBecomeFirstResponder {
return input_enabled;
}
-(void)show_keyboard
{
input_enabled = YES;
[self becomeFirstResponder];
}
-(void)hide_keyboard
{
input_enabled = NO;
[self resignFirstResponder];
}
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeShown:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification object:nil];
}
- (void)unregisterForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardDidHideNotification object:nil];
}
- (void)keyboardWillBeShown:(NSNotification*)aNotification
{
App::I->redraw = true;
App::I->animate = true;
}
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
App::I->redraw = true;
App::I->animate = false;
//[self unregisterForKeyboardNotifications];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
GameView *view = (GameView *)self.view;
//CGRect frame = view.frame;
CGRect frame = [[UIScreen mainScreen] bounds];
frame.size.height -= kbSize.height;
view.frame = frame;
App::I->ui_task_async([size=frame.size, f=self.view.contentScaleFactor]
{
App::I->resize(size.width * f, size.height * f);
});
App::I->animate = false;
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
CGRect frame = [[UIScreen mainScreen] bounds];
self.view.frame = frame;
App::I->ui_task_async([size=frame.size, f=self.view.contentScaleFactor]
{
App::I->resize(size.width * f, size.height * f);
});
App::I->animate = true;
}
- (void)reset_touch
{
App::I->ui_task_async([=]
{
if (t_count == 2)
App::I->gesture_end();
else
App::I->mouse_cancel(0);
});
t_count = 0;
}
std::set<UITouch*> ignored_touch;
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
float scale = self.view.contentScaleFactor;
float force = touch.force;
AppDelegate* app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
if ([app sonarpen_present])
force = [app sonarpen_pressure];
kEventSource source = ((touch.type == UITouchType::UITouchTypeStylus) || [app sonarpen_present]) ?
kEventSource::Stylus : kEventSource::Touch;
// apple pencil
if (touch.type == UITouchType::UITouchTypeStylus)
pen_down = true;
App::I->ui_task_async([touchLocation, scale, f=force, source] {
App::I->mouse_down(0, touchLocation.x * scale, touchLocation.y * scale, f, source, 0);
});
t_count = 1;
t_pos = {touchLocation.x * scale, touchLocation.y * scale};
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSUInteger n = [[event allTouches] count];
float scale = self.view.contentScaleFactor;
std::vector<UITouch*> valid_touches;
for (int i = 0; i < n; i++)
{
auto t = [[[event allTouches] allObjects] objectAtIndex:i];
auto it = ignored_touch.find(t);
if (it == ignored_touch.end())
{
valid_touches.push_back(t);
}
else
{
NSLog(@"ignored moved");
}
}
n = valid_touches.size();
if (n == 0)
return;
UITouch* t0 = nullptr;
UITouch* t1 = nullptr;
t0 = valid_touches[0];
CGPoint tl0 = [t0 locationInView:self.view];
glm::vec2 p0 = glm::vec2(tl0.x * scale, tl0.y * scale);
glm::vec2 p1;
if (n > 1)
{
t1 = valid_touches[1];
CGPoint tl1 = [t1 locationInView:self.view];
p1 = glm::vec2(tl1.x * scale, tl1.y * scale);
}
if (pen_down)
{
App::I->ui_task_async([tt0=t0.type,tt1=t1.type,p0,p1,f0=t0.force,f1=t1.force] {
if (tt0 == UITouchType::UITouchTypeStylus)
App::I->mouse_move(p0.x, p0.y, f0, kEventSource::Stylus, 0);
else if (tt1 == UITouchType::UITouchTypeStylus)
App::I->mouse_move(p1.x, p1.y, f1, kEventSource::Stylus, 0);
});
}
else if (n == 2)
{
App::I->ui_task_async([c=t_count, p0, p1] {
if (c == 1)
{
App::I->mouse_cancel(0);
App::I->gesture_start(p0, p1);
}
else
App::I->gesture_move(p0, p1);
});
t_count = 2;
}
else if (n == 1)
{
UITouch *touch = valid_touches[0];
CGPoint touchLocation = [touch locationInView:self.view];
float force = touch.type == UITouchType::UITouchTypeStylus ? touch.force : 1.0f;
AppDelegate* app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
if ([app sonarpen_present])
force = [app sonarpen_pressure];
if (t_count == 2)
{
//App::I->gesture_end();
//App::I->mouse_down(0, touchLocation.x * scale, touchLocation.y * scale, force);
}
else
{
kEventSource source = ((touch.type == UITouchType::UITouchTypeStylus) || [app sonarpen_present]) ?
kEventSource::Stylus : kEventSource::Touch;
App::I->ui_task_async([touchLocation, scale, source, force] {
App::I->mouse_move(touchLocation.x * scale, touchLocation.y * scale, force, source, 0);
});
}
}
t_pos = {tl0.x * scale, tl0.y * scale};
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
float scale = self.view.contentScaleFactor;
if (ignored_touch.count(touch))
{
ignored_touch.erase(touch);
NSLog(@"remove ignored");
}
kEventSource source = touch.type == UITouchType::UITouchTypeStylus ? kEventSource::Stylus : kEventSource::Touch;
pen_down = false;
App::I->ui_task_async([tc=t_count, touchLocation, scale, source] {
if (tc == 2)
App::I->gesture_end();
else
App::I->mouse_up(0, touchLocation.x * scale, touchLocation.y * scale, source, 0);
});
t_count = 0;
t_pos = {touchLocation.x * scale, touchLocation.y * scale};
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
App::I->ui_task_async([f=self.view.contentScaleFactor, size]
{
App::I->resize(size.width * f, size.height * f);
});
}
- (void)viewDidAppear:(BOOL)animated
{
App::I->redraw = true;
[self resignFirstResponder];
[self registerForKeyboardNotifications];
}
- (void)viewDidLoad
{
[super viewDidLoad];
input_enabled = NO;
App::I = new App;
App::I->ios_view = self;
App::I->ios_app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
App::I->initLog();
//self.preferredFramesPerSecond = 60;
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if (!context) {
NSLog(@"Failed to create ES context");
}
GameView *view = (GameView *)self.view;
view.context = context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
view.enableSetNeedsDisplay = NO;
glview = view;
App::I->width = view.frame.size.width * view.contentScaleFactor;
App::I->height = view.frame.size.height * view.contentScaleFactor;
App::I->zoom = Settings::value_or<Serializer::Float>("ui-scale", (float)view.contentScaleFactor);
App::I->render_thread_start();
App::I->ui_thread_start();
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
@end
@implementation GameView
@end