mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-11-20 02:32:28 +00:00
570147712c
- iOS build is still super Alpha ;)
387 lines
11 KiB
Objective-C
387 lines
11 KiB
Objective-C
/*
|
|
* Apple // emulator for *ix
|
|
*
|
|
* This software package is subject to the GNU General Public License
|
|
* version 3 or later (your choice) as published by the Free Software
|
|
* Foundation.
|
|
*
|
|
* Copyright 2015 Aaron Culliney
|
|
*
|
|
*/
|
|
|
|
#import "EAGLView.h"
|
|
#import "common.h"
|
|
#import "modelUtil.h"
|
|
|
|
#import "A2IXPopupChoreographer.h"
|
|
|
|
#import <OpenGLES/ES3/gl.h>
|
|
#import <OpenGLES/ES3/glext.h>
|
|
|
|
@interface EAGLView ()
|
|
|
|
@property (nonatomic, retain) EAGLContext *context;
|
|
@property (nonatomic, retain) CADisplayLink *displayLink;
|
|
@property (nonatomic, assign) GLuint defaultFBOName;
|
|
@property (nonatomic, assign) GLuint colorRenderbuffer;
|
|
|
|
@end
|
|
|
|
@implementation EAGLView
|
|
|
|
@synthesize renderFrameInterval = _renderFrameInterval;
|
|
|
|
// Must return the CAEAGLLayer class so that CA allocates an EAGLLayer backing for this view
|
|
+ (Class)layerClass
|
|
{
|
|
return [CAEAGLLayer class];
|
|
}
|
|
|
|
// -initWithCoder: is sent when unarchived from storyboard file
|
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
|
{
|
|
self = [super initWithCoder:coder];
|
|
if (self)
|
|
{
|
|
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
|
|
|
|
eaglLayer.opaque = YES;
|
|
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
|
|
|
|
|
|
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
|
if (!_context || ![EAGLContext setCurrentContext:_context])
|
|
{
|
|
[self release];
|
|
return nil;
|
|
}
|
|
|
|
// Create default framebuffer object. The backing will be allocated for the
|
|
// current layer in -resizeFromLayer
|
|
glGenFramebuffers(1, &_defaultFBOName);
|
|
|
|
glGenRenderbuffers(1, &_colorRenderbuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBOName);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
|
|
|
|
// This call associates the storage for the current render buffer with the
|
|
// EAGLDrawable (our CAEAGLLayer) allowing us to draw into a buffer that
|
|
// will later be rendered to the screen wherever the layer is (which
|
|
// corresponds with our view).
|
|
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(id<EAGLDrawable>)self.layer];
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);
|
|
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
|
[self release];
|
|
return nil;
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBOName);
|
|
|
|
#if TESTING
|
|
char *local_argv[] = {
|
|
"-f",
|
|
NULL
|
|
};
|
|
int local_argc = 0;
|
|
for (char **p = &local_argv[0]; *p != NULL; p++) {
|
|
++local_argc;
|
|
}
|
|
|
|
# if TEST_CPU
|
|
// Currently this test is the only one that blocks current thread and runs as a black screen
|
|
extern int test_cpu(int, char *[]);
|
|
test_cpu(local_argc, local_argv);
|
|
# elif TEST_DISK
|
|
extern int test_disk(int, char *[]);
|
|
test_disk(local_argc, local_argv);
|
|
# elif TEST_DISPLAY
|
|
extern int test_display(int, char *[]);
|
|
test_display(local_argc, local_argv);
|
|
# elif TEST_PREFS
|
|
extern void test_prefs(int, char *[]);
|
|
test_prefs(local_argc, local_argv);
|
|
# elif TEST_TRACE
|
|
extern void test_trace(int, char *[]);
|
|
test_trace(local_argc, local_argv);
|
|
# elif TEST_UI
|
|
extern int test_ui(int, char *[]);
|
|
test_ui(local_argc, local_argv);
|
|
# elif TEST_VM
|
|
extern int test_vm(int, char *[]);
|
|
test_vm(local_argc, local_argv);
|
|
# else
|
|
# error "OOPS, no testsuite specified"
|
|
# endif
|
|
#endif
|
|
|
|
// start emulator from paused state
|
|
cpu_pause();
|
|
emulator_start();
|
|
|
|
// set up defaults
|
|
CGRect screenBounds = [[UIScreen mainScreen] bounds];
|
|
prefs_setLongValue(PREF_DOMAIN_INTERFACE, PREF_DEVICE_WIDTH, (long)screenBounds.size.width);
|
|
prefs_setLongValue(PREF_DOMAIN_INTERFACE, PREF_DEVICE_HEIGHT, (long)screenBounds.size.height);
|
|
|
|
video_init();
|
|
|
|
_animating = NO;
|
|
_renderFrameInterval = 1;
|
|
_displayLink = nil;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self pauseRendering];
|
|
[_displayLink release];
|
|
_displayLink = nil;
|
|
|
|
// shut down common OpenGL stuff AFTER display link has been released
|
|
emulator_shutdown();
|
|
|
|
if (_defaultFBOName != UNINITIALIZED_GL)
|
|
{
|
|
glDeleteFramebuffers(1, &_defaultFBOName);
|
|
_defaultFBOName = UNINITIALIZED_GL;
|
|
}
|
|
if (_colorRenderbuffer != UNINITIALIZED_GL)
|
|
{
|
|
glDeleteRenderbuffers(1, &_colorRenderbuffer);
|
|
_colorRenderbuffer = UNINITIALIZED_GL;
|
|
}
|
|
|
|
if ([EAGLContext currentContext] == _context)
|
|
{
|
|
[EAGLContext setCurrentContext:nil];
|
|
}
|
|
|
|
[_context release];
|
|
_context = nil;
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)pauseEmulation
|
|
{
|
|
cpu_pause();
|
|
}
|
|
|
|
- (void)resumeEmulation
|
|
{
|
|
cpu_resume();
|
|
}
|
|
|
|
- (void)drawView:(id)sender
|
|
{
|
|
[EAGLContext setCurrentContext:_context];
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBOName);
|
|
video_render();
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
|
|
|
|
[_context presentRenderbuffer:GL_RENDERBUFFER];
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
// Allocate color buffer backing based on the current layer size
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
|
|
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(id<EAGLDrawable>)self.layer];
|
|
|
|
// The pixel dimensions of the CAEAGLLayer
|
|
GLint backingWidth = 0;
|
|
GLint backingHeight = 0;
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
|
|
|
|
//video_reshape((int)backingWidth, (int)backingHeight, /*landscape:*/true); // TODO : portrait is possible
|
|
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
|
return;
|
|
}
|
|
|
|
[self drawView:nil];
|
|
}
|
|
|
|
- (void)setRenderFrameInterval:(NSInteger)frameInterval
|
|
{
|
|
// Frame interval defines how many display frames must pass between each time the
|
|
// display link fires. The display link will only fire 30 times a second when the
|
|
// frame internal is two on a display that refreshes 60 times a second. The default
|
|
// frame interval setting of one will fire 60 times a second when the display refreshes
|
|
// at 60 times a second. A frame interval setting of less than one results in undefined
|
|
// behavior.
|
|
if (frameInterval >= 1)
|
|
{
|
|
_renderFrameInterval = frameInterval;
|
|
if (_animating)
|
|
{
|
|
[self pauseRendering];
|
|
[self resumeRendering];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)resumeRendering
|
|
{
|
|
if (!_animating)
|
|
{
|
|
_animating = YES;
|
|
|
|
// Create the display link and set the callback to our drawView method
|
|
_displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(drawView:)] retain];
|
|
|
|
// Set it to our _renderFrameInterval
|
|
[_displayLink setFrameInterval:_renderFrameInterval];
|
|
|
|
// Have the display link run on the default runn loop (and the main thread)
|
|
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
|
}
|
|
}
|
|
|
|
- (void)pauseRendering
|
|
{
|
|
if (_animating)
|
|
{
|
|
[_displayLink invalidate];
|
|
[_displayLink release];
|
|
_displayLink = nil;
|
|
_animating = NO;
|
|
}
|
|
}
|
|
|
|
#pragma mark - Touch Handling
|
|
|
|
- (BOOL)isMultipleTouchEnabled
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
static inline void _handleTouch(EAGLView *self, SEL _cmd, UITouch *touch, interface_touch_event_t eventType) {
|
|
|
|
CGPoint location = [touch locationInView:self];
|
|
float x[1] = { location.x };
|
|
float y[1] = { location.y };
|
|
|
|
int64_t flags = interface_onTouchEvent(eventType, (int)1, (int)0, x, y);
|
|
|
|
do {
|
|
if ((flags & TOUCH_FLAGS_HANDLED) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((flags & TOUCH_FLAGS_REQUEST_HOST_MENU) != 0)
|
|
{
|
|
// requested host menu
|
|
[[A2IXPopupChoreographer sharedInstance] showMainMenuFromView:self];
|
|
}
|
|
|
|
if ((flags & TOUCH_FLAGS_KEY_TAP) != 0)
|
|
{
|
|
// tapped key
|
|
}
|
|
|
|
if ((flags & TOUCH_FLAGS_MENU) == 0)
|
|
{
|
|
// touch menu was tapped
|
|
break;
|
|
}
|
|
|
|
// touched menu item ...
|
|
if ((flags & TOUCH_FLAGS_INPUT_DEVICE_CHANGE) != 0)
|
|
{
|
|
video_animation_s *anim = video_getAnimationDriver();
|
|
if ((flags & TOUCH_FLAGS_KBD) != 0)
|
|
{
|
|
//keydriver_setTouchKeyboardOwnsScreen(true);
|
|
//joydriver_setTouchJoystickOwnsScreen(false);
|
|
if (anim && anim->animation_showTouchKeyboard)
|
|
{
|
|
anim->animation_showTouchKeyboard();
|
|
}
|
|
}
|
|
else if ((flags & TOUCH_FLAGS_JOY) != 0)
|
|
{
|
|
//keydriver_setTouchKeyboardOwnsScreen(false);
|
|
//joydriver_setTouchJoystickOwnsScreen(true);
|
|
//joydriver_setTouchVariant(EMULATED_JOYSTICK);
|
|
if (anim && anim->animation_showTouchKeyboard)
|
|
{
|
|
anim->animation_showTouchJoystick();
|
|
}
|
|
}
|
|
else if ((flags & TOUCH_FLAGS_JOY_KPAD) != 0)
|
|
{
|
|
//keydriver_setTouchKeyboardOwnsScreen(false);
|
|
//joydriver_setTouchJoystickOwnsScreen(true);
|
|
//joydriver_setTouchVariant(EMULATED_KEYPAD);
|
|
if (anim && anim->animation_showTouchKeyboard)
|
|
{
|
|
anim->animation_showTouchJoystick();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// switch to next variant ...
|
|
}
|
|
}
|
|
else if ((flags & TOUCH_FLAGS_CPU_SPEED_DEC) != 0)
|
|
{
|
|
// handle cpu decrement
|
|
}
|
|
else if ((flags & TOUCH_FLAGS_CPU_SPEED_INC) != 0)
|
|
{
|
|
// handle cpu increment
|
|
}
|
|
} while (NO);
|
|
}
|
|
|
|
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
|
{
|
|
LOG("...");
|
|
for (UITouch *touch in touches)
|
|
{
|
|
_handleTouch(self, _cmd, touch, TOUCH_DOWN);
|
|
}
|
|
}
|
|
|
|
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
|
{
|
|
LOG("...");
|
|
for (UITouch *touch in touches)
|
|
{
|
|
_handleTouch(self, _cmd, touch, TOUCH_MOVE);
|
|
}
|
|
}
|
|
|
|
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
|
{
|
|
LOG("...");
|
|
for (UITouch *touch in touches)
|
|
{
|
|
_handleTouch(self, _cmd, touch, TOUCH_UP);
|
|
}
|
|
}
|
|
|
|
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
|
{
|
|
LOG("...");
|
|
for (UITouch *touch in touches)
|
|
{
|
|
_handleTouch(self, _cmd, touch, TOUCH_CANCEL);
|
|
}
|
|
}
|
|
|
|
|
|
@end
|