Add Rapid A/B to Cocoa and SDL, closes #589

This commit is contained in:
Lior Halphon 2024-08-30 16:12:31 +03:00
parent d4c715b5f0
commit d97c2fb701
11 changed files with 148 additions and 40 deletions

View File

@ -43,7 +43,7 @@ static uint32_t color_to_int(NSColor *color)
[NSApplication sharedApplication].applicationIconImage = [NSImage imageNamed:@"AppIcon"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
for (unsigned i = 0; i < GBButtonCount; i++) {
for (unsigned i = 0; i < GBKeyboardButtonCount; i++) {
if ([[defaults objectForKey:button_to_preference_name(i, 0)] isKindOfClass:[NSString class]]) {
[defaults removeObjectForKey:button_to_preference_name(i, 0)];
}

View File

@ -7,20 +7,24 @@ typedef enum {
GBB,
GBSelect,
GBStart,
GBRapidA,
GBRapidB,
GBTurbo,
GBRewind,
GBUnderclock,
GBHotkey1,
GBHotkey2,
GBJoypadButtonCount,
GBButtonCount = GBUnderclock + 1,
GBGameBoyButtonCount = GBStart + 1,
GBTotalButtonCount,
GBKeyboardButtonCount = GBUnderclock + 1,
GBPerPlayerButtonCount = GBRapidB + 1,
} GBButton;
#define GBJoyKitHotkey1 JOYButtonUsageGeneric0 + 0x100
#define GBJoyKitHotkey2 JOYButtonUsageGeneric0 + 0x101
#define GBJoyKitRapidA JOYButtonUsageGeneric0 + 0x102
#define GBJoyKitRapidB JOYButtonUsageGeneric0 + 0x103
extern NSString const *GBButtonNames[GBJoypadButtonCount];
extern NSString const *GBButtonNames[GBTotalButtonCount];
static inline NSString *n2s(uint64_t number)
{

View File

@ -1,4 +1,4 @@
#import <Foundation/Foundation.h>
#import "GBButtons.h"
NSString const *GBButtonNames[] = {@"Right", @"Left", @"Up", @"Down", @"A", @"B", @"Select", @"Start", @"Turbo", @"Rewind", @"Slow-Motion", @"Hotkey 1", @"Hotkey 2"};
NSString const *GBButtonNames[] = {@"Right", @"Left", @"Up", @"Down", @"A", @"B", @"Select", @"Start", @"Rapid A", @"Rapid B", @"Turbo", @"Rewind", @"Slow-Motion", @"Hotkey 1", @"Hotkey 2"};

View File

@ -40,16 +40,16 @@ static inline NSString *keyEquivalentString(NSMenuItem *item)
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
if (self.playerListButton.selectedTag == 0) {
return GBButtonCount;
return GBKeyboardButtonCount;
}
return GBGameBoyButtonCount;
return GBPerPlayerButtonCount;
}
- (unsigned) usesForKey:(unsigned) key
{
unsigned ret = 0;
for (unsigned player = 4; player--;) {
for (unsigned button = player == 0? GBButtonCount:GBGameBoyButtonCount; button--;) {
for (unsigned button = player == 0? GBKeyboardButtonCount:GBPerPlayerButtonCount; button--;) {
NSNumber *other = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(button, player)];
if (other && [other unsignedIntValue] == key) {
ret++;
@ -205,7 +205,7 @@ static inline NSString *keyEquivalentString(NSMenuItem *item)
if (joystick_configuration_state == GBUnderclock) {
[self.configureJoypadButton setTitle:@"Press Button for Slo-Mo"]; // Full name is too long :<
}
else if (joystick_configuration_state < GBJoypadButtonCount) {
else if (joystick_configuration_state < GBTotalButtonCount) {
[self.configureJoypadButton setTitle:[NSString stringWithFormat:@"Press Button for %@", GBButtonNames[joystick_configuration_state]]];
}
else {
@ -227,7 +227,7 @@ static inline NSString *keyEquivalentString(NSMenuItem *item)
if (!button.isPressed) return;
if (joystick_configuration_state == -1) return;
if (joystick_configuration_state == GBJoypadButtonCount) return;
if (joystick_configuration_state == GBTotalButtonCount) return;
if (!joystick_being_configured) {
joystick_being_configured = controller.uniqueID;
}
@ -266,6 +266,8 @@ static inline NSString *keyEquivalentString(NSMenuItem *item)
[GBB] = JOYButtonUsageB,
[GBSelect] = JOYButtonUsageSelect,
[GBStart] = JOYButtonUsageStart,
[GBRapidA] = GBJoyKitRapidA,
[GBRapidB] = GBJoyKitRapidB,
[GBTurbo] = JOYButtonUsageL1,
[GBRewind] = JOYButtonUsageL2,
[GBUnderclock] = JOYButtonUsageR1,

View File

@ -119,6 +119,9 @@ static const uint8_t workboy_vk_to_key[] = {
bool _mouseControlEnabled;
NSMutableDictionary<NSNumber *, JOYController *> *_controllerMapping;
unsigned _lastPlayerCount;
bool _rapidA[4], _rapidB[4];
uint8_t _rapidACount[4], _rapidBCount[4];
}
+ (instancetype)alloc
@ -343,6 +346,17 @@ static const uint8_t workboy_vk_to_key[] = {
(analogClockMultiplierValid && analogClockMultiplier < 1)) {
[self.osdView displayText:@"Slow motion…"];
}
for (unsigned i = GB_get_player_count(_gb); i--;) {
if (_rapidA[i]) {
_rapidACount[i]++;
GB_set_key_state_for_player(_gb, GB_KEY_A, i, !(_rapidACount[i] & 2));
}
if (_rapidB[i]) {
_rapidBCount[i]++;
GB_set_key_state_for_player(_gb, GB_KEY_B, i, !(_rapidBCount[i] & 2));
}
}
[super flip];
}
@ -370,7 +384,7 @@ static const uint8_t workboy_vk_to_key[] = {
player_count = 2;
}
for (unsigned player = 0; player < player_count; player++) {
for (GBButton button = 0; button < GBButtonCount; button++) {
for (GBButton button = 0; button < GBKeyboardButtonCount; button++) {
NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
if (!key) continue;
@ -401,6 +415,18 @@ static const uint8_t workboy_vk_to_key[] = {
analogClockMultiplierValid = false;
break;
case GBRapidA:
_rapidA[player] = true;
_rapidACount[player] = 0;
GB_set_key_state_for_player(_gb, GB_KEY_A, player, true);
break;
case GBRapidB:
_rapidB[player] = true;
_rapidBCount[player] = 0;
GB_set_key_state_for_player(_gb, GB_KEY_B, player, true);
break;
default:
if (self.document.partner) {
if (player == 0) {
@ -444,7 +470,7 @@ static const uint8_t workboy_vk_to_key[] = {
player_count = 2;
}
for (unsigned player = 0; player < player_count; player++) {
for (GBButton button = 0; button < GBButtonCount; button++) {
for (GBButton button = 0; button < GBKeyboardButtonCount; button++) {
NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
if (!key) continue;
@ -470,6 +496,16 @@ static const uint8_t workboy_vk_to_key[] = {
underclockKeyDown = false;
analogClockMultiplierValid = false;
break;
case GBRapidA:
_rapidA[player] = false;
GB_set_key_state_for_player(_gb, GB_KEY_A, player, false);
break;
case GBRapidB:
_rapidB[player] = false;
GB_set_key_state_for_player(_gb, GB_KEY_B, player, false);
break;
default:
if (self.document.partner) {
@ -651,7 +687,7 @@ static const uint8_t workboy_vk_to_key[] = {
}
}
switch (usage) {
switch ((unsigned)usage) {
case JOYButtonUsageNone: break;
case JOYButtonUsageA: GB_set_key_state_for_player(effectiveGB, GB_KEY_A, effectivePlayer, button.isPressed); break;
@ -696,7 +732,16 @@ static const uint8_t workboy_vk_to_key[] = {
case JOYButtonUsageDPadUp: GB_set_key_state_for_player(effectiveGB, GB_KEY_UP, effectivePlayer, button.isPressed); break;
case JOYButtonUsageDPadDown: GB_set_key_state_for_player(effectiveGB, GB_KEY_DOWN, effectivePlayer, button.isPressed); break;
default:
case GBJoyKitRapidA:
_rapidA[effectivePlayer] = button.isPressed;
_rapidACount[effectivePlayer] = 0;
GB_set_key_state_for_player(_gb, GB_KEY_A, effectivePlayer, button.isPressed);
break;
case GBJoyKitRapidB:
_rapidB[effectivePlayer] = button.isPressed;
_rapidBCount[effectivePlayer] = 0;
GB_set_key_state_for_player(_gb, GB_KEY_B, effectivePlayer, button.isPressed);
break;
}
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -13,7 +13,7 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="oUc-bq-d5t">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsCharacterPickerTouchBarItem="YES" id="oUc-bq-d5t">
<rect key="frame" x="0.0" y="0.0" width="66" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<textFieldCell key="cell" controlSize="mini" sendsActionOnEndEditing="YES" alignment="left" title="Test" id="xyx-iy-kse">

View File

@ -986,15 +986,15 @@
</connections>
</button>
<scrollView focusRingType="none" fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="PBp-dj-EIa">
<rect key="frame" x="32" y="124" width="262" height="211"/>
<rect key="frame" x="32" y="87" width="262" height="249"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
<rect key="frame" x="1" y="1" width="260" height="209"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" focusRingType="none" drawsBackground="NO" id="AMs-PO-nid">
<rect key="frame" x="1" y="1" width="260" height="247"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="UDd-IJ-fxX">
<rect key="frame" x="0.0" y="0.0" width="260" height="209"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="frame" x="0.0" y="0.0" width="260" height="247"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
@ -1044,7 +1044,7 @@
</scroller>
</scrollView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fcF-wc-KwM">
<rect key="frame" x="30" y="99" width="231" height="17"/>
<rect key="frame" x="30" y="62" width="231" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Controller for multiplayer games:" id="AJA-9b-VKI">
<font key="font" metaFont="system"/>
@ -1053,7 +1053,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Az-0R-oNw">
<rect key="frame" x="42" y="66" width="255" height="26"/>
<rect key="frame" x="42" y="29" width="255" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingMiddle" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="hy8-cr-RrE" id="uEC-vN-8Jq">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>

View File

@ -11,6 +11,27 @@ enum scaling_mode {
GB_SDL_SCALING_MAX,
};
enum {
GB_CONF_KEYS_RIGHT = GB_KEY_RIGHT,
GB_CONF_KEYS_LEFT = GB_KEY_LEFT,
GB_CONF_KEYS_UP = GB_KEY_UP,
GB_CONF_KEYS_DOWN = GB_KEY_DOWN,
GB_CONF_KEYS_A = GB_KEY_A,
GB_CONF_KEYS_B = GB_KEY_B,
GB_CONF_KEYS_SELECT = GB_KEY_SELECT,
GB_CONF_KEYS_START = GB_KEY_START,
GB_CONF_KEYS_TURBO,
GB_CONF_KEYS_COUNT,
};
enum {
GB_CONF_KEYS2_REWIND,
GB_CONF_KEYS2_UNDERCLOCK,
GB_CONF_KEYS2_RAPID_A,
GB_CONF_KEYS2_RAPID_B,
GB_CONF_KEYS2_COUNT = 32,
};
typedef enum {
JOYPAD_BUTTON_RIGHT,
JOYPAD_BUTTON_LEFT,
@ -26,6 +47,8 @@ typedef enum {
JOYPAD_BUTTON_SLOW_MOTION,
JOYPAD_BUTTON_HOTKEY_1,
JOYPAD_BUTTON_HOTKEY_2,
JOYPAD_BUTTON_RAPID_A,
JOYPAD_BUTTON_RAPID_B,
JOYPAD_BUTTONS_MAX
} joypad_button_t;
@ -65,7 +88,7 @@ typedef enum {
} hotkey_action_t;
typedef struct {
SDL_Scancode keys[9];
SDL_Scancode keys[GB_CONF_KEYS_COUNT];
GB_color_correction_mode_t color_correction_mode;
enum scaling_mode scaling_mode;
uint8_t blending_mode;
@ -89,7 +112,7 @@ typedef struct {
/* v0.11 */
uint32_t rewind_length;
SDL_Scancode keys_2[32]; /* Rewind and underclock, + padding for the future */
SDL_Scancode keys_2[GB_CONF_KEYS2_COUNT]; /* Rewind and underclock, + padding for the future */
uint8_t joypad_configuration[32]; /* 14 Keys + padding for the future*/;
uint8_t joypad_axises[JOYPAD_AXISES_MAX];

View File

@ -1764,16 +1764,17 @@ static const struct menu_item keyboard_menu[] = {
{"Turbo:", modify_key, key_name,},
{"Rewind:", modify_key, key_name,},
{"Slow-Motion:", modify_key, key_name,},
{"Rapid A:", modify_key, key_name,},
{"Rapid B:", modify_key, key_name,},
{"Back", enter_controls_menu},
{NULL,}
};
static const char *key_name(unsigned index)
{
if (index > 8) {
return SDL_GetScancodeName(configuration.keys_2[index - 9]);
}
return SDL_GetScancodeName(configuration.keys[index]);
SDL_Scancode code = index >= GB_CONF_KEYS_COUNT? configuration.keys_2[index - GB_CONF_KEYS_COUNT] : configuration.keys[index];
if (!code) return "Not Set";
return SDL_GetScancodeName(code);
}
static void enter_keyboard_menu(unsigned index)
@ -2532,7 +2533,7 @@ void run_gui(bool is_running)
}
else if (gui_state == WAITING_FOR_KEY) {
if (current_selection > 8) {
configuration.keys_2[current_selection - 9] = event.key.keysym.scancode;
configuration.keys_2[current_selection - GB_CONF_KEYS_COUNT] = event.key.keysym.scancode;
}
else {
configuration.keys[current_selection] = event.key.keysym.scancode;
@ -2773,6 +2774,8 @@ void run_gui(bool is_running)
"Slow-Motion",
"Hotkey 1",
"Hotkey 2",
"Rapid A",
"Rapid B",
"",
}) [joypad_configuration_progress],
gui_palette_native[3], gui_palette_native[0], STYLE_CENTER);

View File

@ -26,6 +26,8 @@ static bool paused = false;
static uint32_t pixel_buffer_1[256 * 224], pixel_buffer_2[256 * 224];
static uint32_t *active_pixel_buffer = pixel_buffer_1, *previous_pixel_buffer = pixel_buffer_2;
static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false;
static bool rapid_a = false, rapid_b = false;
static uint8_t rapid_a_count = 0, rapid_b_count = 0;
static double clock_mutliplier = 1.0;
char *filename = NULL;
@ -322,8 +324,18 @@ static void handle_events(GB_gameboy_t *gb)
break;
}
}
else if (button == JOYPAD_BUTTON_RAPID_A) {
rapid_a = event.type == SDL_JOYBUTTONDOWN;
rapid_a_count = 0;
GB_set_key_state(gb, GB_KEY_A, event.type == SDL_JOYBUTTONDOWN);
}
else if (button == JOYPAD_BUTTON_RAPID_B) {
rapid_b = event.type == SDL_JOYBUTTONDOWN;
rapid_b_count = 0;
GB_set_key_state(gb, GB_KEY_B, event.type == SDL_JOYBUTTONDOWN);
}
}
break;
break;
case SDL_JOYAXISMOTION: {
static bool axis_active[2] = {false, false};
@ -475,21 +487,31 @@ static void handle_events(GB_gameboy_t *gb)
break;
}
case SDL_KEYUP: // Fallthrough
if (event.key.keysym.scancode == configuration.keys[8]) {
if (event.key.keysym.scancode == configuration.keys[GB_CONF_KEYS_TURBO]) {
turbo_down = event.type == SDL_KEYDOWN;
GB_audio_clear_queue();
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down);
}
else if (event.key.keysym.scancode == configuration.keys_2[0]) {
else if (event.key.keysym.scancode == configuration.keys_2[GB_CONF_KEYS2_REWIND]) {
rewind_down = event.type == SDL_KEYDOWN;
if (event.type == SDL_KEYUP) {
rewind_paused = false;
}
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down);
}
else if (event.key.keysym.scancode == configuration.keys_2[1]) {
else if (event.key.keysym.scancode == configuration.keys_2[GB_CONF_KEYS2_UNDERCLOCK]) {
underclock_down = event.type == SDL_KEYDOWN;
}
else if (event.key.keysym.scancode == configuration.keys_2[GB_CONF_KEYS2_RAPID_A]) {
rapid_a = event.type == SDL_KEYDOWN;
rapid_a_count = 0;
GB_set_key_state(gb, GB_KEY_A, event.type == SDL_KEYDOWN);
}
else if (event.key.keysym.scancode == configuration.keys_2[GB_CONF_KEYS2_RAPID_B]) {
rapid_b = event.type == SDL_KEYDOWN;
rapid_b_count = 0;
GB_set_key_state(gb, GB_KEY_B, event.type == SDL_KEYDOWN);
}
else {
for (unsigned i = 0; i < GB_KEY_MAX; i++) {
if (event.key.keysym.scancode == configuration.keys[i]) {
@ -520,6 +542,15 @@ static void vblank(GB_gameboy_t *gb, GB_vblank_type_t type)
GB_set_clock_multiplier(gb, clock_mutliplier);
}
if (rapid_a) {
rapid_a_count++;
GB_set_key_state(gb, GB_KEY_A, !(rapid_a_count & 2));
}
if (rapid_b) {
rapid_b_count++;
GB_set_key_state(gb, GB_KEY_B, !(rapid_b_count & 2));
}
if (turbo_down) {
show_osd_text("Fast forward...");
}

View File

@ -17,9 +17,9 @@ typedef enum {
GBUnderclock,
// GBHotkey1, // Todo
// GBHotkey2, // Todo
GBJoypadButtonCount,
GBButtonCount = GBUnderclock + 1,
GBGameBoyButtonCount = GBStart + 1,
GBTotalButtonCount,
GBKeyboardButtonCount = GBUnderclock + 1,
GBPerPlayerButtonCount = GBStart + 1,
GBUnusedButton = 0xFF,
} GBButton;