SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
Cocoa/GBPreferencesWindow.m
1 #import "GBPreferencesWindow.h"
2 #import "GBJoyConManager.h"
3 #import "NSString+StringForKey.h"
4 #import "GBButtons.h"
5 #import "BigSurToolbar.h"
6 #import "GBViewMetal.h"
7 #import "GBWarningPopover.h"
8 #import <Carbon/Carbon.h>
9
10 @implementation GBPreferencesWindow
11 {
12 bool is_button_being_modified;
13 NSInteger button_being_modified;
14 signed joystick_configuration_state;
15 NSString *joystick_being_configured;
16 bool joypad_wait;
17
18 NSEventModifierFlags previousModifiers;
19 }
20
21 - (NSWindowToolbarStyle)toolbarStyle
22 {
23 return NSWindowToolbarStylePreference;
24 }
25
26 - (void)close
27 {
28 joystick_configuration_state = -1;
29 [self.configureJoypadButton setEnabled:true];
30 [self.skipButton setEnabled:false];
31 [self.configureJoypadButton setTitle:@"Configure Controller"];
32 [super close];
33 }
34
35 static inline NSString *keyEquivalentString(NSMenuItem *item)
36 {
37 return [NSString stringWithFormat:@"%s%@", (item.keyEquivalentModifierMask & NSEventModifierFlagShift)? "^":"", item.keyEquivalent];
38 }
39
40 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
41 {
42 if (self.playerListButton.selectedTag == 0) {
43 return GBKeyboardButtonCount;
44 }
45 return GBPerPlayerButtonCount;
46 }
47
48 - (unsigned) usesForKey:(unsigned) key
49 {
50 unsigned ret = 0;
51 for (unsigned player = 4; player--;) {
52 for (unsigned button = player == 0? GBKeyboardButtonCount:GBPerPlayerButtonCount; button--;) {
53 NSNumber *other = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(button, player)];
54 if (other && [other unsignedIntValue] == key) {
55 ret++;
56 }
57 }
58 }
59 return ret;
60 }
61
62 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
63 {
64 if ([tableColumn.identifier isEqualToString:@"keyName"]) {
65 return GBButtonNames[row];
66 }
67
68 if (is_button_being_modified && button_being_modified == row) {
69 return @"Select a new key…";
70 }
71
72 NSNumber *key = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(row, self.playerListButton.selectedTag)];
73 if (key) {
74 if ([self usesForKey:[key unsignedIntValue]] > 1) {
75 return [[NSAttributedString alloc] initWithString:[NSString displayStringForKeyCode: [key unsignedIntegerValue]]
76 attributes:@{NSForegroundColorAttributeName: [NSColor colorWithRed:0.9375 green:0.25 blue:0.25 alpha:1.0],
77 NSFontAttributeName: [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]
78 }];
79 }
80 return [NSString displayStringForKeyCode: [key unsignedIntegerValue]];
81 }
82
83 return @"";
84 }
85
86 - (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
87 {
88
89 dispatch_async(dispatch_get_main_queue(), ^{
90 is_button_being_modified = true;
91 button_being_modified = row;
92 tableView.enabled = false;
93 self.playerListButton.enabled = false;
94 [tableView reloadData];
95 [self makeFirstResponder:self];
96 });
97 return false;
98 }
99
100 -(void)keyDown:(NSEvent *)theEvent
101 {
102 if (!is_button_being_modified) {
103 if (self.firstResponder != self.controlsTableView && [theEvent type] != NSEventTypeFlagsChanged) {
104 [super keyDown:theEvent];
105 }
106 return;
107 }
108
109 is_button_being_modified = false;
110
111 [[NSUserDefaults standardUserDefaults] setInteger:theEvent.keyCode
112 forKey:button_to_preference_name(button_being_modified, self.playerListButton.selectedTag)];
113 self.controlsTableView.enabled = true;
114 self.playerListButton.enabled = true;
115 [self.controlsTableView reloadData];
116 [self makeFirstResponder:self.controlsTableView];
117 }
118
119 - (void) flagsChanged:(NSEvent *)event
120 {
121 if (event.modifierFlags > previousModifiers) {
122 [self keyDown:event];
123 }
124
125 previousModifiers = event.modifierFlags;
126 }
127
128 - (void)updatePalettesMenu
129 {
130 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
131 NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
132 NSMenu *menu = _colorPalettePopupButton.menu;
133 while (menu.itemArray.count != 4) {
134 [menu removeItemAtIndex:4];
135 }
136 [menu addItem:[NSMenuItem separatorItem]];
137 for (NSString *name in [themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]) {
138 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:name action:nil keyEquivalent:@""];
139 item.tag = -2;
140 [menu addItem:item];
141 }
142 if (themes) {
143 [menu addItem:[NSMenuItem separatorItem]];
144 }
145 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"Custom…" action:nil keyEquivalent:@""];
146 item.tag = -1;
147 [menu addItem:item];
148 }
149
150 - (IBAction)colorPaletteChanged:(id)sender
151 {
152 signed tag = [sender selectedItem].tag;
153 if (tag == -2) {
154 [[NSUserDefaults standardUserDefaults] setObject:@(-1)
155 forKey:@"GBColorPalette"];
156 [[NSUserDefaults standardUserDefaults] setObject:[sender selectedItem].title
157 forKey:@"GBCurrentTheme"];
158
159 }
160 else if (tag == -1) {
161 [[NSUserDefaults standardUserDefaults] setObject:@(-1)
162 forKey:@"GBColorPalette"];
163 [_paletteEditorController awakeFromNib];
164 [self beginSheet:_paletteEditor completionHandler:^(NSModalResponse returnCode) {
165 [self updatePalettesMenu];
166 [_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
167 }];
168 }
169 else {
170 [[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
171 forKey:@"GBColorPalette"];
172 }
173 [[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
174 }
175
176 - (IBAction)hotkey1Changed:(id)sender
177 {
178 [[NSUserDefaults standardUserDefaults] setObject:keyEquivalentString([sender selectedItem])
179 forKey:@"GBJoypadHotkey1"];
180 }
181
182 - (IBAction)hotkey2Changed:(id)sender
183 {
184 [[NSUserDefaults standardUserDefaults] setObject:keyEquivalentString([sender selectedItem])
185 forKey:@"GBJoypadHotkey2"];
186 }
187
188
189 - (IBAction) configureJoypad:(id)sender
190 {
191 [self.configureJoypadButton setEnabled:false];
192 [self.skipButton setEnabled:true];
193 joystick_being_configured = nil;
194 [self advanceConfigurationStateMachine];
195 }
196
197 - (IBAction) skipButton:(id)sender
198 {
199 [self advanceConfigurationStateMachine];
200 }
201
202 - (void) advanceConfigurationStateMachine
203 {
204 joystick_configuration_state++;
205 if (joystick_configuration_state == GBUnderclock) {
206 [self.configureJoypadButton setTitle:@"Press Button for Slo-Mo"]; // Full name is too long :<
207 }
208 else if (joystick_configuration_state < GBTotalButtonCount) {
209 [self.configureJoypadButton setTitle:[NSString stringWithFormat:@"Press Button for %@", GBButtonNames[joystick_configuration_state]]];
210 }
211 else {
212 joystick_configuration_state = -1;
213 [self.configureJoypadButton setEnabled:true];
214 [self.skipButton setEnabled:false];
215 [self.configureJoypadButton setTitle:@"Configure Joypad"];
216 }
217 }
218
219 - (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
220 {
221 /* Debounce */
222 if (joypad_wait) return;
223 joypad_wait = true;
224 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
225 joypad_wait = false;
226 });
227
228 if (!button.isPressed) return;
229 if (joystick_configuration_state == -1) return;
230 if (joystick_configuration_state == GBTotalButtonCount) return;
231 if (!joystick_being_configured) {
232 joystick_being_configured = controller.uniqueID;
233 }
234 else if (![joystick_being_configured isEqualToString:controller.uniqueID]) {
235 return;
236 }
237
238 NSMutableDictionary *instance_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"] mutableCopy];
239
240 NSMutableDictionary *name_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"] mutableCopy];
241
242
243 if (!instance_mappings) {
244 instance_mappings = [[NSMutableDictionary alloc] init];
245 }
246
247 if (!name_mappings) {
248 name_mappings = [[NSMutableDictionary alloc] init];
249 }
250
251 NSMutableDictionary *mapping = nil;
252 if (joystick_configuration_state != 0) {
253 mapping = [instance_mappings[controller.uniqueID] mutableCopy];
254 }
255 else {
256 mapping = [[NSMutableDictionary alloc] init];
257 }
258
259
260 static const unsigned gb_to_joykit[] = {
261 [GBRight] = JOYButtonUsageDPadRight,
262 [GBLeft] = JOYButtonUsageDPadLeft,
263 [GBUp] = JOYButtonUsageDPadUp,
264 [GBDown] = JOYButtonUsageDPadDown,
265 [GBA] = JOYButtonUsageA,
266 [GBB] = JOYButtonUsageB,
267 [GBSelect] = JOYButtonUsageSelect,
268 [GBStart] = JOYButtonUsageStart,
269 [GBRapidA] = GBJoyKitRapidA,
270 [GBRapidB] = GBJoyKitRapidB,
271 [GBTurbo] = JOYButtonUsageL1,
272 [GBRewind] = JOYButtonUsageL2,
273 [GBUnderclock] = JOYButtonUsageR1,
274 [GBHotkey1] = GBJoyKitHotkey1,
275 [GBHotkey2] = GBJoyKitHotkey2,
276 };
277
278 if (joystick_configuration_state == GBUnderclock) {
279 mapping[@"AnalogUnderclock"] = nil;
280 double max = 0;
281 for (JOYAxis *axis in controller.axes) {
282 if ((axis.value > 0.5 || (axis.equivalentButtonUsage == button.usage)) && axis.value >= max) {
283 mapping[@"AnalogUnderclock"] = @(axis.uniqueID);
284 break;
285 }
286 }
287 }
288
289 if (joystick_configuration_state == GBTurbo) {
290 mapping[@"AnalogTurbo"] = nil;
291 double max = 0;
292 for (JOYAxis *axis in controller.axes) {
293 if ((axis.value > 0.5 || (axis.equivalentButtonUsage == button.usage)) && axis.value >= max) {
294 max = axis.value;
295 mapping[@"AnalogTurbo"] = @(axis.uniqueID);
296 }
297 }
298 }
299
300 mapping[n2s(button.uniqueID)] = @(gb_to_joykit[joystick_configuration_state]);
301
302 instance_mappings[controller.uniqueID] = mapping;
303 name_mappings[controller.deviceName] = mapping;
304 [[NSUserDefaults standardUserDefaults] setObject:instance_mappings forKey:@"JoyKitInstanceMapping"];
305 [[NSUserDefaults standardUserDefaults] setObject:name_mappings forKey:@"JoyKitNameMapping"];
306 [self advanceConfigurationStateMachine];
307 }
308
309 - (void)awakeFromNib
310 {
311 [super awakeFromNib];
312 [self updateBootROMFolderButton];
313 [[NSDistributedNotificationCenter defaultCenter] addObserver:self.controlsTableView selector:@selector(reloadData) name:(NSString*)kTISNotifySelectedKeyboardInputSourceChanged object:nil];
314 [JOYController registerListener:self];
315 joystick_configuration_state = -1;
316 [self refreshJoypadMenu:nil];
317
318 NSString *keyEquivalent = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBJoypadHotkey1"];
319 for (NSMenuItem *item in _hotkey1PopupButton.menu.itemArray) {
320 if ([keyEquivalent isEqualToString:keyEquivalentString(item)]) {
321 [_hotkey1PopupButton selectItem:item];
322 break;
323 }
324 }
325
326 keyEquivalent = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBJoypadHotkey2"];
327 for (NSMenuItem *item in _hotkey2PopupButton.menu.itemArray) {
328 if ([keyEquivalent isEqualToString:keyEquivalentString(item)]) {
329 [_hotkey2PopupButton selectItem:item];
330 break;
331 }
332 }
333
334 [self updatePalettesMenu];
335 NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
336 if (mode >= 0) {
337 [_colorPalettePopupButton selectItemWithTag:mode];
338 }
339 else {
340 [_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
341 }
342
343 _fontSizeStepper.intValue = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBDebuggerFontSize"];
344 [self updateFonts];
345
346 double cap = [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBTurboCap"];
347 if (cap) {
348 _turboCapSlider.intValue = round(cap * 100);
349 _turboCapButton.state = NSOnState;
350 }
351 [self turboCapToggled:_turboCapButton];
352 }
353
354 - (IBAction)fontSizeChanged:(id)sender
355 {
356 NSString *selectedFont = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBDebuggerFont"];
357 [[NSUserDefaults standardUserDefaults] setInteger:[sender intValue] forKey:@"GBDebuggerFontSize"];
358 [_fontPopupButton setDisplayTitle:[NSString stringWithFormat:@"%@ %upt", selectedFont, (unsigned)[[NSUserDefaults standardUserDefaults] integerForKey:@"GBDebuggerFontSize"]]];
359 }
360
361 - (IBAction)fontChanged:(id)sender
362 {
363 NSString *selectedFont = _fontPopupButton.selectedItem.title;
364 [[NSUserDefaults standardUserDefaults] setObject:selectedFont forKey:@"GBDebuggerFont"];
365 [_fontPopupButton setDisplayTitle:[NSString stringWithFormat:@"%@ %upt", selectedFont, (unsigned)[[NSUserDefaults standardUserDefaults] integerForKey:@"GBDebuggerFontSize"]]];
366
367 }
368
369 - (void)updateFonts
370 {
371 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
372 NSFontManager *fontManager = [NSFontManager sharedFontManager];
373 NSArray *allFamilies = [fontManager availableFontFamilies];
374 NSMutableSet *families = [NSMutableSet set];
375 for (NSString *family in allFamilies) {
376 if ([fontManager fontNamed:family hasTraits:NSFixedPitchFontMask]) {
377 [families addObject:family];
378 }
379 }
380
381 bool hasSFMono = false;
382 if (@available(macOS 10.15, *)) {
383 hasSFMono = [[NSFont monospacedSystemFontOfSize:12 weight:NSFontWeightRegular].displayName containsString:@"SF"];
384 }
385
386 if (hasSFMono) {
387 [families addObject:@"SF Mono"];
388 }
389
390 NSArray *sortedFamilies = [[families allObjects] sortedArrayUsingSelector:@selector(compare:)];
391
392 dispatch_async(dispatch_get_main_queue(), ^{
393 if (![families containsObject:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBDebuggerFont"]]) {
394 [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"GBDebuggerFont"];
395 }
396
397 [_fontPopupButton.menu removeAllItems];
398 for (NSString *family in sortedFamilies) {
399 [_fontPopupButton addItemWithTitle:family];
400 }
401 NSString *selectedFont = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBDebuggerFont"];
402 [_fontPopupButton selectItemWithTitle:selectedFont];
403 [_fontPopupButton setDisplayTitle:[NSString stringWithFormat:@"%@ %upt", selectedFont, (unsigned)[[NSUserDefaults standardUserDefaults] integerForKey:@"GBDebuggerFontSize"]]];
404 });
405 });
406 }
407
408 - (void)dealloc
409 {
410 [JOYController unregisterListener:self];
411 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self.controlsTableView];
412 }
413
414 - (IBAction)selectOtherBootROMFolder:(id)sender
415 {
416 NSOpenPanel *panel = [[NSOpenPanel alloc] init];
417 [panel setCanChooseDirectories:true];
418 [panel setCanChooseFiles:false];
419 [panel setPrompt:@"Select"];
420 [panel setDirectoryURL:[[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"]];
421 [panel beginSheetModalForWindow:self completionHandler:^(NSModalResponse result) {
422 if (result == NSModalResponseOK) {
423 NSURL *url = [[panel URLs] firstObject];
424 [[NSUserDefaults standardUserDefaults] setURL:url forKey:@"GBBootROMsFolder"];
425 }
426 [self updateBootROMFolderButton];
427 }];
428
429 }
430
431 - (void) updateBootROMFolderButton
432 {
433 NSURL *url = [[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"];
434 BOOL is_dir = false;
435 [[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&is_dir];
436 if (!is_dir) url = nil;
437
438 if (url) {
439 [self.bootROMsFolderItem setTitle:[url lastPathComponent]];
440 NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
441 [icon setSize:NSMakeSize(16, 16)];
442 [self.bootROMsFolderItem setHidden:false];
443 [self.bootROMsFolderItem setImage:icon];
444 [self.bootROMsButton selectItemAtIndex:1];
445 }
446 else {
447 [self.bootROMsFolderItem setHidden:true];
448 [self.bootROMsButton selectItemAtIndex:0];
449 }
450 }
451
452 - (IBAction)useBuiltinBootROMs:(id)sender
453 {
454 [[NSUserDefaults standardUserDefaults] setURL:nil forKey:@"GBBootROMsFolder"];
455 [self updateBootROMFolderButton];
456 }
457
458 - (void)controllerConnected:(JOYController *)controller
459 {
460 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
461 [self refreshJoypadMenu:nil];
462 });
463 }
464
465 - (void)controllerDisconnected:(JOYController *)controller
466 {
467 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
468 [self refreshJoypadMenu:nil];
469 });
470 }
471
472 - (IBAction)refreshJoypadMenu:(id)sender
473 {
474 bool preferred_is_connected = false;
475 NSString *player_string = n2s(self.playerListButton.selectedTag);
476 NSString *selected_controller = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"][player_string];
477
478 [self.preferredJoypadButton removeAllItems];
479 [self.preferredJoypadButton addItemWithTitle:@"None"];
480 for (JOYController *controller in [JOYController allControllers]) {
481 [self.preferredJoypadButton addItemWithTitle:[NSString stringWithFormat:@"%@ (%@)", controller.deviceName, controller.uniqueID]];
482
483 self.preferredJoypadButton.lastItem.identifier = controller.uniqueID;
484
485 if ([controller.uniqueID isEqualToString:selected_controller]) {
486 preferred_is_connected = true;
487 [self.preferredJoypadButton selectItem:self.preferredJoypadButton.lastItem];
488 }
489 }
490
491 if (!preferred_is_connected && selected_controller) {
492 [self.preferredJoypadButton addItemWithTitle:[NSString stringWithFormat:@"Unavailable Controller (%@)", selected_controller]];
493 self.preferredJoypadButton.lastItem.identifier = selected_controller;
494 [self.preferredJoypadButton selectItem:self.preferredJoypadButton.lastItem];
495 }
496
497
498 if (!selected_controller) {
499 [self.preferredJoypadButton selectItemWithTitle:@"None"];
500 }
501 [self.controlsTableView reloadData];
502 }
503
504 - (IBAction)changeDefaultJoypad:(id)sender
505 {
506 NSMutableDictionary *default_joypads = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"] mutableCopy];
507 if (!default_joypads) {
508 default_joypads = [[NSMutableDictionary alloc] init];
509 }
510
511 NSString *player_string = n2s(self.playerListButton.selectedTag);
512 if ([[sender titleOfSelectedItem] isEqualToString:@"None"]) {
513 [default_joypads removeObjectForKey:player_string];
514 }
515 else {
516 default_joypads[player_string] = [[sender selectedItem] identifier];
517 }
518 [[NSUserDefaults standardUserDefaults] setObject:default_joypads forKey:@"JoyKitDefaultControllers"];
519 }
520
521 - (IBAction)displayColorCorrectionHelp:(id)sender
522 {
523 [GBWarningPopover popoverWithContents:
524 GB_inline_const(NSString *[], {
525 [GB_COLOR_CORRECTION_DISABLED] = @"Colors are directly interpreted as sRGB, resulting in unbalanced colors and inaccurate hues.",
526 [GB_COLOR_CORRECTION_CORRECT_CURVES] = @"Colors have their brightness corrected, but hues remain unbalanced.",
527 [GB_COLOR_CORRECTION_MODERN_BALANCED] = @"Emulates a modern display. Blue contrast is moderately enhanced at the cost of slight hue inaccuracy.",
528 [GB_COLOR_CORRECTION_MODERN_BOOST_CONTRAST] = @"Like Modern – Balanced, but further boosts the contrast of greens and magentas that is lacking on the original hardware.",
529 [GB_COLOR_CORRECTION_REDUCE_CONTRAST] = @"Slightly reduce the contrast to better represent the tint and contrast of the original display.",
530 [GB_COLOR_CORRECTION_LOW_CONTRAST] = @"Harshly reduce the contrast to accurately represent the tint and low contrast of the original display.",
531 [GB_COLOR_CORRECTION_MODERN_ACCURATE] = @"Emulates a modern display. Colors have their hues and brightness corrected.",
532 }) [self.colorCorrectionPopupButton.selectedItem.tag]
533 title:self.colorCorrectionPopupButton.selectedItem.title
534 onView:sender
535 timeout:6
536 preferredEdge:NSRectEdgeMaxX];
537 }
538
539 - (IBAction)displayHighPassHelp:(id)sender
540 {
541 [GBWarningPopover popoverWithContents:
542 GB_inline_const(NSString *[], {
543 [GB_HIGHPASS_OFF] = @"No high-pass filter will be applied. DC offset will be kept, pausing and resuming will trigger audio pops.",
544 [GB_HIGHPASS_ACCURATE] = @"An accurate high-pass filter will be applied, removing the DC offset while somewhat attenuating the bass.",
545 [GB_HIGHPASS_REMOVE_DC_OFFSET] = @"A high-pass filter will be applied to the DC offset itself, removing the DC offset while preserving the waveform.",
546 }) [self.highpassFilterPopupButton.selectedItem.tag]
547 title:self.highpassFilterPopupButton.selectedItem.title
548 onView:sender
549 timeout:6
550 preferredEdge:NSRectEdgeMaxX];
551 }
552
553 - (IBAction)arrangeJoyCons:(id)sender
554 {
555 [GBJoyConManager sharedInstance].arrangementMode = true;
556 [self beginSheet:self.joyconsSheet completionHandler:nil];
557 }
558
559 - (IBAction)closeJoyConsSheet:(id)sender
560 {
561 [self endSheet:self.joyconsSheet];
562 [GBJoyConManager sharedInstance].arrangementMode = false;
563 }
564
565 - (IBAction)turboCapToggled:(NSButton *)sender
566 {
567 if (sender.state) {
568 _turboCapSlider.enabled = true;
569 [self turboCapChanged:_turboCapSlider];
570 if (@available(macOS 10.10, *)) {
571 _turboCapLabel.textColor = [NSColor labelColor];
572 }
573 else {
574 _turboCapLabel.textColor = [NSColor blackColor];
575 }
576 }
577 else {
578 _turboCapSlider.enabled = false;
579 _turboCapLabel.enabled = false;
580 [[NSUserDefaults standardUserDefaults] setDouble:0 forKey:@"GBTurboCap"];
581 if (@available(macOS 10.10, *)) {
582 _turboCapLabel.textColor = [NSColor disabledControlTextColor];
583 }
584 else {
585 _turboCapLabel.textColor = [NSColor colorWithWhite:0 alpha:0.25];
586 }
587 }
588 }
589
590 - (IBAction)turboCapChanged:(NSSlider *)sender
591 {
592 _turboCapLabel.stringValue = [NSString stringWithFormat:@"%d%%", sender.intValue];
593 [[NSUserDefaults standardUserDefaults] setDouble:sender.doubleValue / 100.0 forKey:@"GBTurboCap"];
594 }
595
596 @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.