mirror of https://github.com/snes9xgit/snes9x.git
Merge pull request #741 from jraby/gtk-joystick-hotplug
gtk: Support adding/removing joysticks at runtime
This commit is contained in:
commit
17e75b1469
|
@ -207,24 +207,6 @@ int Snes9xConfig::load_defaults()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Snes9xConfig::joystick_register_centers()
|
|
||||||
{
|
|
||||||
for (auto &j : joystick)
|
|
||||||
j.register_centers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Snes9xConfig::flush_joysticks()
|
|
||||||
{
|
|
||||||
for (auto &j : joystick)
|
|
||||||
j.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Snes9xConfig::set_joystick_mode(int mode)
|
|
||||||
{
|
|
||||||
for (auto &j : joystick)
|
|
||||||
j.mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Snes9xConfig::save_config_file()
|
int Snes9xConfig::save_config_file()
|
||||||
{
|
{
|
||||||
ConfigFile cf;
|
ConfigFile cf;
|
||||||
|
|
|
@ -58,9 +58,6 @@ class Snes9xConfig
|
||||||
int save_config_file();
|
int save_config_file();
|
||||||
int load_defaults();
|
int load_defaults();
|
||||||
void rebind_keys();
|
void rebind_keys();
|
||||||
void flush_joysticks();
|
|
||||||
void set_joystick_mode(int mode);
|
|
||||||
void joystick_register_centers();
|
|
||||||
|
|
||||||
/* Screen options */
|
/* Screen options */
|
||||||
bool full_screen_on_open;
|
bool full_screen_on_open;
|
||||||
|
@ -168,7 +165,8 @@ class Snes9xConfig
|
||||||
bool use_sync_control;
|
bool use_sync_control;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::vector<JoyDevice> joystick;
|
|
||||||
|
JoyDevices joysticks;
|
||||||
int joystick_threshold;
|
int joystick_threshold;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -426,6 +426,7 @@ s9xcommand_t S9xGetPortCommandT(const char *name)
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void S9xProcessEvents(bool8 block)
|
void S9xProcessEvents(bool8 block)
|
||||||
{
|
{
|
||||||
JoyEvent event;
|
JoyEvent event;
|
||||||
|
@ -433,11 +434,12 @@ void S9xProcessEvents(bool8 block)
|
||||||
|
|
||||||
if (S9xGrabJoysticks())
|
if (S9xGrabJoysticks())
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < gui_config->joystick.size(); i++)
|
gui_config->joysticks.poll_events();
|
||||||
|
for (auto &j : gui_config->joysticks)
|
||||||
{
|
{
|
||||||
while (gui_config->joystick[i].get_event(&event))
|
while (j.second->get_event(&event))
|
||||||
{
|
{
|
||||||
binding = Binding(i, event.parameter, 0);
|
binding = Binding(j.second->joynum, event.parameter, 0);
|
||||||
S9xReportButton(binding.hex(), event.state == JOY_PRESSED ? 1 : 0);
|
S9xReportButton(binding.hex(), event.state == JOY_PRESSED ? 1 : 0);
|
||||||
gui_config->screensaver_needs_reset = true;
|
gui_config->screensaver_needs_reset = true;
|
||||||
}
|
}
|
||||||
|
@ -447,37 +449,15 @@ void S9xProcessEvents(bool8 block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void poll_joystick_events()
|
|
||||||
{
|
|
||||||
SDL_Event event;
|
|
||||||
|
|
||||||
while (SDL_PollEvent(&event))
|
|
||||||
{
|
|
||||||
if (event.type == SDL_JOYAXISMOTION)
|
|
||||||
{
|
|
||||||
gui_config->joystick[event.jaxis.which].handle_event(&event);
|
|
||||||
}
|
|
||||||
else if (event.type == SDL_JOYHATMOTION)
|
|
||||||
{
|
|
||||||
gui_config->joystick[event.jhat.which].handle_event(&event);
|
|
||||||
}
|
|
||||||
else if (event.type == SDL_JOYBUTTONUP ||
|
|
||||||
event.type == SDL_JOYBUTTONDOWN)
|
|
||||||
{
|
|
||||||
gui_config->joystick[event.jbutton.which].handle_event(&event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void S9xInitInputDevices()
|
void S9xInitInputDevices()
|
||||||
{
|
{
|
||||||
SDL_Init(SDL_INIT_JOYSTICK);
|
SDL_Init(SDL_INIT_JOYSTICK);
|
||||||
size_t num_joysticks = SDL_NumJoysticks();
|
size_t num_joysticks = SDL_NumJoysticks();
|
||||||
gui_config->joystick.resize(num_joysticks);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_joysticks; i++)
|
for (size_t i = 0; i < num_joysticks; i++)
|
||||||
{
|
{
|
||||||
gui_config->joystick[i].set_sdl_joystick_num(i);
|
gui_config->joysticks.add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
//First plug in both, they'll change later as needed
|
//First plug in both, they'll change later as needed
|
||||||
|
@ -487,7 +467,7 @@ void S9xInitInputDevices()
|
||||||
|
|
||||||
void S9xDeinitInputDevices()
|
void S9xDeinitInputDevices()
|
||||||
{
|
{
|
||||||
gui_config->joystick.clear();
|
gui_config->joysticks.clear();
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,26 +480,28 @@ JoyDevice::JoyDevice()
|
||||||
|
|
||||||
JoyDevice::~JoyDevice()
|
JoyDevice::~JoyDevice()
|
||||||
{
|
{
|
||||||
if (enabled)
|
if (filedes)
|
||||||
{
|
{
|
||||||
SDL_JoystickClose(filedes);
|
SDL_JoystickClose(filedes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JoyDevice::set_sdl_joystick_num(unsigned int device_num)
|
bool JoyDevice::set_sdl_joystick(unsigned int sdl_device_index, int new_joynum)
|
||||||
{
|
{
|
||||||
if ((int)device_num >= SDL_NumJoysticks())
|
if ((int)sdl_device_index >= SDL_NumJoysticks())
|
||||||
{
|
{
|
||||||
enabled = false;
|
enabled = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
filedes = SDL_JoystickOpen(device_num);
|
filedes = SDL_JoystickOpen(sdl_device_index);
|
||||||
|
|
||||||
if (!filedes)
|
if (!filedes)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
enabled = true;
|
enabled = true;
|
||||||
|
instance_id = SDL_JoystickInstanceID(filedes);
|
||||||
|
joynum = new_joynum;
|
||||||
|
|
||||||
int num_axes = SDL_JoystickNumAxes(filedes);
|
int num_axes = SDL_JoystickNumAxes(filedes);
|
||||||
int num_hats = SDL_JoystickNumHats(filedes);
|
int num_hats = SDL_JoystickNumHats(filedes);
|
||||||
|
@ -534,12 +516,14 @@ bool JoyDevice::set_sdl_joystick_num(unsigned int device_num)
|
||||||
calibration[i].center = 0;
|
calibration[i].center = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Joystick %d, %s:\n %d axes, %d buttons, %d hats\n",
|
description = SDL_JoystickName(filedes);
|
||||||
device_num + 1,
|
description += ": ";
|
||||||
SDL_JoystickName(filedes),
|
description += std::to_string(SDL_JoystickNumButtons(filedes));
|
||||||
SDL_JoystickNumButtons(filedes),
|
description += " buttons, ";
|
||||||
num_axes,
|
description += std::to_string(num_axes);
|
||||||
num_hats);
|
description += " axes, ";
|
||||||
|
description += std::to_string(num_hats);
|
||||||
|
description += " hats\n";
|
||||||
|
|
||||||
for (auto &i : axis)
|
for (auto &i : axis)
|
||||||
i = 0;
|
i = 0;
|
||||||
|
@ -732,8 +716,6 @@ void JoyDevice::handle_event(SDL_Event *event)
|
||||||
|
|
||||||
int JoyDevice::get_event(JoyEvent *event)
|
int JoyDevice::get_event(JoyEvent *event)
|
||||||
{
|
{
|
||||||
poll_events();
|
|
||||||
|
|
||||||
if (queue.empty())
|
if (queue.empty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -745,11 +727,6 @@ int JoyDevice::get_event(JoyEvent *event)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoyDevice::poll_events()
|
|
||||||
{
|
|
||||||
poll_joystick_events();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JoyDevice::flush()
|
void JoyDevice::flush()
|
||||||
{
|
{
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
@ -761,3 +738,106 @@ void JoyDevice::flush()
|
||||||
while (!queue.empty())
|
while (!queue.empty())
|
||||||
queue.pop();
|
queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JoyDevices::clear()
|
||||||
|
{
|
||||||
|
joysticks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JoyDevices::add(int sdl_device_index)
|
||||||
|
{
|
||||||
|
std::array<bool, NUM_JOYPADS> joynums;
|
||||||
|
joynums.fill(false);
|
||||||
|
for (auto &j : joysticks)
|
||||||
|
{
|
||||||
|
joynums[j.second->joynum] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// New joystick always gets the lowest available joynum
|
||||||
|
int joynum(0);
|
||||||
|
for (; joynum < NUM_JOYPADS && joynums[joynum]; ++joynum);
|
||||||
|
|
||||||
|
if (joynum == NUM_JOYPADS)
|
||||||
|
{
|
||||||
|
printf("Joystick slots are full, cannot add joystick (device index %d)\n", sdl_device_index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ujd = std::make_unique<JoyDevice>();
|
||||||
|
ujd->set_sdl_joystick(sdl_device_index, joynum);
|
||||||
|
printf("Joystick %d, %s", ujd->joynum+1, ujd->description.c_str());
|
||||||
|
joysticks[ujd->instance_id] = std::move(ujd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JoyDevices::remove(SDL_JoystickID instance_id)
|
||||||
|
{
|
||||||
|
if (!joysticks.count(instance_id))
|
||||||
|
{
|
||||||
|
printf("joystick_remove: invalid instance id %d", instance_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printf("Removed joystick %d, %s", joysticks[instance_id]->joynum+1, joysticks[instance_id]->description.c_str());
|
||||||
|
joysticks.erase(instance_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyDevice *JoyDevices::get_joystick(SDL_JoystickID instance_id)
|
||||||
|
{
|
||||||
|
if (joysticks.count(instance_id)){
|
||||||
|
return joysticks[instance_id].get();
|
||||||
|
}
|
||||||
|
printf("BUG: Event for unknown joystick instance id: %d", instance_id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoyDevices::register_centers()
|
||||||
|
{
|
||||||
|
for (auto &j : joysticks)
|
||||||
|
j.second->register_centers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoyDevices::flush_events()
|
||||||
|
{
|
||||||
|
for (auto &j : joysticks)
|
||||||
|
j.second->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoyDevices::set_mode(int mode)
|
||||||
|
{
|
||||||
|
for (auto &j : joysticks)
|
||||||
|
j.second->mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoyDevices::poll_events()
|
||||||
|
{
|
||||||
|
SDL_Event event;
|
||||||
|
JoyDevice *jd;
|
||||||
|
|
||||||
|
while (SDL_PollEvent(&event))
|
||||||
|
{
|
||||||
|
switch(event.type) {
|
||||||
|
case SDL_JOYAXISMOTION:
|
||||||
|
jd = get_joystick(event.jaxis.which);
|
||||||
|
break;
|
||||||
|
case SDL_JOYHATMOTION:
|
||||||
|
jd = get_joystick(event.jhat.which);
|
||||||
|
break;
|
||||||
|
case SDL_JOYBUTTONUP:
|
||||||
|
case SDL_JOYBUTTONDOWN:
|
||||||
|
jd = get_joystick(event.jbutton.which);
|
||||||
|
break;
|
||||||
|
case SDL_JOYDEVICEADDED:
|
||||||
|
add(event.jdevice.which);
|
||||||
|
continue;
|
||||||
|
case SDL_JOYDEVICEREMOVED:
|
||||||
|
remove(event.jdevice.which);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (jd)
|
||||||
|
{
|
||||||
|
jd->handle_event(&event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,9 +100,12 @@ class JoyDevice
|
||||||
void flush();
|
void flush();
|
||||||
void handle_event(SDL_Event *event);
|
void handle_event(SDL_Event *event);
|
||||||
void register_centers();
|
void register_centers();
|
||||||
bool set_sdl_joystick_num(unsigned int device_num);
|
bool set_sdl_joystick(unsigned int device_index, int slot);
|
||||||
|
static void poll_joystick_events();
|
||||||
|
|
||||||
|
std::string description;
|
||||||
SDL_Joystick *filedes;
|
SDL_Joystick *filedes;
|
||||||
|
SDL_JoystickID instance_id;
|
||||||
std::queue<JoyEvent> queue;
|
std::queue<JoyEvent> queue;
|
||||||
int mode;
|
int mode;
|
||||||
int joynum;
|
int joynum;
|
||||||
|
@ -112,10 +115,28 @@ class JoyDevice
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void poll_events();
|
|
||||||
void add_event(unsigned int parameter, unsigned int state);
|
void add_event(unsigned int parameter, unsigned int state);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class JoyDevices
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void clear();
|
||||||
|
bool add(int sdl_device_index);
|
||||||
|
bool remove(SDL_JoystickID instance_id);
|
||||||
|
void register_centers();
|
||||||
|
void flush_events();
|
||||||
|
void set_mode(int mode);
|
||||||
|
|
||||||
|
void poll_events();
|
||||||
|
std::map<SDL_JoystickID, std::unique_ptr<JoyDevice>>::const_iterator begin() const { return joysticks.begin(); }
|
||||||
|
std::map<SDL_JoystickID, std::unique_ptr<JoyDevice>>::const_iterator end() const { return joysticks.end(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
JoyDevice *get_joystick(SDL_JoystickID instance_id);
|
||||||
|
std::map<SDL_JoystickID, std::unique_ptr<JoyDevice>> joysticks;
|
||||||
|
};
|
||||||
|
|
||||||
void S9xDeinitInputDevices();
|
void S9xDeinitInputDevices();
|
||||||
Binding S9xGetBindingByName(const char *name);
|
Binding S9xGetBindingByName(const char *name);
|
||||||
bool S9xIsMousePluggedIn();
|
bool S9xIsMousePluggedIn();
|
||||||
|
|
|
@ -33,11 +33,11 @@ void snes9x_preferences_open(Snes9xWindow *window)
|
||||||
|
|
||||||
preferences->window->set_transient_for(*window->window.get());
|
preferences->window->set_transient_for(*window->window.get());
|
||||||
|
|
||||||
config->set_joystick_mode(JOY_MODE_GLOBAL);
|
config->joysticks.set_mode(JOY_MODE_GLOBAL);
|
||||||
preferences->show();
|
preferences->show();
|
||||||
window->unpause_from_focus_change();
|
window->unpause_from_focus_change();
|
||||||
|
|
||||||
config->set_joystick_mode(JOY_MODE_INDIVIDUAL);
|
config->joysticks.set_mode(JOY_MODE_INDIVIDUAL);
|
||||||
|
|
||||||
config->rebind_keys();
|
config->rebind_keys();
|
||||||
window->update_accelerators();
|
window->update_accelerators();
|
||||||
|
@ -50,22 +50,23 @@ gboolean poll_joystick(gpointer data)
|
||||||
Binding binding;
|
Binding binding;
|
||||||
int focus;
|
int focus;
|
||||||
|
|
||||||
for (size_t i = 0; i < window->config->joystick.size(); i++)
|
window->config->joysticks.poll_events();
|
||||||
|
for (auto &j : window->config->joysticks)
|
||||||
{
|
{
|
||||||
while (window->config->joystick[i].get_event(&event))
|
while (j.second->get_event(&event))
|
||||||
{
|
{
|
||||||
if (event.state == JOY_PRESSED)
|
if (event.state == JOY_PRESSED)
|
||||||
{
|
{
|
||||||
if ((focus = window->get_focused_binding()) >= 0)
|
if ((focus = window->get_focused_binding()) >= 0)
|
||||||
{
|
{
|
||||||
binding = Binding(i,
|
binding = Binding(j.second->joynum,
|
||||||
event.parameter,
|
event.parameter,
|
||||||
window->config->joystick_threshold);
|
window->config->joystick_threshold);
|
||||||
|
|
||||||
window->store_binding(b_links[focus].button_name,
|
window->store_binding(b_links[focus].button_name,
|
||||||
binding);
|
binding);
|
||||||
|
|
||||||
window->config->flush_joysticks();
|
window->config->joysticks.flush_events();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -948,7 +949,7 @@ void Snes9xPreferences::bindings_to_dialog(int joypad)
|
||||||
|
|
||||||
void Snes9xPreferences::calibration_dialog()
|
void Snes9xPreferences::calibration_dialog()
|
||||||
{
|
{
|
||||||
config->joystick_register_centers();
|
config->joysticks.register_centers();
|
||||||
auto dialog = Gtk::MessageDialog(_("Current joystick centers have been saved."));
|
auto dialog = Gtk::MessageDialog(_("Current joystick centers have been saved."));
|
||||||
dialog.set_title(_("Calibration Complete"));
|
dialog.set_title(_("Calibration Complete"));
|
||||||
dialog.run();
|
dialog.run();
|
||||||
|
|
|
@ -149,7 +149,7 @@ int main(int argc, char *argv[])
|
||||||
if (gui_config->fullscreen)
|
if (gui_config->fullscreen)
|
||||||
top_level->enter_fullscreen_mode();
|
top_level->enter_fullscreen_mode();
|
||||||
|
|
||||||
gui_config->flush_joysticks();
|
gui_config->joysticks.flush_events();
|
||||||
|
|
||||||
if (rom_filename && *Settings.InitialSnapshotFilename)
|
if (rom_filename && *Settings.InitialSnapshotFilename)
|
||||||
S9xUnfreezeGame(Settings.InitialSnapshotFilename);
|
S9xUnfreezeGame(Settings.InitialSnapshotFilename);
|
||||||
|
@ -276,7 +276,7 @@ static bool S9xPauseFunc()
|
||||||
if (!Settings.Paused) /* Coming out of pause */
|
if (!Settings.Paused) /* Coming out of pause */
|
||||||
{
|
{
|
||||||
/* Clear joystick queues */
|
/* Clear joystick queues */
|
||||||
gui_config->flush_joysticks();
|
gui_config->joysticks.flush_events();
|
||||||
|
|
||||||
S9xSoundStart();
|
S9xSoundStart();
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ static bool S9xIdleFunc()
|
||||||
{
|
{
|
||||||
S9xSoundStop();
|
S9xSoundStop();
|
||||||
|
|
||||||
gui_config->flush_joysticks();
|
gui_config->joysticks.flush_events();
|
||||||
|
|
||||||
if (Settings.NetPlay && NetPlay.Connected)
|
if (Settings.NetPlay && NetPlay.Connected)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue