1#import <IOKit/pwr_mgt/IOPMLib.h>
2#import <Carbon/Carbon.h>
3#import "GBView.h"
4#import "GBViewGL.h"
5#import "GBViewMetal.h"
6#import "GBButtons.h"
7#import "NSString+StringForKey.h"
8#import "Document.h"
9
10#define JOYSTICK_HIGH 0x4000
11#define JOYSTICK_LOW 0x3800
12
13static const uint8_t workboy_ascii_to_key[] = {
14    ['0'] = GB_WORKBOY_0,
15    ['`'] = GB_WORKBOY_UMLAUT,
16    ['1'] = GB_WORKBOY_1,
17    ['2'] = GB_WORKBOY_2,
18    ['3'] = GB_WORKBOY_3,
19    ['4'] = GB_WORKBOY_4,
20    ['5'] = GB_WORKBOY_5,
21    ['6'] = GB_WORKBOY_6,
22    ['7'] = GB_WORKBOY_7,
23    ['8'] = GB_WORKBOY_8,
24    ['9'] = GB_WORKBOY_9,
25
26    ['\r'] = GB_WORKBOY_ENTER,
27    [3] = GB_WORKBOY_ENTER,
28
29    ['!'] = GB_WORKBOY_EXCLAMATION_MARK,
30    ['$'] = GB_WORKBOY_DOLLAR,
31    ['#'] = GB_WORKBOY_HASH,
32    ['~'] = GB_WORKBOY_TILDE,
33    ['*'] = GB_WORKBOY_ASTERISK,
34    ['+'] = GB_WORKBOY_PLUS,
35    ['-'] = GB_WORKBOY_MINUS,
36    ['('] = GB_WORKBOY_LEFT_PARENTHESIS,
37    [')'] = GB_WORKBOY_RIGHT_PARENTHESIS,
38    [';'] = GB_WORKBOY_SEMICOLON,
39    [':'] = GB_WORKBOY_COLON,
40    ['%'] = GB_WORKBOY_PERCENT,
41    ['='] = GB_WORKBOY_EQUAL,
42    [','] = GB_WORKBOY_COMMA,
43    ['<'] = GB_WORKBOY_LT,
44    ['.'] = GB_WORKBOY_DOT,
45    ['>'] = GB_WORKBOY_GT,
46    ['/'] = GB_WORKBOY_SLASH,
47    ['?'] = GB_WORKBOY_QUESTION_MARK,
48    [' '] = GB_WORKBOY_SPACE,
49    ['\''] = GB_WORKBOY_QUOTE,
50    ['@'] = GB_WORKBOY_AT,
51
52    ['q'] = GB_WORKBOY_Q,
53    ['w'] = GB_WORKBOY_W,
54    ['e'] = GB_WORKBOY_E,
55    ['r'] = GB_WORKBOY_R,
56    ['t'] = GB_WORKBOY_T,
57    ['y'] = GB_WORKBOY_Y,
58    ['u'] = GB_WORKBOY_U,
59    ['i'] = GB_WORKBOY_I,
60    ['o'] = GB_WORKBOY_O,
61    ['p'] = GB_WORKBOY_P,
62    ['a'] = GB_WORKBOY_A,
63    ['s'] = GB_WORKBOY_S,
64    ['d'] = GB_WORKBOY_D,
65    ['f'] = GB_WORKBOY_F,
66    ['g'] = GB_WORKBOY_G,
67    ['h'] = GB_WORKBOY_H,
68    ['j'] = GB_WORKBOY_J,
69    ['k'] = GB_WORKBOY_K,
70    ['l'] = GB_WORKBOY_L,
71    ['z'] = GB_WORKBOY_Z,
72    ['x'] = GB_WORKBOY_X,
73    ['c'] = GB_WORKBOY_C,
74    ['v'] = GB_WORKBOY_V,
75    ['b'] = GB_WORKBOY_B,
76    ['n'] = GB_WORKBOY_N,
77    ['m'] = GB_WORKBOY_M,
78};
79
80static const uint8_t workboy_vk_to_key[] = {
81    [kVK_F1] = GB_WORKBOY_CLOCK,
82    [kVK_F2] = GB_WORKBOY_TEMPERATURE,
83    [kVK_F3] = GB_WORKBOY_MONEY,
84    [kVK_F4] = GB_WORKBOY_CALCULATOR,
85    [kVK_F5] = GB_WORKBOY_DATE,
86    [kVK_F6] = GB_WORKBOY_CONVERSION,
87    [kVK_F7] = GB_WORKBOY_RECORD,
88    [kVK_F8] = GB_WORKBOY_WORLD,
89    [kVK_F9] = GB_WORKBOY_PHONE,
90    [kVK_F10] = GB_WORKBOY_UNKNOWN,
91    [kVK_Delete] = GB_WORKBOY_BACKSPACE,
92    [kVK_Shift] = GB_WORKBOY_SHIFT_DOWN,
93    [kVK_RightShift] = GB_WORKBOY_SHIFT_DOWN,
94    [kVK_UpArrow] = GB_WORKBOY_UP,
95    [kVK_DownArrow] = GB_WORKBOY_DOWN,
96    [kVK_LeftArrow] = GB_WORKBOY_LEFT,
97    [kVK_RightArrow] = GB_WORKBOY_RIGHT,
98    [kVK_Escape] = GB_WORKBOY_ESCAPE,
99    [kVK_ANSI_KeypadDecimal] = GB_WORKBOY_DECIMAL_POINT,
100    [kVK_ANSI_KeypadClear] = GB_WORKBOY_M,
101    [kVK_ANSI_KeypadMultiply] = GB_WORKBOY_H,
102    [kVK_ANSI_KeypadDivide] = GB_WORKBOY_J,
103};
104
105@implementation GBView
106{
107    uint32_t *image_buffers[3];
108    unsigned char current_buffer;
109    bool mouse_hidden;
110    NSTrackingArea *tracking_area;
111    bool _mouseHidingEnabled;
112    bool axisActive[2];
113    bool underclockKeyDown;
114    double clockMultiplier;
115    double analogClockMultiplier;
116    bool analogClockMultiplierValid;
117    NSEventModifierFlags previousModifiers;
118    JOYController *lastController;
119    GB_frame_blending_mode_t _frameBlendingMode;
120    bool _turbo;
121}
122
123+ (instancetype)alloc
124{
125    return [self allocWithZone:NULL];
126}
127
128+ (instancetype)allocWithZone:(struct _NSZone *)zone
129{
130    if (self == [GBView class]) {
131        if ([GBViewMetal isSupported]) {
132            return [GBViewMetal allocWithZone: zone];
133        }
134        return [GBViewGL allocWithZone: zone];
135    }
136    return [super allocWithZone:zone];
137}
138
139- (void) createInternalView
140{
141    assert(false && "createInternalView must not be inherited");
142}
143
144- (void) _init
145{
146    [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
147
148    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
149    tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
150                                                  options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect
151                                                    owner:self
152                                                 userInfo:nil];
153    [self addTrackingArea:tracking_area];
154    clockMultiplier = 1.0;
155    [self createInternalView];
156    [self addSubview:self.internalView];
157    self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
158    [JOYController registerListener:self];
159}
160
161- (void)screenSizeChanged
162{
163    if (image_buffers[0]) free(image_buffers[0]);
164    if (image_buffers[1]) free(image_buffers[1]);
165    if (image_buffers[2]) free(image_buffers[2]);
166
167    size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(_gb) * GB_get_screen_height(_gb);
168
169    image_buffers[0] = calloc(1, buffer_size);
170    image_buffers[1] = calloc(1, buffer_size);
171    image_buffers[2] = calloc(1, buffer_size);
172
173    dispatch_async(dispatch_get_main_queue(), ^{
174        [self setFrame:self.superview.frame];
175    });
176}
177
178- (void) ratioKeepingChanged
179{
180    [self setFrame:self.superview.frame];
181}
182
183- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
184{
185    _frameBlendingMode = frameBlendingMode;
186    [self setNeedsDisplay:true];
187}
188
189
190- (GB_frame_blending_mode_t)frameBlendingMode
191{
192    if (_frameBlendingMode == GB_FRAME_BLENDING_MODE_ACCURATE) {
193        if (!_gb || GB_is_sgb(_gb)) {
194            return GB_FRAME_BLENDING_MODE_SIMPLE;
195        }
196        return GB_is_odd_frame(_gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN;
197    }
198    return _frameBlendingMode;
199}
200- (unsigned char) numberOfBuffers
201{
202    return _frameBlendingMode? 3 : 2;
203}
204
205- (void)dealloc
206{
207    free(image_buffers[0]);
208    free(image_buffers[1]);
209    free(image_buffers[2]);
210    if (mouse_hidden) {
211        mouse_hidden = false;
212        [NSCursor unhide];
213    }
214    [[NSNotificationCenter defaultCenter] removeObserver:self];
215    [self setRumble:0];
216    [JOYController unregisterListener:self];
217}
218- (instancetype)initWithCoder:(NSCoder *)coder
219{
220    if (!(self = [super initWithCoder:coder])) {
221        return self;
222    }
223    [self _init];
224    return self;
225}
226
227- (instancetype)initWithFrame:(NSRect)frameRect
228{
229    if (!(self = [super initWithFrame:frameRect])) {
230        return self;
231    }
232    [self _init];
233    return self;
234}
235
236- (void)setFrame:(NSRect)frame
237{
238    frame = self.superview.frame;
239    if (_gb && ![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]) {
240        double ratio = frame.size.width / frame.size.height;
241        double width = GB_get_screen_width(_gb);
242        double height = GB_get_screen_height(_gb);
243        if (ratio >= width / height) {
244            double new_width = round(frame.size.height / height * width);
245            frame.origin.x = floor((frame.size.width - new_width) / 2);
246            frame.size.width = new_width;
247            frame.origin.y = 0;
248        }
249        else {
250            double new_height = round(frame.size.width / width * height);
251            frame.origin.y = floor((frame.size.height - new_height) / 2);
252            frame.size.height = new_height;
253            frame.origin.x = 0;
254        }
255    }
256
257    [super setFrame:frame];
258}
259
260- (void) flip
261{
262    if (analogClockMultiplierValid && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]) {
263        clockMultiplier = 1.0;
264        GB_set_clock_multiplier(_gb, analogClockMultiplier);
265        if (self.document.partner) {
266            GB_set_clock_multiplier(self.document.partner.gb, analogClockMultiplier);
267        }
268        if (analogClockMultiplier == 1.0) {
269            analogClockMultiplierValid = false;
270        }
271        if (analogClockMultiplier < 2.0 && analogClockMultiplier > 1.0) {
272            GB_set_turbo_mode(_gb, false, false);
273            if (self.document.partner) {
274                GB_set_turbo_mode(self.document.partner.gb, false, false);
275            }
276        }
277    }
278    else {
279        if (underclockKeyDown && clockMultiplier > 0.5) {
280            clockMultiplier -= 1.0/16;
281            GB_set_clock_multiplier(_gb, clockMultiplier);
282            if (self.document.partner) {
283                GB_set_clock_multiplier(self.document.partner.gb, clockMultiplier);
284            }
285        }
286        if (!underclockKeyDown && clockMultiplier < 1.0) {
287            clockMultiplier += 1.0/16;
288            GB_set_clock_multiplier(_gb, clockMultiplier);
289            if (self.document.partner) {
290                GB_set_clock_multiplier(self.document.partner.gb, clockMultiplier);
291            }
292        }
293    }
294    if ((!analogClockMultiplierValid && clockMultiplier > 1) ||
295        _turbo || (analogClockMultiplierValid && analogClockMultiplier > 1)) {
296        [self.osdView displayText:@"Fast forwarding..."];
297    }
298    else if ((!analogClockMultiplierValid && clockMultiplier < 1) ||
299             (analogClockMultiplierValid && analogClockMultiplier < 1)) {
300        [self.osdView displayText:@"Slow motion..."];
301    }
302    current_buffer = (current_buffer + 1) % self.numberOfBuffers;
303}
304
305- (uint32_t *) pixels
306{
307    return image_buffers[(current_buffer + 1) % self.numberOfBuffers];
308}
309
310-(void)keyDown:(NSEvent *)theEvent
311{
312    if ([theEvent type] != NSEventTypeFlagsChanged && theEvent.isARepeat) return;
313    unsigned short keyCode = theEvent.keyCode;
314    if (GB_workboy_is_enabled(_gb)) {
315        if (theEvent.keyCode < sizeof(workboy_vk_to_key) && workboy_vk_to_key[theEvent.keyCode]) {
316            GB_workboy_set_key(_gb, workboy_vk_to_key[theEvent.keyCode]);
317            return;
318        }
319        unichar c = [theEvent type] != NSEventTypeFlagsChanged? [theEvent.charactersIgnoringModifiers.lowercaseString characterAtIndex:0] : 0;
320        if (c < sizeof(workboy_ascii_to_key) && workboy_ascii_to_key[c]) {
321            GB_workboy_set_key(_gb, workboy_ascii_to_key[c]);
322            return;
323        }
324    }
325
326    bool handled = false;
327
328    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
329    unsigned player_count = GB_get_player_count(_gb);
330    if (self.document.partner) {
331        player_count = 2;
332    }
333    for (unsigned player = 0; player < player_count; player++) {
334        for (GBButton button = 0; button < GBButtonCount; button++) {
335            NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
336            if (!key) continue;
337
338            if (key.unsignedShortValue == keyCode) {
339                handled = true;
340                switch (button) {
341                    case GBTurbo:
342                        if (self.document.isSlave) {
343                            GB_set_turbo_mode(self.document.partner.gb, true, false);
344                        }
345                        else {
346                            GB_set_turbo_mode(_gb, true, self.isRewinding);
347                        }
348                        _turbo = true;
349                        analogClockMultiplierValid = false;
350                        break;
351
352                    case GBRewind:
353                        if (!self.document.partner) {
354                            self.isRewinding = true;
355                            GB_set_turbo_mode(_gb, false, false);
356                            _turbo = false;
357                        }
358                        break;
359
360                    case GBUnderclock:
361                        underclockKeyDown = true;
362                        analogClockMultiplierValid = false;
363                        break;
364
365                    default:
366                        if (self.document.partner) {
367                            if (player == 0) {
368                                GB_set_key_state_for_player(_gb, (GB_key_t)button, 0, true);
369                            }
370                            else {
371                                GB_set_key_state_for_player(self.document.partner.gb, (GB_key_t)button, 0, true);
372                            }
373                        }
374                        else {
375                            GB_set_key_state_for_player(_gb, (GB_key_t)button, player, true);
376                        }
377                        break;
378                }
379            }
380        }
381    }
382
383    if (!handled && [theEvent type] != NSEventTypeFlagsChanged) {
384        [super keyDown:theEvent];
385    }
386}
387
388-(void)keyUp:(NSEvent *)theEvent
389{
390    unsigned short keyCode = theEvent.keyCode;
391    if (GB_workboy_is_enabled(_gb)) {
392        if (keyCode == kVK_Shift || keyCode == kVK_RightShift) {
393            GB_workboy_set_key(_gb, GB_WORKBOY_SHIFT_UP);
394        }
395        else {
396            GB_workboy_set_key(_gb, GB_WORKBOY_NONE);
397        }
398
399    }
400    bool handled = false;
401
402    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
403    unsigned player_count = GB_get_player_count(_gb);
404    if (self.document.partner) {
405        player_count = 2;
406    }
407    for (unsigned player = 0; player < player_count; player++) {
408        for (GBButton button = 0; button < GBButtonCount; button++) {
409            NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
410            if (!key) continue;
411
412            if (key.unsignedShortValue == keyCode) {
413                handled = true;
414                switch (button) {
415                    case GBTurbo:
416                        if (self.document.isSlave) {
417                            GB_set_turbo_mode(self.document.partner.gb, false, false);
418                        }
419                        else {
420                            GB_set_turbo_mode(_gb, false, false);
421                        }
422                        _turbo = false;
423                        analogClockMultiplierValid = false;
424                        break;
425
426                    case GBRewind:
427                        self.isRewinding = false;
428                        break;
429
430                    case GBUnderclock:
431                        underclockKeyDown = false;
432                        analogClockMultiplierValid = false;
433                        break;
434
435                    default:
436                        if (self.document.partner) {
437                            if (player == 0) {
438                                GB_set_key_state_for_player(_gb, (GB_key_t)button, 0, false);
439                            }
440                            else {
441                                GB_set_key_state_for_player(self.document.partner.gb, (GB_key_t)button, 0, false);
442                            }
443                        }
444                        else {
445                            GB_set_key_state_for_player(_gb, (GB_key_t)button, player, false);
446                        }
447                        break;
448                }
449            }
450        }
451    }
452    if (!handled && [theEvent type] != NSEventTypeFlagsChanged) {
453        [super keyUp:theEvent];
454    }
455}
456
457- (void)setRumble:(double)amp
458{
459    [lastController setRumbleAmplitude:amp];
460}
461
462- (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis
463{
464    if (![self.window isMainWindow]) return;
465
466    NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
467    if (!mapping) {
468        mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
469    }
470
471    if ((axis.usage == JOYAxisUsageR1 && !mapping) ||
472        axis.uniqueID == [mapping[@"AnalogUnderclock"] unsignedLongValue]){
473        analogClockMultiplier = MIN(MAX(1 - axis.value + 0.05, 1.0 / 3), 1.0);
474        analogClockMultiplierValid = true;
475    }
476
477    else if ((axis.usage == JOYAxisUsageL1 && !mapping) ||
478        axis.uniqueID == [mapping[@"AnalogTurbo"] unsignedLongValue]){
479        analogClockMultiplier = MIN(MAX(axis.value * 3 + 0.95, 1.0), 3.0);
480        analogClockMultiplierValid = true;
481    }
482}
483
484- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
485{
486    if (![self.window isMainWindow]) return;
487
488    unsigned player_count = GB_get_player_count(_gb);
489    if (self.document.partner) {
490        player_count = 2;
491    }
492
493    IOPMAssertionID assertionID;
494    IOPMAssertionDeclareUserActivity(CFSTR(""), kIOPMUserActiveLocal, &assertionID);
495
496    for (unsigned player = 0; player < player_count; player++) {
497        NSString *preferred_joypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"]
498                                      objectForKey:n2s(player)];
499        if (player_count != 1 && // Single player, accpet inputs from all joypads
500            !(player == 0 && !preferred_joypad) && // Multiplayer, but player 1 has no joypad configured, so it takes inputs from all joypads
501            ![preferred_joypad isEqualToString:controller.uniqueID]) {
502            continue;
503        }
504        dispatch_async(dispatch_get_main_queue(), ^{
505            [controller setPlayerLEDs:[controller LEDMaskForPlayer:player]];
506        });
507        NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
508        if (!mapping) {
509            mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
510        }
511
512        JOYButtonUsage usage = ((JOYButtonUsage)[mapping[n2s(button.uniqueID)] unsignedIntValue]) ?: button.usage;
513        if (!mapping && usage >= JOYButtonUsageGeneric0) {
514            usage = (const JOYButtonUsage[]){JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX}[(usage - JOYButtonUsageGeneric0) & 3];
515        }
516
517        GB_gameboy_t *effectiveGB = _gb;
518        unsigned effectivePlayer = player;
519
520        if (player && self.document.partner) {
521            effectiveGB = self.document.partner.gb;
522            effectivePlayer = 0;
523            if (controller != self.document.partner.view->lastController) {
524                [self setRumble:0];
525                self.document.partner.view->lastController = controller;
526            }
527        }
528        else {
529            if (controller != lastController) {
530                [self setRumble:0];
531                lastController = controller;
532            }
533        }
534
535        switch (usage) {
536
537            case JOYButtonUsageNone: break;
538            case JOYButtonUsageA: GB_set_key_state_for_player(effectiveGB, GB_KEY_A, effectivePlayer, button.isPressed); break;
539            case JOYButtonUsageB: GB_set_key_state_for_player(effectiveGB, GB_KEY_B, effectivePlayer, button.isPressed); break;
540            case JOYButtonUsageC: break;
541            case JOYButtonUsageStart:
542            case JOYButtonUsageX: GB_set_key_state_for_player(effectiveGB, GB_KEY_START, effectivePlayer, button.isPressed); break;
543            case JOYButtonUsageSelect:
544            case JOYButtonUsageY: GB_set_key_state_for_player(effectiveGB, GB_KEY_SELECT, effectivePlayer, button.isPressed); break;
545            case JOYButtonUsageR2:
546            case JOYButtonUsageL2:
547            case JOYButtonUsageZ: {
548                self.isRewinding = button.isPressed;
549                if (button.isPressed) {
550                    if (self.document.isSlave) {
551                        GB_set_turbo_mode(self.document.partner.gb, false, false);
552                    }
553                    else {
554                        GB_set_turbo_mode(_gb, false, false);
555                    }
556                    _turbo = false;
557                }
558                break;
559            }
560
561            case JOYButtonUsageL1: {
562                if (!analogClockMultiplierValid || analogClockMultiplier == 1.0 || !button.isPressed) {
563                    if (self.document.isSlave) {
564                        GB_set_turbo_mode(self.document.partner.gb, button.isPressed, false);
565                    }
566                    else {
567                        GB_set_turbo_mode(_gb, button.isPressed, button.isPressed && self.isRewinding);
568                    }
569                    _turbo = button.isPressed;
570                }
571                break;
572            }
573
574            case JOYButtonUsageR1: underclockKeyDown = button.isPressed; break;
575            case JOYButtonUsageDPadLeft: GB_set_key_state_for_player(effectiveGB, GB_KEY_LEFT, effectivePlayer, button.isPressed); break;
576            case JOYButtonUsageDPadRight: GB_set_key_state_for_player(effectiveGB, GB_KEY_RIGHT, effectivePlayer, button.isPressed); break;
577            case JOYButtonUsageDPadUp: GB_set_key_state_for_player(effectiveGB, GB_KEY_UP, effectivePlayer, button.isPressed); break;
578            case JOYButtonUsageDPadDown: GB_set_key_state_for_player(effectiveGB, GB_KEY_DOWN, effectivePlayer, button.isPressed); break;
579
580            default:
581                break;
582        }
583    }
584}
585
586- (BOOL)acceptsFirstResponder
587{
588    return true;
589}
590
591- (void)mouseEntered:(NSEvent *)theEvent
592{
593    if (!mouse_hidden) {
594        mouse_hidden = true;
595        if (_mouseHidingEnabled) {
596            [NSCursor hide];
597        }
598    }
599    [super mouseEntered:theEvent];
600}
601
602- (void)mouseExited:(NSEvent *)theEvent
603{
604    if (mouse_hidden) {
605        mouse_hidden = false;
606        if (_mouseHidingEnabled) {
607            [NSCursor unhide];
608        }
609    }
610    [super mouseExited:theEvent];
611}
612
613- (void)setMouseHidingEnabled:(bool)mouseHidingEnabled
614{
615    if (mouseHidingEnabled == _mouseHidingEnabled) return;
616
617    _mouseHidingEnabled = mouseHidingEnabled;
618
619    if (mouse_hidden && _mouseHidingEnabled) {
620        [NSCursor hide];
621    }
622
623    if (mouse_hidden && !_mouseHidingEnabled) {
624        [NSCursor unhide];
625    }
626}
627
628- (bool)isMouseHidingEnabled
629{
630    return _mouseHidingEnabled;
631}
632
633- (void) flagsChanged:(NSEvent *)event
634{
635    if (event.modifierFlags > previousModifiers) {
636        [self keyDown:event];
637    }
638    else {
639        [self keyUp:event];
640    }
641
642    previousModifiers = event.modifierFlags;
643}
644
645- (uint32_t *)currentBuffer
646{
647    return image_buffers[current_buffer];
648}
649
650- (uint32_t *)previousBuffer
651{
652    return image_buffers[(current_buffer + 2) % self.numberOfBuffers];
653}
654
655-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
656{
657    NSPasteboard *pboard = [sender draggingPasteboard];
658
659    if ( [[pboard types] containsObject:NSURLPboardType] ) {
660        NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
661        if (GB_is_save_state(fileURL.fileSystemRepresentation)) {
662            return NSDragOperationGeneric;
663        }
664    }
665    return NSDragOperationNone;
666}
667
668-(BOOL)performDragOperation:(id<NSDraggingInfo>)sender
669{
670    NSPasteboard *pboard = [sender draggingPasteboard];
671
672    if ( [[pboard types] containsObject:NSURLPboardType] ) {
673        NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
674        return [_document loadStateFile:fileURL.fileSystemRepresentation noErrorOnNotFound:false];
675    }
676
677    return false;
678}
679
680- (NSImage *)renderToImage;
681{
682    /* Not going to support this on OpenGL, OpenGL is too much of a terrible API for me
683       to bother figuring out how the hell something so trivial can be done. */
684    return nil;
685}
686@end
687