SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
Cocoa/GBView.m
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 "NSObject+DefaultsObserver.h"
9 #import "Document.h"
10
11 #define JOYSTICK_HIGH 0x4000
12 #define JOYSTICK_LOW 0x3800
13
14 static const uint8_t workboy_ascii_to_key[] = {
15 ['0'] = GB_WORKBOY_0,
16 ['`'] = GB_WORKBOY_UMLAUT,
17 ['1'] = GB_WORKBOY_1,
18 ['2'] = GB_WORKBOY_2,
19 ['3'] = GB_WORKBOY_3,
20 ['4'] = GB_WORKBOY_4,
21 ['5'] = GB_WORKBOY_5,
22 ['6'] = GB_WORKBOY_6,
23 ['7'] = GB_WORKBOY_7,
24 ['8'] = GB_WORKBOY_8,
25 ['9'] = GB_WORKBOY_9,
26
27 ['\r'] = GB_WORKBOY_ENTER,
28 [3] = GB_WORKBOY_ENTER,
29
30 ['!'] = GB_WORKBOY_EXCLAMATION_MARK,
31 ['$'] = GB_WORKBOY_DOLLAR,
32 ['#'] = GB_WORKBOY_HASH,
33 ['~'] = GB_WORKBOY_TILDE,
34 ['*'] = GB_WORKBOY_ASTERISK,
35 ['+'] = GB_WORKBOY_PLUS,
36 ['-'] = GB_WORKBOY_MINUS,
37 ['('] = GB_WORKBOY_LEFT_PARENTHESIS,
38 [')'] = GB_WORKBOY_RIGHT_PARENTHESIS,
39 [';'] = GB_WORKBOY_SEMICOLON,
40 [':'] = GB_WORKBOY_COLON,
41 ['%'] = GB_WORKBOY_PERCENT,
42 ['='] = GB_WORKBOY_EQUAL,
43 [','] = GB_WORKBOY_COMMA,
44 ['<'] = GB_WORKBOY_LT,
45 ['.'] = GB_WORKBOY_DOT,
46 ['>'] = GB_WORKBOY_GT,
47 ['/'] = GB_WORKBOY_SLASH,
48 ['?'] = GB_WORKBOY_QUESTION_MARK,
49 [' '] = GB_WORKBOY_SPACE,
50 ['\''] = GB_WORKBOY_QUOTE,
51 ['@'] = GB_WORKBOY_AT,
52
53 ['q'] = GB_WORKBOY_Q,
54 ['w'] = GB_WORKBOY_W,
55 ['e'] = GB_WORKBOY_E,
56 ['r'] = GB_WORKBOY_R,
57 ['t'] = GB_WORKBOY_T,
58 ['y'] = GB_WORKBOY_Y,
59 ['u'] = GB_WORKBOY_U,
60 ['i'] = GB_WORKBOY_I,
61 ['o'] = GB_WORKBOY_O,
62 ['p'] = GB_WORKBOY_P,
63 ['a'] = GB_WORKBOY_A,
64 ['s'] = GB_WORKBOY_S,
65 ['d'] = GB_WORKBOY_D,
66 ['f'] = GB_WORKBOY_F,
67 ['g'] = GB_WORKBOY_G,
68 ['h'] = GB_WORKBOY_H,
69 ['j'] = GB_WORKBOY_J,
70 ['k'] = GB_WORKBOY_K,
71 ['l'] = GB_WORKBOY_L,
72 ['z'] = GB_WORKBOY_Z,
73 ['x'] = GB_WORKBOY_X,
74 ['c'] = GB_WORKBOY_C,
75 ['v'] = GB_WORKBOY_V,
76 ['b'] = GB_WORKBOY_B,
77 ['n'] = GB_WORKBOY_N,
78 ['m'] = GB_WORKBOY_M,
79 };
80
81 static const uint8_t workboy_vk_to_key[] = {
82 [kVK_F1] = GB_WORKBOY_CLOCK,
83 [kVK_F2] = GB_WORKBOY_TEMPERATURE,
84 [kVK_F3] = GB_WORKBOY_MONEY,
85 [kVK_F4] = GB_WORKBOY_CALCULATOR,
86 [kVK_F5] = GB_WORKBOY_DATE,
87 [kVK_F6] = GB_WORKBOY_CONVERSION,
88 [kVK_F7] = GB_WORKBOY_RECORD,
89 [kVK_F8] = GB_WORKBOY_WORLD,
90 [kVK_F9] = GB_WORKBOY_PHONE,
91 [kVK_F10] = GB_WORKBOY_UNKNOWN,
92 [kVK_Delete] = GB_WORKBOY_BACKSPACE,
93 [kVK_Shift] = GB_WORKBOY_SHIFT_DOWN,
94 [kVK_RightShift] = GB_WORKBOY_SHIFT_DOWN,
95 [kVK_UpArrow] = GB_WORKBOY_UP,
96 [kVK_DownArrow] = GB_WORKBOY_DOWN,
97 [kVK_LeftArrow] = GB_WORKBOY_LEFT,
98 [kVK_RightArrow] = GB_WORKBOY_RIGHT,
99 [kVK_Escape] = GB_WORKBOY_ESCAPE,
100 [kVK_ANSI_KeypadDecimal] = GB_WORKBOY_DECIMAL_POINT,
101 [kVK_ANSI_KeypadClear] = GB_WORKBOY_M,
102 [kVK_ANSI_KeypadMultiply] = GB_WORKBOY_H,
103 [kVK_ANSI_KeypadDivide] = GB_WORKBOY_J,
104 };
105
106 @implementation GBView
107 {
108 bool mouse_hidden;
109 NSTrackingArea *tracking_area;
110 bool _mouseHidingEnabled;
111 bool axisActive[2];
112 bool underclockKeyDown;
113 double clockMultiplier;
114 double analogClockMultiplier;
115 bool analogClockMultiplierValid;
116 NSEventModifierFlags previousModifiers;
117 JOYController *lastController;
118 bool _turbo;
119 bool _mouseControlEnabled;
120 NSMutableDictionary<NSNumber *, JOYController *> *_controllerMapping;
121 unsigned _lastPlayerCount;
122
123 bool _rapidA[4], _rapidB[4];
124 uint8_t _rapidACount[4], _rapidBCount[4];
125 }
126
127 + (instancetype)alloc
128 {
129 return [self allocWithZone:NULL];
130 }
131
132 + (instancetype)allocWithZone:(struct _NSZone *)zone
133 {
134 if (self == [GBView class]) {
135 if ([GBViewMetal isSupported]) {
136 return [GBViewMetal allocWithZone: zone];
137 }
138 return [GBViewGL allocWithZone: zone];
139 }
140 return [super allocWithZone:zone];
141 }
142
143 - (void) _init
144 {
145 [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
146
147 __unsafe_unretained GBView *weakSelf = self;
148 [self observeStandardDefaultsKey:@"GBAspectRatioUnkept" withBlock:^(id newValue) {
149 [weakSelf setFrame:weakSelf.superview.frame];
150 }];
151 [self observeStandardDefaultsKey:@"GBForceIntegerScale" withBlock:^(id newValue) {
152 [weakSelf setFrame:weakSelf.superview.frame];
153 }];
154 [self observeStandardDefaultsKey:@"JoyKitDefaultControllers" withBlock:^(id newValue) {
155 [weakSelf reassignControllers];
156 }];
157 tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
158 options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseMoved
159 owner:self
160 userInfo:nil];
161 [self addTrackingArea:tracking_area];
162 clockMultiplier = 1.0;
163 [self createInternalView];
164 [self addSubview:self.internalView];
165 self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
166 [JOYController registerListener:self];
167 _mouseControlEnabled = true;
168 [self reassignControllers];
169 }
170
171 - (void)controllerConnected:(JOYController *)controller
172 {
173 [self reassignControllers];
174 }
175
176 - (void)controllerDisconnected:(JOYController *)controller
177 {
178 [self reassignControllers];
179 }
180
181 - (unsigned)playerCount
182 {
183 if (self.document.partner) {
184 return 2;
185 }
186 if (!_gb) {
187 return 1;
188 }
189 return GB_get_player_count(_gb);
190 }
191
192 - (void)reassignControllers
193 {
194 unsigned playerCount = self.playerCount;
195 /* Don't assign controlelrs if there's only one player, allow all controllers. */
196 if (playerCount == 1) {
197 _controllerMapping = [NSMutableDictionary dictionary];
198 return;
199 }
200
201 if (!_controllerMapping) {
202 _controllerMapping = [NSMutableDictionary dictionary];
203 }
204
205 for (NSNumber *player in [_controllerMapping copy]) {
206 if (player.unsignedIntValue >= playerCount || !_controllerMapping[player].connected) {
207 [_controllerMapping removeObjectForKey:player];
208 }
209 }
210
211 _lastPlayerCount = playerCount;
212 for (unsigned i = 0; i < playerCount; i++) {
213 NSString *preferredJoypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"]
214 objectForKey:n2s(i)];
215 for (JOYController *controller in [JOYController allControllers]) {
216 if (!controller.connected) continue;
217 if ([controller.uniqueID isEqual:preferredJoypad]) {
218 _controllerMapping[@(i)] = controller;
219 break;
220 }
221 }
222 }
223 }
224
225 - (void)tryAssigningController:(JOYController *)controller
226 {
227 unsigned playerCount = self.playerCount;
228 if (playerCount == 1) return;
229 if (_controllerMapping.count == playerCount) return;
230 if ([_controllerMapping.allValues containsObject:controller]) return;
231 for (unsigned i = 0; i < playerCount; i++) {
232 if (!_controllerMapping[@(i)]) {
233 _controllerMapping[@(i)] = controller;
234 return;
235 }
236 }
237 }
238
239 - (NSDictionary<NSNumber *, JOYController *> *)controllerMapping
240 {
241 if (_lastPlayerCount != self.playerCount) {
242 [self reassignControllers];
243 }
244
245 return _controllerMapping;
246 }
247
248 - (void)screenSizeChanged
249 {
250 [super screenSizeChanged];
251
252 dispatch_async(dispatch_get_main_queue(), ^{
253 [self setFrame:self.superview.frame];
254 });
255 }
256
257 - (void)dealloc
258 {
259 if (mouse_hidden) {
260 mouse_hidden = false;
261 [NSCursor unhide];
262 }
263 [[NSNotificationCenter defaultCenter] removeObserver:self];
264 [self setRumble:0];
265 [JOYController unregisterListener:self];
266 }
267
268 - (instancetype)initWithCoder:(NSCoder *)coder
269 {
270 if (!(self = [super initWithCoder:coder])) {
271 return self;
272 }
273 [self _init];
274 return self;
275 }
276
277 - (instancetype)initWithFrame:(NSRect)frameRect
278 {
279 if (!(self = [super initWithFrame:frameRect])) {
280 return self;
281 }
282 [self _init];
283 return self;
284 }
285
286 - (void)setFrame:(NSRect)frame
287 {
288 NSView *superview = self.superview;
289 if (GB_unlikely(!superview)) return;
290 frame = superview.frame;
291 if (_gb && ![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]) {
292 double ratio = frame.size.width / frame.size.height;
293 double width = GB_get_screen_width(_gb);
294 double height = GB_get_screen_height(_gb);
295 if (ratio >= width / height) {
296 double new_width = round(frame.size.height / height * width);
297 frame.origin.x = floor((frame.size.width - new_width) / 2);
298 frame.size.width = new_width;
299 frame.origin.y = 0;
300 }
301 else {
302 double new_height = round(frame.size.width / width * height);
303 frame.origin.y = floor((frame.size.height - new_height) / 2);
304 frame.size.height = new_height;
305 frame.origin.x = 0;
306 }
307 }
308
309 if (_gb && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBForceIntegerScale"]) {
310 double factor = self.window.backingScaleFactor;
311 double width = GB_get_screen_width(_gb) / factor;
312 double height = GB_get_screen_height(_gb) / factor;
313
314 double new_width = floor(frame.size.width / width) * width;
315 double new_height = floor(frame.size.height / height) * height;
316 frame.origin.x += floor((frame.size.width - new_width) / 2);
317 frame.origin.y += floor((frame.size.height - new_height) / 2);
318 frame.size.width = new_width;
319 frame.size.height = new_height;
320 }
321
322 [super setFrame:frame];
323 }
324
325 - (void) flip
326 {
327 if (analogClockMultiplierValid && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]) {
328 clockMultiplier = 1.0;
329 GB_set_clock_multiplier(_gb, analogClockMultiplier);
330 if (self.document.partner) {
331 GB_set_clock_multiplier(self.document.partner.gb, analogClockMultiplier);
332 }
333 if (analogClockMultiplier == 1.0) {
334 analogClockMultiplierValid = false;
335 }
336 if (analogClockMultiplier < 2.0 && analogClockMultiplier > 1.0) {
337 GB_set_turbo_mode(_gb, false, false);
338 if (self.document.partner) {
339 GB_set_turbo_mode(self.document.partner.gb, false, false);
340 }
341 }
342 }
343 else {
344 if (underclockKeyDown && clockMultiplier > 0.5) {
345 clockMultiplier -= 1.0/16;
346 GB_set_clock_multiplier(_gb, clockMultiplier);
347 if (self.document.partner) {
348 GB_set_clock_multiplier(self.document.partner.gb, clockMultiplier);
349 }
350 }
351 if (!underclockKeyDown && clockMultiplier < 1.0) {
352 clockMultiplier += 1.0/16;
353 GB_set_clock_multiplier(_gb, clockMultiplier);
354 if (self.document.partner) {
355 GB_set_clock_multiplier(self.document.partner.gb, clockMultiplier);
356 }
357 }
358 }
359 if ((!analogClockMultiplierValid && clockMultiplier > 1) ||
360 _turbo || (analogClockMultiplierValid && analogClockMultiplier > 1)) {
361 [self.osdView displayText:@"Fast forwarding…"];
362 }
363 else if ((!analogClockMultiplierValid && clockMultiplier < 1) ||
364 (analogClockMultiplierValid && analogClockMultiplier < 1)) {
365 [self.osdView displayText:@"Slow motion…"];
366 }
367
368 for (unsigned i = GB_get_player_count(_gb); i--;) {
369 if (_rapidA[i]) {
370 _rapidACount[i]++;
371 GB_set_key_state_for_player(_gb, GB_KEY_A, i, !(_rapidACount[i] & 2));
372 }
373 if (_rapidB[i]) {
374 _rapidBCount[i]++;
375 GB_set_key_state_for_player(_gb, GB_KEY_B, i, !(_rapidBCount[i] & 2));
376 }
377 }
378 [super flip];
379 }
380
381 -(void)keyDown:(NSEvent *)theEvent
382 {
383 if ([theEvent type] != NSEventTypeFlagsChanged && theEvent.isARepeat) return;
384 unsigned short keyCode = theEvent.keyCode;
385 if (GB_workboy_is_enabled(_gb)) {
386 if (theEvent.keyCode < sizeof(workboy_vk_to_key) && workboy_vk_to_key[theEvent.keyCode]) {
387 GB_workboy_set_key(_gb, workboy_vk_to_key[theEvent.keyCode]);
388 return;
389 }
390 unichar c = [theEvent type] != NSEventTypeFlagsChanged? [theEvent.charactersIgnoringModifiers.lowercaseString characterAtIndex:0] : 0;
391 if (c < sizeof(workboy_ascii_to_key) && workboy_ascii_to_key[c]) {
392 GB_workboy_set_key(_gb, workboy_ascii_to_key[c]);
393 return;
394 }
395 }
396
397 bool handled = false;
398
399 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
400 unsigned player_count = GB_get_player_count(_gb);
401 if (self.document.partner) {
402 player_count = 2;
403 }
404 for (unsigned player = 0; player < player_count; player++) {
405 for (GBButton button = 0; button < GBKeyboardButtonCount; button++) {
406 NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
407 if (!key) continue;
408
409 if (key.unsignedShortValue == keyCode) {
410 handled = true;
411 switch (button) {
412 case GBTurbo:
413 if (self.document.isSlave) {
414 GB_set_turbo_mode(self.document.partner.gb, true, false);
415 }
416 else {
417 GB_set_turbo_mode(_gb, true, self.isRewinding);
418 }
419 _turbo = true;
420 analogClockMultiplierValid = false;
421 break;
422
423 case GBRewind:
424 if (!self.document.partner) {
425 self.isRewinding = true;
426 GB_set_turbo_mode(_gb, false, false);
427 _turbo = false;
428 }
429 break;
430
431 case GBUnderclock:
432 underclockKeyDown = true;
433 analogClockMultiplierValid = false;
434 break;
435
436 case GBRapidA:
437 _rapidA[player] = true;
438 _rapidACount[player] = 0;
439 GB_set_key_state_for_player(_gb, GB_KEY_A, player, true);
440 break;
441
442 case GBRapidB:
443 _rapidB[player] = true;
444 _rapidBCount[player] = 0;
445 GB_set_key_state_for_player(_gb, GB_KEY_B, player, true);
446 break;
447
448 default:
449 if (self.document.partner) {
450 if (player == 0) {
451 GB_set_key_state_for_player(_gb, (GB_key_t)button, 0, true);
452 if ((GB_key_t)button <= GB_KEY_DOWN) {
453 GB_set_use_faux_analog_inputs(_gb, 0, false);
454 }
455 }
456 else {
457 GB_set_key_state_for_player(self.document.partner.gb, (GB_key_t)button, 0, true);
458 if ((GB_key_t)button <= GB_KEY_DOWN) {
459 GB_set_use_faux_analog_inputs(self.document.partner.gb, 0, false);
460 }
461 }
462 }
463 else {
464 GB_set_key_state_for_player(_gb, (GB_key_t)button, player, true);
465 if ((GB_key_t)button <= GB_KEY_DOWN) {
466 GB_set_use_faux_analog_inputs(_gb, player, false);
467 }
468 }
469 break;
470 }
471 }
472 }
473 }
474
475 if (!handled && [theEvent type] != NSEventTypeFlagsChanged) {
476 [super keyDown:theEvent];
477 }
478 }
479
480 -(void)keyUp:(NSEvent *)theEvent
481 {
482 unsigned short keyCode = theEvent.keyCode;
483 if (GB_workboy_is_enabled(_gb)) {
484 if (keyCode == kVK_Shift || keyCode == kVK_RightShift) {
485 GB_workboy_set_key(_gb, GB_WORKBOY_SHIFT_UP);
486 }
487 else {
488 GB_workboy_set_key(_gb, GB_WORKBOY_NONE);
489 }
490
491 }
492 bool handled = false;
493
494 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
495 unsigned player_count = GB_get_player_count(_gb);
496 if (self.document.partner) {
497 player_count = 2;
498 }
499 for (unsigned player = 0; player < player_count; player++) {
500 for (GBButton button = 0; button < GBKeyboardButtonCount; button++) {
501 NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
502 if (!key) continue;
503
504 if (key.unsignedShortValue == keyCode) {
505 handled = true;
506 switch (button) {
507 case GBTurbo:
508 if (self.document.isSlave) {
509 GB_set_turbo_mode(self.document.partner.gb, false, false);
510 }
511 else {
512 GB_set_turbo_mode(_gb, false, false);
513 }
514 _turbo = false;
515 analogClockMultiplierValid = false;
516 break;
517
518 case GBRewind:
519 self.isRewinding = false;
520 break;
521
522 case GBUnderclock:
523 underclockKeyDown = false;
524 analogClockMultiplierValid = false;
525 break;
526
527 case GBRapidA:
528 _rapidA[player] = false;
529 GB_set_key_state_for_player(_gb, GB_KEY_A, player, false);
530 break;
531
532 case GBRapidB:
533 _rapidB[player] = false;
534 GB_set_key_state_for_player(_gb, GB_KEY_B, player, false);
535 break;
536
537 default:
538 if (self.document.partner) {
539 if (player == 0) {
540 GB_set_key_state_for_player(_gb, (GB_key_t)button, 0, false);
541 }
542 else {
543 GB_set_key_state_for_player(self.document.partner.gb, (GB_key_t)button, 0, false);
544 }
545 }
546 else {
547 GB_set_key_state_for_player(_gb, (GB_key_t)button, player, false);
548 }
549 break;
550 }
551 }
552 }
553 }
554 if (!handled && [theEvent type] != NSEventTypeFlagsChanged) {
555 [super keyUp:theEvent];
556 }
557 }
558
559 - (void)setRumble:(double)amp
560 {
561 double strength = [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBRumbleStrength"];
562 if (strength != 1) {
563 amp = pow(amp, strength) * strength;
564 }
565 [lastController setRumbleAmplitude:amp];
566 }
567
568 - (bool)shouldControllerUseJoystickForMotion:(JOYController *)controller
569 {
570 if (!_gb) return false;
571 if (!GB_has_accelerometer(_gb)) return false;
572 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]) return true;
573 for (JOYAxes3D *axes in controller.axes3D) {
574 if (axes.usage == JOYAxes3DUsageOrientation || axes.usage == JOYAxes3DUsageAcceleration) {
575 return false;
576 }
577 }
578 return true;
579 }
580
581 - (bool)allowController
582 {
583 if ([self.window isMainWindow]) return true;
584 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBAllowBackgroundControllers"]) {
585 if ([(Document *)[NSApplication sharedApplication].orderedDocuments.firstObject mainWindow] == self.window) {
586 return true;
587 }
588 }
589 return false;
590 }
591
592 - (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis
593 {
594 if (!_gb) return;
595 if (![self allowController]) return;
596
597 NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
598 if (!mapping) {
599 mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
600 }
601
602 if ((axis.usage == JOYAxisUsageR1 && !mapping) ||
603 axis.uniqueID == [mapping[@"AnalogUnderclock"] unsignedLongValue]){
604 analogClockMultiplier = MIN(MAX(1 - axis.value + 0.05, 1.0 / 3), 1.0);
605 analogClockMultiplierValid = true;
606 }
607
608 else if ((axis.usage == JOYAxisUsageL1 && !mapping) ||
609 axis.uniqueID == [mapping[@"AnalogTurbo"] unsignedLongValue]){
610 analogClockMultiplier = MIN(MAX(axis.value * 3 + 0.95, 1.0), 3.0);
611 analogClockMultiplierValid = true;
612 }
613 }
614
615 - (bool)controller:(JOYController *)controller applicableForPlayer:(unsigned)player effectivePlayer:(unsigned *)effectivePlayer effectiveGB:(GB_gameboy_t **)effectiveGB
616 {
617 NSDictionary<NSNumber *, JOYController *> *controllerMapping = [self controllerMapping];
618
619 JOYController *preferredJoypad = controllerMapping[@(player)];
620 if (preferredJoypad && preferredJoypad != controller) return false; // The player has a different assigned controller
621 if (!preferredJoypad && self.playerCount != 1) return false; // The player has no assigned controller in multiplayer mode, prevent controller inputs
622
623 dispatch_async(dispatch_get_main_queue(), ^{
624 [controller setPlayerLEDs:[controller LEDMaskForPlayer:player]];
625 });
626
627 *effectiveGB = _gb;
628 *effectivePlayer = player;
629
630 if (player && self.document.partner) {
631 *effectiveGB = self.document.partner.gb;
632 *effectivePlayer = 0;
633 if (controller != self.document.partner.view->lastController) {
634 [self setRumble:0];
635 self.document.partner.view->lastController = controller;
636 }
637 }
638 else {
639 if (controller != lastController) {
640 [self setRumble:0];
641 lastController = controller;
642 }
643 }
644 return true;
645 }
646
647 - (void)controller:(JOYController *)controller movedAxes2D:(JOYAxes2D *)axes
648 {
649 if (!_gb) return;
650 /* Always handle only the most dominant 2D input. */
651 for (JOYAxes2D *otherAxes in controller.axes2D) {
652 if (otherAxes == axes) continue;
653 if (otherAxes.distance > axes.distance) {
654 return;
655 }
656 }
657 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
658
659 if ([self shouldControllerUseJoystickForMotion:controller] && !self.mouseControlsActive) {
660 GB_set_accelerometer_values(_gb, -axes.value.x, -axes.value.y);
661 }
662 else if ([defaults boolForKey:@"GBFauxAnalogInputs"]) {
663 unsigned playerCount = self.playerCount;
664 for (unsigned player = 0; player < playerCount; player++) {
665 unsigned effectivePlayer;
666 GB_gameboy_t *effectiveGB;
667 if (![self controller:controller applicableForPlayer:player effectivePlayer:&effectivePlayer effectiveGB:&effectiveGB]) continue;
668
669 GB_set_use_faux_analog_inputs(effectiveGB, effectivePlayer, true);
670 NSPoint position = axes.value;
671 GB_set_faux_analog_inputs(effectiveGB, effectivePlayer, position.x, position.y);
672 }
673 }
674 }
675
676 - (void)controller:(JOYController *)controller movedAxes3D:(JOYAxes3D *)axes
677 {
678 if (!_gb) return;
679 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]) return;
680 if (self.mouseControlsActive) return;
681 if (controller != lastController) return;
682 // When using Joy-Cons in dual-controller grip, ignore motion data from the left Joy-Con
683 if (controller.joyconType == JOYJoyConTypeDual) {
684 for (JOYController *child in [(JOYCombinedController *)controller children]) {
685 if (child.joyconType != JOYJoyConTypeRight && [child.axes3D containsObject:axes]) {
686 return;
687 }
688 }
689 }
690
691 NSDictionary<NSNumber *, JOYController *> *controllerMapping = [self controllerMapping];
692 GB_gameboy_t *effectiveGB = _gb;
693
694 if (self.document.partner) {
695 if (controllerMapping[@1] == controller) {
696 effectiveGB = self.document.partner.gb;
697 }
698 if (controllerMapping[@0] != controller) {
699 return;
700 }
701
702 }
703
704 if (axes.usage == JOYAxes3DUsageOrientation) {
705 for (JOYAxes3D *axes in controller.axes3D) {
706 // Only use orientation if there's no acceleration axes
707 if (axes.usage == JOYAxes3DUsageAcceleration) {
708 return;
709 }
710 }
711 JOYPoint3D point = axes.normalizedValue;
712 GB_set_accelerometer_values(effectiveGB, point.x, point.z);
713 }
714 else if (axes.usage == JOYAxes3DUsageAcceleration) {
715 JOYPoint3D point = axes.gUnitsValue;
716 GB_set_accelerometer_values(effectiveGB, point.x, point.z);
717 }
718 }
719
720 - (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
721 {
722 if (!_gb) return;
723 if (![self allowController]) return;
724 _mouseControlEnabled = false;
725 if (button.type == JOYButtonTypeAxes2DEmulated && [self shouldControllerUseJoystickForMotion:controller]) return;
726
727 [self tryAssigningController:controller];
728
729 unsigned playerCount = self.playerCount;
730
731 IOPMAssertionID assertionID;
732 IOPMAssertionDeclareUserActivity(CFSTR(""), kIOPMUserActiveLocal, &assertionID);
733
734
735 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
736 bool fauxAnalog = [defaults boolForKey:@"GBFauxAnalogInputs"];
737
738 for (unsigned player = 0; player < playerCount; player++) {
739 unsigned effectivePlayer;
740 GB_gameboy_t *effectiveGB;
741 if (![self controller:controller applicableForPlayer:player effectivePlayer:&effectivePlayer effectiveGB:&effectiveGB]) continue;
742
743 NSDictionary *mapping = [defaults dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
744 if (!mapping) {
745 mapping = [defaults dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
746 }
747
748 JOYButtonUsage usage = ((JOYButtonUsage)[mapping[n2s(button.uniqueID)] unsignedIntValue]) ?: button.usage;
749 if (!mapping && usage >= JOYButtonUsageGeneric0) {
750 usage = GB_inline_const(JOYButtonUsage[], {JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX})[(usage - JOYButtonUsageGeneric0) & 3];
751 }
752
753 if (usage >= JOYButtonUsageDPadLeft && usage <= JOYButtonUsageDPadDown) {
754 if (fauxAnalog && button.type == JOYButtonTypeAxes2DEmulated) {
755 // This isn't a real button, it's an emulated Axes2D. We want to handle it as an Axes2D instead
756 continue;
757 }
758 else {
759 // User used a digital direction input, revert to non-analog inputs
760 GB_set_use_faux_analog_inputs(effectiveGB, effectivePlayer, false);
761 }
762 }
763
764 switch ((unsigned)usage) {
765
766 case JOYButtonUsageNone: break;
767 case JOYButtonUsageA: GB_set_key_state_for_player(effectiveGB, GB_KEY_A, effectivePlayer, button.isPressed); break;
768 case JOYButtonUsageB: GB_set_key_state_for_player(effectiveGB, GB_KEY_B, effectivePlayer, button.isPressed); break;
769 case JOYButtonUsageC: break;
770 case JOYButtonUsageStart:
771 case JOYButtonUsageX: GB_set_key_state_for_player(effectiveGB, GB_KEY_START, effectivePlayer, button.isPressed); break;
772 case JOYButtonUsageSelect:
773 case JOYButtonUsageY: GB_set_key_state_for_player(effectiveGB, GB_KEY_SELECT, effectivePlayer, button.isPressed); break;
774 case JOYButtonUsageR2:
775 case JOYButtonUsageL2:
776 case JOYButtonUsageZ: {
777 self.isRewinding = button.isPressed;
778 if (button.isPressed) {
779 if (self.document.isSlave) {
780 GB_set_turbo_mode(self.document.partner.gb, false, false);
781 }
782 else {
783 GB_set_turbo_mode(_gb, false, false);
784 }
785 _turbo = false;
786 }
787 break;
788 }
789
790 case JOYButtonUsageL1: {
791 if (!analogClockMultiplierValid || analogClockMultiplier == 1.0 || !button.isPressed) {
792 if (self.document.isSlave) {
793 GB_set_turbo_mode(self.document.partner.gb, button.isPressed, false);
794 }
795 else {
796 GB_set_turbo_mode(_gb, button.isPressed, button.isPressed && self.isRewinding);
797 }
798 _turbo = button.isPressed;
799 }
800 break;
801 }
802
803 case JOYButtonUsageR1: underclockKeyDown = button.isPressed; break;
804 case JOYButtonUsageDPadLeft: GB_set_key_state_for_player(effectiveGB, GB_KEY_LEFT, effectivePlayer, button.isPressed); break;
805 case JOYButtonUsageDPadRight: GB_set_key_state_for_player(effectiveGB, GB_KEY_RIGHT, effectivePlayer, button.isPressed); break;
806 case JOYButtonUsageDPadUp: GB_set_key_state_for_player(effectiveGB, GB_KEY_UP, effectivePlayer, button.isPressed); break;
807 case JOYButtonUsageDPadDown: GB_set_key_state_for_player(effectiveGB, GB_KEY_DOWN, effectivePlayer, button.isPressed); break;
808
809 case GBJoyKitRapidA:
810 _rapidA[effectivePlayer] = button.isPressed;
811 _rapidACount[effectivePlayer] = 0;
812 GB_set_key_state_for_player(_gb, GB_KEY_A, effectivePlayer, button.isPressed);
813 break;
814
815 case GBJoyKitRapidB:
816 _rapidB[effectivePlayer] = button.isPressed;
817 _rapidBCount[effectivePlayer] = 0;
818 GB_set_key_state_for_player(_gb, GB_KEY_B, effectivePlayer, button.isPressed);
819 break;
820 }
821 }
822 }
823
824 - (BOOL)acceptsFirstResponder
825 {
826 return true;
827 }
828
829 - (bool)mouseControlsActive
830 {
831 return _gb && GB_is_inited(_gb) && GB_has_accelerometer(_gb) &&
832 _mouseControlEnabled && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7AllowMouse"];
833 }
834
835 - (void)mouseEntered:(NSEvent *)theEvent
836 {
837 if (!mouse_hidden) {
838 mouse_hidden = true;
839 if (_mouseHidingEnabled &&
840 !self.mouseControlsActive) {
841 [NSCursor hide];
842 }
843 }
844 [super mouseEntered:theEvent];
845 }
846
847 - (void)mouseExited:(NSEvent *)theEvent
848 {
849 if (mouse_hidden) {
850 mouse_hidden = false;
851 if (_mouseHidingEnabled) {
852 [NSCursor unhide];
853 }
854 }
855 [super mouseExited:theEvent];
856 }
857
858 - (void)mouseDown:(NSEvent *)event
859 {
860 _mouseControlEnabled = true;
861 if (self.mouseControlsActive) {
862 if (event.type == NSEventTypeLeftMouseDown) {
863 GB_set_key_state(_gb, GB_KEY_A, true);
864 }
865 }
866 }
867
868 - (void)mouseUp:(NSEvent *)event
869 {
870 if (self.mouseControlsActive) {
871 if (event.type == NSEventTypeLeftMouseUp) {
872 GB_set_key_state(_gb, GB_KEY_A, false);
873 }
874 }
875 }
876
877 - (void)mouseMoved:(NSEvent *)event
878 {
879 if (self.mouseControlsActive) {
880 NSPoint point = [self convertPoint:[event locationInWindow] toView:nil];
881
882 point.x /= self.frame.size.width;
883 point.x *= 2;
884 point.x -= 1;
885
886 point.y /= self.frame.size.height;
887 point.y *= 2;
888 point.y -= 1;
889
890 if (GB_get_screen_width(_gb) != 160) { // has border
891 point.x *= 256 / 160.0;
892 point.y *= 224 / 114.0;
893 }
894 GB_set_accelerometer_values(_gb, -point.x, point.y);
895 }
896 }
897
898 - (void)setMouseHidingEnabled:(bool)mouseHidingEnabled
899 {
900 if (mouseHidingEnabled == _mouseHidingEnabled) return;
901 if (![NSThread isMainThread]) {
902 dispatch_async(dispatch_get_main_queue(), ^{
903 [self setMouseHidingEnabled:mouseHidingEnabled];
904 });
905 return;
906 }
907
908 _mouseHidingEnabled = mouseHidingEnabled;
909
910 if (mouse_hidden && _mouseHidingEnabled) {
911 [NSCursor hide];
912 }
913
914 if (mouse_hidden && !_mouseHidingEnabled) {
915 [NSCursor unhide];
916 }
917 }
918
919 - (bool)isMouseHidingEnabled
920 {
921 return _mouseHidingEnabled;
922 }
923
924 - (void) flagsChanged:(NSEvent *)event
925 {
926 if (event.modifierFlags > previousModifiers) {
927 [self keyDown:event];
928 }
929 else {
930 [self keyUp:event];
931 }
932
933 previousModifiers = event.modifierFlags;
934 }
935
936 -(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
937 {
938 NSPasteboard *pboard = [sender draggingPasteboard];
939
940 if ( [[pboard types] containsObject:NSURLPboardType] ) {
941 NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
942 if (GB_is_save_state(fileURL.fileSystemRepresentation)) {
943 return NSDragOperationGeneric;
944 }
945 }
946 return NSDragOperationNone;
947 }
948
949 -(BOOL)performDragOperation:(id<NSDraggingInfo>)sender
950 {
951 NSPasteboard *pboard = [sender draggingPasteboard];
952
953 if ( [[pboard types] containsObject:NSURLPboardType] ) {
954 NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
955 return [_document loadStateFile:fileURL.fileSystemRepresentation noErrorOnNotFound:false];
956 }
957
958 return false;
959 }
960
961 - (NSImage *)renderToImage;
962 {
963 /* Not going to support this on OpenGL, OpenGL is too much of a terrible API for me
964 to bother figuring out how the hell something so trivial can be done. */
965 return nil;
966 }
967 @end
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.