-
Notifications
You must be signed in to change notification settings - Fork 556
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Linux/GTK3: add gamepad hotplug support #855
Changes from 2 commits
f439e08
6a65fcd
7e94363
8d112ac
67069ee
a744a3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,8 @@ | |
#include "NDSSystem.h" | ||
#include "frontend/modules/osd/agg/agg_osd.h" | ||
#include "driver.h" | ||
#include <list> | ||
#include <unordered_map> | ||
|
||
#ifdef FAKE_MIC | ||
#include "mic.h" | ||
|
@@ -39,7 +41,8 @@ u16 nbr_joy; | |
mouse_status mouse; | ||
static int fullscreen; | ||
|
||
static SDL_Joystick **open_joysticks = NULL; | ||
//List of currently connected joysticks (key - instance id, value - SDL_Joystick structure, joystick number) | ||
static std::unordered_map<SDL_JoystickID, std::pair<SDL_Joystick *, SDL_JoystickID> > open_joysticks; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems like overkill, currently we support a max of 16 joysticks, which means the joystick id we use fits into one byte... and the average gamer certainly has max 4 joysticks connected so everything would fit into one cache line using a dumb loop over an array. i'm pretty certain the overhead involved in the use of a map for such tiny amount of data is way slower There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
actually, this could be derived from the array index, so it's for free. we only need to make sure to fix the ordering of the array on insertion/removal of a device There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then I probably just use static array of 16 elements? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, sounds like the best option. in the unlikely case that more than 16 joys are connected, we just have to discard those > 15. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redone with static array. Note that there's nbr_joy variable declared as extern in ctrlssdl.h that is now only keeps number of joysticks detected at the start, not the actual number of currently connected joysticks, because joysticks can be removed from any element of the array upon disconnect, so frontends should not rely on that variable (though as far as I can tell, they don't). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added int get_number_of_joysticks() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, please proceed and use it in the interface frontend and remove nbr_joys. (just grep posix subdir for nbr_joy to find the file). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I also make corresponding changes to gtk2 frontend in my branch? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, please There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
/* Keypad key names */ | ||
const char *key_names[NB_KEYS] = | ||
|
@@ -120,23 +123,23 @@ BOOL init_joy( void) { | |
|
||
if ( nbr_joy > 0) { | ||
printf("Found %d joysticks\n", nbr_joy); | ||
open_joysticks = | ||
(SDL_Joystick**)calloc( sizeof ( SDL_Joystick *), nbr_joy); | ||
|
||
if ( open_joysticks != NULL) { | ||
for (i = 0; i < nbr_joy; i++) | ||
{ | ||
SDL_Joystick * joy = SDL_JoystickOpen(i); | ||
printf("Joystick %d %s\n", i, SDL_JoystickNameForIndex(i)); | ||
printf("Axes: %d\n", SDL_JoystickNumAxes(joy)); | ||
printf("Buttons: %d\n", SDL_JoystickNumButtons(joy)); | ||
printf("Trackballs: %d\n", SDL_JoystickNumBalls(joy)); | ||
printf("Hats: %d\n\n", SDL_JoystickNumHats(joy)); | ||
} | ||
} | ||
else { | ||
joy_init_good = FALSE; | ||
|
||
for (i = 0; i < nbr_joy; i++) { | ||
SDL_Joystick * joy = SDL_JoystickOpen(i); | ||
if(joy) { | ||
printf("Joystick %d %s\n", i, SDL_JoystickNameForIndex(i)); | ||
printf("Axes: %d\n", SDL_JoystickNumAxes(joy)); | ||
printf("Buttons: %d\n", SDL_JoystickNumButtons(joy)); | ||
printf("Trackballs: %d\n", SDL_JoystickNumBalls(joy)); | ||
printf("Hats: %d\n\n", SDL_JoystickNumHats(joy)); | ||
open_joysticks.insert(std::make_pair(SDL_JoystickInstanceID(joy), std::make_pair(joy, i))); | ||
} | ||
else { | ||
fprintf(stderr, "Failed to open joystick %d: %s\n", i, SDL_GetError()); | ||
joy_init_good = FALSE; | ||
} | ||
} | ||
nbr_joy = open_joysticks.size(); | ||
} | ||
|
||
return joy_init_good; | ||
|
@@ -145,17 +148,13 @@ BOOL init_joy( void) { | |
/* Unload joysticks */ | ||
void uninit_joy( void) | ||
{ | ||
int i; | ||
|
||
if ( open_joysticks != NULL) { | ||
for (i = 0; i < SDL_NumJoysticks(); i++) { | ||
SDL_JoystickClose( open_joysticks[i]); | ||
} | ||
|
||
free( open_joysticks); | ||
for (auto & it: open_joysticks) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'd really cherish if we could stick to C++03 features for the time being, following the overall style of the existing code and not gratuitously breaking the build for people with older compilers There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. open_joysticks is now static array and is iterated with int index. |
||
if(it.second.first) | ||
SDL_JoystickClose(it.second.first); | ||
} | ||
|
||
open_joysticks = NULL; | ||
|
||
open_joysticks.clear(); | ||
nbr_joy = 0; | ||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK); | ||
} | ||
|
||
|
@@ -203,14 +202,14 @@ u16 get_joy_key(int index) { | |
{ | ||
case SDL_JOYBUTTONDOWN: | ||
printf( "Device: %d; Button: %d\n", event.jbutton.which, event.jbutton.button ); | ||
key = ((event.jbutton.which & 15) << 12) | JOY_BUTTON << 8 | (event.jbutton.button & 255); | ||
key = ((get_joystick_number_by_id(event.jbutton.which) & 15) << 12) | JOY_BUTTON << 8 | (event.jbutton.button & 255); | ||
done = TRUE; | ||
break; | ||
case SDL_JOYAXISMOTION: | ||
/* Dead zone of 50% */ | ||
if( ((u32)abs(event.jaxis.value) >> 14) != 0 ) | ||
{ | ||
key = ((event.jaxis.which & 15) << 12) | JOY_AXIS << 8 | ((event.jaxis.axis & 127) << 1); | ||
key = ((get_joystick_number_by_id(event.jaxis.which) & 15) << 12) | JOY_AXIS << 8 | ((event.jaxis.axis & 127) << 1); | ||
if (event.jaxis.value > 0) { | ||
printf( "Device: %d; Axis: %d (+)\n", event.jaxis.which, event.jaxis.axis ); | ||
key |= 1; | ||
|
@@ -224,7 +223,7 @@ u16 get_joy_key(int index) { | |
/* Diagonal positions will be treated as two separate keys being activated, rather than a single diagonal key. */ | ||
/* JOY_HAT_* are sequential integers, rather than a bitmask */ | ||
if (event.jhat.value != SDL_HAT_CENTERED) { | ||
key = ((event.jhat.which & 15) << 12) | JOY_HAT << 8 | ((event.jhat.hat & 63) << 2); | ||
key = ((get_joystick_number_by_id(event.jhat.which) & 15) << 12) | JOY_HAT << 8 | ((event.jhat.hat & 63) << 2); | ||
/* Can't just use a switch here because SDL_HAT_* make up a bitmask. We only want one of these when assigning keys. */ | ||
if ((event.jhat.value & SDL_HAT_UP) != 0) { | ||
key |= JOY_HAT_UP; | ||
|
@@ -379,7 +378,7 @@ do_process_joystick_events( u16 *keypad, SDL_Event *event) { | |
/* Joystick axis motion | ||
Note: button constants have a 1bit offset. */ | ||
case SDL_JOYAXISMOTION: | ||
key_code = ((event->jaxis.which & 15) << 12) | JOY_AXIS << 8 | ((event->jaxis.axis & 127) << 1); | ||
key_code = ((get_joystick_number_by_id(event->jaxis.which) & 15) << 12) | JOY_AXIS << 8 | ((event->jaxis.axis & 127) << 1); | ||
if( ((u32)abs(event->jaxis.value) >> 14) != 0 ) | ||
{ | ||
if (event->jaxis.value > 0) | ||
|
@@ -406,7 +405,7 @@ do_process_joystick_events( u16 *keypad, SDL_Event *event) { | |
case SDL_JOYHATMOTION: | ||
/* Diagonal positions will be treated as two separate keys being activated, rather than a single diagonal key. */ | ||
/* JOY_HAT_* are sequential integers, rather than a bitmask */ | ||
key_code = ((event->jhat.which & 15) << 12) | JOY_HAT << 8 | ((event->jhat.hat & 63) << 2); | ||
key_code = ((get_joystick_number_by_id(event->jhat.which) & 15) << 12) | JOY_HAT << 8 | ((event->jhat.hat & 63) << 2); | ||
key_u = lookup_joy_key( key_code | JOY_HAT_UP ); | ||
key_r = lookup_joy_key( key_code | JOY_HAT_RIGHT ); | ||
key_d = lookup_joy_key( key_code | JOY_HAT_DOWN ); | ||
|
@@ -432,15 +431,15 @@ do_process_joystick_events( u16 *keypad, SDL_Event *event) { | |
/* Joystick button pressed */ | ||
/* FIXME: Add support for BOOST */ | ||
case SDL_JOYBUTTONDOWN: | ||
key_code = ((event->jbutton.which & 15) << 12) | JOY_BUTTON << 8 | (event->jbutton.button & 255); | ||
key_code = ((get_joystick_number_by_id(event->jbutton.which) & 15) << 12) | JOY_BUTTON << 8 | (event->jbutton.button & 255); | ||
key = lookup_joy_key( key_code ); | ||
if (key != 0) | ||
ADD_KEY( *keypad, key ); | ||
break; | ||
|
||
/* Joystick button released */ | ||
case SDL_JOYBUTTONUP: | ||
key_code = ((event->jbutton.which & 15) << 12) | JOY_BUTTON << 8 | (event->jbutton.button & 255); | ||
key_code = ((get_joystick_number_by_id(event->jbutton.which) & 15) << 12) | JOY_BUTTON << 8 | (event->jbutton.button & 255); | ||
key = lookup_joy_key( key_code ); | ||
if (key != 0) | ||
RM_KEY( *keypad, key ); | ||
|
@@ -454,6 +453,54 @@ do_process_joystick_events( u16 *keypad, SDL_Event *event) { | |
return processed; | ||
} | ||
|
||
/* | ||
* The real joystick connect/disconnect processing function | ||
* Not static because we need it in some frontends during gamepad button configuration | ||
*/ | ||
int | ||
do_process_joystick_device_events(SDL_Event* event) { | ||
int processed = 1; | ||
switch(event->type) { | ||
/* Joystick disconnected */ | ||
case SDL_JOYDEVICEREMOVED: | ||
{ | ||
auto it = open_joysticks.find(event->jdevice.which); | ||
if(it != open_joysticks.cend()) { | ||
printf("Joystick with instance %d disconnected\n", event->jdevice.which); | ||
SDL_JoystickClose(it->second.first); | ||
open_joysticks.erase(it); | ||
} | ||
nbr_joy = open_joysticks.size(); | ||
} | ||
break; | ||
|
||
/* Joystick connected */ | ||
case SDL_JOYDEVICEADDED: | ||
{ | ||
if(get_joystick_number_by_id(SDL_JoystickGetDeviceInstanceID(event->jdevice.which))==-1) { | ||
SDL_Joystick* joy = SDL_JoystickOpen(event->jdevice.which); | ||
if(joy) { | ||
printf("Joystick %d %s\n", event->jdevice.which, SDL_JoystickNameForIndex(event->jdevice.which)); | ||
printf("Axes: %d\n", SDL_JoystickNumAxes(joy)); | ||
printf("Buttons: %d\n", SDL_JoystickNumButtons(joy)); | ||
printf("Trackballs: %d\n", SDL_JoystickNumBalls(joy)); | ||
printf("Hats: %d\n\n", SDL_JoystickNumHats(joy)); | ||
open_joysticks.insert(std::make_pair(SDL_JoystickInstanceID(joy), std::make_pair(joy, event->jdevice.which))); | ||
nbr_joy = open_joysticks.size(); | ||
} | ||
else | ||
fprintf(stderr, "Failed to open joystick %d: %s\n", event->jdevice.which, SDL_GetError()); | ||
} | ||
} | ||
break; | ||
|
||
default: | ||
processed = 0; | ||
break; | ||
} | ||
return processed; | ||
} | ||
|
||
/* | ||
* Process only the joystick events | ||
*/ | ||
|
@@ -468,10 +515,34 @@ process_joystick_events( u16 *keypad) { | |
/* There's an event waiting to be processed? */ | ||
while (SDL_PollEvent(&event)) | ||
{ | ||
do_process_joystick_events( keypad, &event); | ||
if(!do_process_joystick_events( keypad, &event)) | ||
do_process_joystick_device_events(&event); | ||
} | ||
} | ||
|
||
/* | ||
* Process only joystick connect/disconnect events | ||
*/ | ||
void process_joystick_device_events() { | ||
SDL_Event event; | ||
|
||
/* IMPORTANT: Reenable joystick events if needed. */ | ||
if(SDL_JoystickEventState(SDL_QUERY) == SDL_IGNORE) | ||
SDL_JoystickEventState(SDL_ENABLE); | ||
|
||
/* There's an event waiting to be processed? */ | ||
while (SDL_PollEvent(&event)) | ||
do_process_joystick_device_events(&event); | ||
} | ||
|
||
int get_joystick_number_by_id(SDL_JoystickID id) | ||
{ | ||
auto it = open_joysticks.find(id); | ||
if(it != open_joysticks.cend()) | ||
return it->second.second; | ||
return -1; | ||
} | ||
|
||
static u16 shift_pressed; | ||
|
||
void | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd guess that between plugging in a joystick and moving the mouse to the controls menu, at least a second passes, so polling less often would make sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on second thought, why poll at all ? sdl sends an event when a device is plugged in, so we could simply notify the frontend about that, or am i missing something ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We must read the event from the queue as far as I understand, but when emulation is not started, events are not processed in the loop (EmuLoop function). When EmuLoop starts, this timer fucntion exits.