From b2397a2e7a25e1f451a389671889c771d4c3b570 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 14 Jun 2019 18:06:15 +0300 Subject: [PATCH] Joystick hat support in Cocoa --- Cocoa/GBJoystickListener.h | 1 + Cocoa/GBPreferencesWindow.m | 36 ++++++++++++++++++++ Cocoa/GBView.m | 21 ++++++++++++ Cocoa/joypad.m | 68 +++++++++++++++++++++++++++++++++++-- 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/Cocoa/GBJoystickListener.h b/Cocoa/GBJoystickListener.h index 690fde9..069db10 100644 --- a/Cocoa/GBJoystickListener.h +++ b/Cocoa/GBJoystickListener.h @@ -4,5 +4,6 @@ - (void) joystick:(NSString *)joystick_name button: (unsigned)button changedState: (bool) state; - (void) joystick:(NSString *)joystick_name axis: (unsigned)axis movedTo: (signed) value; +- (void) joystick:(NSString *)joystick_name hat: (unsigned)hat changedState: (int8_t) value; @end diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m index 982aa45..ecf0311 100644 --- a/Cocoa/GBPreferencesWindow.m +++ b/Cocoa/GBPreferencesWindow.m @@ -309,6 +309,42 @@ [self advanceConfigurationStateMachine]; } +- (void) joystick:(NSString *)joystick_name hat: (unsigned)hat changedState: (int8_t) state +{ + /* Hats are always mapped to the D-pad, ignore them on non-Dpad keys and skip the D-pad configuration if used*/ + if (!state) return; + if (joystick_configuration_state == -1) return; + if (joystick_configuration_state > GBDown) return; + if (!joystick_being_configured) { + joystick_being_configured = joystick_name; + } + else if (![joystick_being_configured isEqualToString:joystick_name]) { + return; + } + + NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy]; + + if (!all_mappings) { + all_mappings = [[NSMutableDictionary alloc] init]; + } + + NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy]; + + if (!mapping) { + mapping = [[NSMutableDictionary alloc] init]; + } + + for (joystick_configuration_state = 0;; joystick_configuration_state++) { + [mapping removeObjectForKey:GBButtonNames[joystick_configuration_state]]; + if (joystick_configuration_state == GBDown) break; + } + + all_mappings[joystick_name] = mapping; + [[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"]; + [self refreshJoypadMenu:nil]; + [self advanceConfigurationStateMachine]; +} + - (NSButton *)aspectRatioCheckbox { return _aspectRatioCheckbox; diff --git a/Cocoa/GBView.m b/Cocoa/GBView.m index 7e425c5..7f87901 100644 --- a/Cocoa/GBView.m +++ b/Cocoa/GBView.m @@ -341,6 +341,27 @@ } } +- (void) joystick:(NSString *)joystick_name hat: (unsigned)hat changedState: (int8_t) state +{ + unsigned player_count = GB_get_player_count(_gb); + + UpdateSystemActivity(UsrActivity); + for (unsigned player = 0; player < player_count; player++) { + NSString *preferred_joypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBDefaultJoypads"] + objectForKey:[NSString stringWithFormat:@"%u", player]]; + if (player_count != 1 && // Single player, accpet inputs from all joypads + !(player == 0 && !preferred_joypad) && // Multiplayer, but player 1 has no joypad configured, so it takes inputs from all joypads + ![preferred_joypad isEqualToString:joystick_name]) { + continue; + } + assert(state + 1 < 9); + /* - N NE E SE S SW W NW */ + GB_set_key_state_for_player(_gb, GB_KEY_UP, player, (bool []){0, 1, 1, 0, 0, 0, 0, 0, 1}[state + 1]); + GB_set_key_state_for_player(_gb, GB_KEY_RIGHT, player, (bool []){0, 0, 1, 1, 1, 0, 0, 0, 0}[state + 1]); + GB_set_key_state_for_player(_gb, GB_KEY_DOWN, player, (bool []){0, 0, 0, 0, 1, 1, 1, 0, 0}[state + 1]); + GB_set_key_state_for_player(_gb, GB_KEY_LEFT, player, (bool []){0, 0, 0, 0, 0, 0, 1, 1, 1}[state + 1]); + } +} - (BOOL)acceptsFirstResponder { diff --git a/Cocoa/joypad.m b/Cocoa/joypad.m index bca4097..2ffe56b 100755 --- a/Cocoa/joypad.m +++ b/Cocoa/joypad.m @@ -53,6 +53,9 @@ struct _SDL_Joystick int nbuttons; /* Number of buttons on the joystick */ uint8_t *buttons; /* Current button states */ + int nhats; + uint8_t *hats; + struct joystick_hwdata *hwdata; /* Driver dependent information */ int ref_count; /* Reference count for multiple opens */ @@ -93,11 +96,13 @@ struct joystick_hwdata int axes; /* number of axis (calculated, not reported by device) */ int buttons; /* number of buttons (calculated, not reported by device) */ + int hats; int elements; /* number of total elements (should be total of above) (calculated, not reported by device) */ recElement *firstAxis; recElement *firstButton; - + recElement *firstHat; + bool removed; int instance_id; @@ -178,6 +183,30 @@ void SDL_PrivateJoystickButton(SDL_Joystick *joystick, uint8_t button, uint8_t s } } +void SDL_PrivateJoystickHat(SDL_Joystick *joystick, uint8_t hat, uint8_t state) +{ + + /* Make sure we're not getting garbage or duplicate events */ + if (hat >= joystick->nhats) { + return; + } + if (state == joystick->hats[hat]) { + return; + } + + /* Update internal joystick state */ + joystick->hats[hat] = state; + + NSResponder *responder = (typeof(responder)) [[NSApp keyWindow] firstResponder]; + while (responder) { + if ([responder respondsToSelector:@selector(joystick:button:changedState:)]) { + [responder joystick:@(joystick->name) hat:hat changedState:state]; + break; + } + responder = (typeof(responder)) [responder nextResponder]; + } +} + static void FreeElementList(recElement *pElement) { @@ -202,6 +231,7 @@ FreeDevice(recDevice *removeDevice) /* free element lists */ FreeElementList(removeDevice->firstAxis); FreeElementList(removeDevice->firstButton); + FreeElementList(removeDevice->firstHat); free(removeDevice); } @@ -315,6 +345,15 @@ AddHIDElement(const void *value, void *parameter) } } break; + case kHIDUsage_GD_Hatswitch: + if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) { + element = (recElement *) calloc(1, sizeof (recElement)); + if (element) { + pDevice->hats++; + headElement = &(pDevice->firstHat); + } + } + break; case kHIDUsage_GD_DPadUp: case kHIDUsage_GD_DPadDown: @@ -535,6 +574,27 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) element = element->pNext; ++i; } + + element = device->firstHat; + i = 0; + while (element) { + signed range = (element->max - element->min + 1); + value = GetHIDElementState(device, element) - element->min; + if (range == 4) { /* 4 position hatswitch - scale up value */ + value *= 2; + } else if (range != 8) { /* Neither a 4 nor 8 positions - fall back to default position (centered) */ + value = -1; + } + if ((unsigned)value >= 8) { + value = -1; + } + + SDL_PrivateJoystickHat(joystick, i, value); + + element = element->pNext; + ++i; + } + } static void JoystickInputCallback( @@ -579,13 +639,17 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic joystick->naxes = device->axes; joystick->nbuttons = device->buttons; - + joystick->nhats = device->hats; + if (joystick->naxes > 0) { joystick->axes = (SDL_JoystickAxisInfo *) calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo)); } if (joystick->nbuttons > 0) { joystick->buttons = (uint8_t *) calloc(joystick->nbuttons, 1); } + if (joystick->nhats > 0) { + joystick->hats = (uint8_t *) calloc(joystick->nhats, 1); + } /* Get notified when this device is disconnected. */ IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);