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