2016-03-30 20:07:55 +00:00
|
|
|
#import <OpenGL/gl.h>
|
2016-06-08 21:37:00 +00:00
|
|
|
#import <Carbon/Carbon.h>
|
2016-03-30 20:07:55 +00:00
|
|
|
#import "GBView.h"
|
2016-04-13 19:43:16 +00:00
|
|
|
#import "GBButtons.h"
|
|
|
|
#import "NSString+StringForKey.h"
|
2016-03-30 20:07:55 +00:00
|
|
|
|
|
|
|
@implementation GBView
|
|
|
|
{
|
|
|
|
uint32_t *image_buffers[3];
|
|
|
|
unsigned char current_buffer;
|
|
|
|
}
|
|
|
|
|
2016-06-14 11:11:37 +00:00
|
|
|
- (void) awakeFromNib
|
|
|
|
{
|
|
|
|
NSOpenGLPixelFormatAttribute attrs[] =
|
|
|
|
{
|
|
|
|
NSOpenGLPFAOpenGLProfile,
|
|
|
|
NSOpenGLProfileVersion3_2Core,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] ;
|
|
|
|
|
|
|
|
if (!pf)
|
|
|
|
{
|
|
|
|
NSLog(@"No OpenGL pixel format");
|
|
|
|
}
|
|
|
|
|
|
|
|
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil] ;
|
|
|
|
|
|
|
|
[self setPixelFormat:pf];
|
|
|
|
|
|
|
|
[self setOpenGLContext:context];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
- (void) _init
|
|
|
|
{
|
|
|
|
image_buffers[0] = malloc(160 * 144 * 4);
|
|
|
|
image_buffers[1] = malloc(160 * 144 * 4);
|
|
|
|
image_buffers[2] = malloc(160 * 144 * 4);
|
|
|
|
_shouldBlendFrameWithPrevious = 1;
|
2016-04-28 20:07:05 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(filterChanged) name:@"GBFilterChanged" object:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) filterChanged
|
|
|
|
{
|
2016-06-08 21:06:55 +00:00
|
|
|
[self setNeedsDisplay:YES];
|
2016-04-28 20:07:05 +00:00
|
|
|
self.shader = nil;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-08 21:06:55 +00:00
|
|
|
- (void) setShouldBlendFrameWithPrevious:(BOOL)shouldBlendFrameWithPrevious
|
|
|
|
{
|
|
|
|
_shouldBlendFrameWithPrevious = shouldBlendFrameWithPrevious;
|
|
|
|
[self setNeedsDisplay:YES];
|
|
|
|
}
|
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
- (unsigned char) numberOfBuffers
|
|
|
|
{
|
|
|
|
return _shouldBlendFrameWithPrevious? 3 : 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
free(image_buffers[0]);
|
|
|
|
free(image_buffers[1]);
|
|
|
|
free(image_buffers[2]);
|
2016-04-28 20:07:05 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
|
|
|
{
|
|
|
|
if (!(self = [super initWithCoder:coder]))
|
|
|
|
{
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
[self _init];
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (instancetype)initWithFrame:(NSRect)frameRect
|
|
|
|
{
|
|
|
|
if (!(self = [super initWithFrame:frameRect]))
|
|
|
|
{
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
[self _init];
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)drawRect:(NSRect)dirtyRect {
|
2016-04-28 20:07:05 +00:00
|
|
|
if (!self.shader) {
|
|
|
|
self.shader = [[GBShader alloc] initWithName:[[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]];
|
|
|
|
}
|
2016-06-18 14:48:02 +00:00
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
double scale = self.window.backingScaleFactor;
|
2016-06-18 14:48:02 +00:00
|
|
|
glViewport(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
if (_shouldBlendFrameWithPrevious) {
|
2016-04-28 20:07:05 +00:00
|
|
|
[self.shader renderBitmap:image_buffers[current_buffer]
|
|
|
|
previous:image_buffers[(current_buffer + 2) % self.numberOfBuffers]
|
|
|
|
inSize:self.bounds.size
|
|
|
|
scale:scale];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
[self.shader renderBitmap:image_buffers[current_buffer]
|
|
|
|
previous:NULL
|
|
|
|
inSize:self.bounds.size
|
|
|
|
scale:scale];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
glFlush();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void) flip
|
|
|
|
{
|
|
|
|
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
|
|
|
|
[self setNeedsDisplay:YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (uint32_t *) pixels
|
|
|
|
{
|
|
|
|
return image_buffers[(current_buffer + 1) % self.numberOfBuffers];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)keyDown:(NSEvent *)theEvent
|
|
|
|
{
|
2016-04-13 19:43:16 +00:00
|
|
|
bool handled = false;
|
|
|
|
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
for (GBButton i = 0; i < GBButtonCount; i++) {
|
|
|
|
if ([[defaults stringForKey:button_to_preference_name(i)] isEqualToString:theEvent.charactersIgnoringModifiers]) {
|
|
|
|
handled = true;
|
|
|
|
switch (i) {
|
|
|
|
case GBTurbo:
|
|
|
|
_gb->turbo = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
_gb->keys[i] = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handled) {
|
|
|
|
[super keyDown:theEvent];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)keyUp:(NSEvent *)theEvent
|
|
|
|
{
|
2016-04-13 19:43:16 +00:00
|
|
|
bool handled = false;
|
|
|
|
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
for (GBButton i = 0; i < GBButtonCount; i++) {
|
|
|
|
if ([[defaults stringForKey:button_to_preference_name(i)] isEqualToString:theEvent.charactersIgnoringModifiers]) {
|
|
|
|
handled = true;
|
|
|
|
switch (i) {
|
|
|
|
case GBTurbo:
|
|
|
|
_gb->turbo = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
_gb->keys[i] = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handled) {
|
|
|
|
[super keyUp:theEvent];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-08 21:37:00 +00:00
|
|
|
- (BOOL)becomeFirstResponder
|
|
|
|
{
|
|
|
|
/* Non-Roman keyboard layouts breaks user input. */
|
|
|
|
TSMDocumentID document = TSMGetActiveDocument();
|
|
|
|
|
|
|
|
CFArrayRef inpu_sources = TISCreateASCIICapableInputSourceList();
|
|
|
|
TSMSetDocumentProperty(document, kTSMDocumentEnabledInputSourcesPropertyTag,
|
|
|
|
sizeof(CFArrayRef), &inpu_sources);
|
|
|
|
CFRelease(inpu_sources);
|
|
|
|
|
|
|
|
return [super becomeFirstResponder];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)resignFirstResponder
|
|
|
|
{
|
|
|
|
TSMDocumentID document = TSMGetActiveDocument();
|
|
|
|
TSMRemoveDocumentProperty(document, kTSMDocumentEnabledInputSourcesPropertyTag);
|
|
|
|
|
|
|
|
return [super resignFirstResponder];
|
|
|
|
}
|
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
- (BOOL)acceptsFirstResponder
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
@end
|