Input refactor.

- Allow key shortcuts to run with loaded game.

For example, when we set `CTRL+A` for `load most recent save state` and
use `A` for some input command, holding `CTRL` and then pressing `A`
will not execute the shortcut. Instead, the key press `A` will be used
only as the input and nothing else.

With this, we use both the input and shortcut key.

- Isolate function to get keyboard key codes.

As explained on [1]:

"Using `GetUnicodeKey()` is in general the right thing to do if you are
interested in the characters typed by the user, `GetKeyCode()` should
be only used for special keys (for which `GetUnicodeKey()` returns
`WXK_NONE`)."

We also allow special keys to be mapped, hence the requirement of using
both functions.

[1] https://docs.wxwidgets.org/3.1/classwx_key_event.html

- Allow use of unicode keys for input and shortcut.

Use format `KeyCode:Modifier` for saving/loading unicode keys.

`WxWidgets=3.{0,1}` does not create an accelerator from strings with
unicode keys such as `ç` (`FromString` function). It fails with an
assertion error and stops execution. At the same time, we use the keys'
strings that are known for WxWidgets, such as `A`, `CTRL+O`,
`PAGEUP` etc.

Use both `EVT_KEY_DOWN` and `EVT_CHAR`.

`EVT_CHAR` is better than `EVT_KEY_DOWN` here because it is where the
raw key events will have been cooked using whatever recipes are in
effect from the os, locale, international keyboard settings, etc.

- Enable SDL joysticks input as key shortcuts.

Start/Stop polling joysticks on Unload/load game.

Our main loop already polls the joystick, we don't need the timer
while a game is running.

- Create function `str_split_with_sep` and use it.

For when we parse strings that may include the sep string, such as
game input and key shortcuts.
This commit is contained in:
Edênis Freindorfer Azevedo 2020-05-07 17:01:19 -03:00
parent 6b257d52f2
commit baa0341bd5
No known key found for this signature in database
GPG Key ID: 968FB6EC280C7222
21 changed files with 508 additions and 249 deletions

View File

@ -4953,7 +4953,6 @@ void gbEmulate(int ticksToStop)
if (turbo_button_pressed) { if (turbo_button_pressed) {
if (!speedup_throttle_set && throttle != speedup_throttle) { if (!speedup_throttle_set && throttle != speedup_throttle) {
last_throttle = throttle; last_throttle = throttle;
//throttle = speedup_throttle;
soundSetThrottle(speedup_throttle); soundSetThrottle(speedup_throttle);
speedup_throttle_set = true; speedup_throttle_set = true;
} }
@ -4970,7 +4969,6 @@ void gbEmulate(int ticksToStop)
framesToSkip = speedup_frame_skip; framesToSkip = speedup_frame_skip;
} }
else if (speedup_throttle_set) { else if (speedup_throttle_set) {
//throttle = last_throttle;
soundSetThrottle(last_throttle); soundSetThrottle(last_throttle);
speedup_throttle_set = false; speedup_throttle_set = false;

View File

@ -3799,7 +3799,6 @@ void CPULoop(int ticks)
if (turbo_button_pressed) { if (turbo_button_pressed) {
if (!speedup_throttle_set && throttle != speedup_throttle) { if (!speedup_throttle_set && throttle != speedup_throttle) {
last_throttle = throttle; last_throttle = throttle;
//throttle = speedup_throttle;
soundSetThrottle(speedup_throttle); soundSetThrottle(speedup_throttle);
speedup_throttle_set = true; speedup_throttle_set = true;
} }
@ -3816,7 +3815,6 @@ void CPULoop(int ticks)
framesToSkip = speedup_frame_skip; framesToSkip = speedup_frame_skip;
} }
else if (speedup_throttle_set) { else if (speedup_throttle_set) {
//throttle = last_throttle;
soundSetThrottle(last_throttle); soundSetThrottle(last_throttle);
speedup_throttle_set = false; speedup_throttle_set = false;

View File

@ -739,6 +739,7 @@ set(
viewsupt.cpp viewsupt.cpp
wayland.cpp wayland.cpp
strutils.cpp strutils.cpp
wxutil.cpp
widgets/keyedit.cpp widgets/keyedit.cpp
widgets/joyedit.cpp widgets/joyedit.cpp
widgets/sdljoy.cpp widgets/sdljoy.cpp
@ -769,6 +770,7 @@ set(
viewsupt.h viewsupt.h
wxhead.h wxhead.h
wayland.h wayland.h
wxutil.h
widgets/wx/keyedit.h widgets/wx/keyedit.h
widgets/wx/joyedit.h widgets/wx/joyedit.h
widgets/wx/sdljoy.h widgets/wx/sdljoy.h

View File

@ -2706,7 +2706,6 @@ EVT_HANDLER(EmulatorDirectories, "Directories...")
EVT_HANDLER(JoypadConfigure, "Joypad options...") EVT_HANDLER(JoypadConfigure, "Joypad options...")
{ {
wxDialog* dlg = GetXRCDialog("JoypadConfig"); wxDialog* dlg = GetXRCDialog("JoypadConfig");
joy.Attach(NULL);
joy.Add(); joy.Add();
if (ShowModal(dlg) == wxID_OK) if (ShowModal(dlg) == wxID_OK)
@ -2718,9 +2717,12 @@ EVT_HANDLER(JoypadConfigure, "Joypad options...")
EVT_HANDLER(Customize, "Customize UI...") EVT_HANDLER(Customize, "Customize UI...")
{ {
wxDialog* dlg = GetXRCDialog("AccelConfig"); wxDialog* dlg = GetXRCDialog("AccelConfig");
joy.Add();
if (ShowModal(dlg) == wxID_OK) if (ShowModal(dlg) == wxID_OK)
update_opts(); update_opts();
SetJoystick();
} }
#ifndef NO_ONLINEUPDATES #ifndef NO_ONLINEUPDATES
@ -3068,6 +3070,9 @@ void SetLinkTypeMenu(const char* type, int value)
mf->SetMenuOption(type, 1); mf->SetMenuOption(type, 1);
gopts.gba_link_type = value; gopts.gba_link_type = value;
update_opts(); update_opts();
#ifndef NO_LINK
CloseLink();
#endif
mf->EnableNetworkMenu(); mf->EnableNetworkMenu();
} }
@ -3097,33 +3102,21 @@ EVT_HANDLER_MASK(LanLink, "Start Network link", CMDEN_LINK_ANY)
EVT_HANDLER(LinkType0Nothing, "Link nothing") EVT_HANDLER(LinkType0Nothing, "Link nothing")
{ {
#ifndef NO_LINK
CloseLink();
#endif
SetLinkTypeMenu("LinkType0Nothing", 0); SetLinkTypeMenu("LinkType0Nothing", 0);
} }
EVT_HANDLER(LinkType1Cable, "Link cable") EVT_HANDLER(LinkType1Cable, "Link cable")
{ {
#ifndef NO_LINK
CloseLink();
#endif
SetLinkTypeMenu("LinkType1Cable", 1); SetLinkTypeMenu("LinkType1Cable", 1);
} }
EVT_HANDLER(LinkType2Wireless, "Link wireless") EVT_HANDLER(LinkType2Wireless, "Link wireless")
{ {
#ifndef NO_LINK
CloseLink();
#endif
SetLinkTypeMenu("LinkType2Wireless", 2); SetLinkTypeMenu("LinkType2Wireless", 2);
} }
EVT_HANDLER(LinkType3GameCube, "Link GameCube") EVT_HANDLER(LinkType3GameCube, "Link GameCube")
{ {
#ifndef NO_LINK
CloseLink();
#endif
SetLinkTypeMenu("LinkType3GameCube", 3); SetLinkTypeMenu("LinkType3GameCube", 3);
} }

View File

@ -1695,19 +1695,6 @@ public:
} }
} JoyPadConfigHandler[4]; } JoyPadConfigHandler[4];
class JoystickPoller : public wxTimer {
public:
void Notify() {
wxGetApp().frame->PollJoysticks();
}
void ShowDialog(wxShowEvent& ev) {
if (ev.IsShown())
Start(50);
else
Stop();
}
};
// manage fullscreen mode widget // manage fullscreen mode widget
// technically, it's more than a validator: it modifies the widget as well // technically, it's more than a validator: it modifies the widget as well
class ScreenModeList : public wxValidator { class ScreenModeList : public wxValidator {
@ -2003,7 +1990,7 @@ static bool treeid_to_name(int id, wxString& name, wxTreeCtrl* tc,
} }
// for sorting accels by command ID // for sorting accels by command ID
static bool cmdid_lt(const wxAcceleratorEntry& a, const wxAcceleratorEntry& b) static bool cmdid_lt(const wxAcceleratorEntryUnicode& a, const wxAcceleratorEntryUnicode& b)
{ {
return a.GetCommand() < b.GetCommand(); return a.GetCommand() < b.GetCommand();
} }
@ -2015,7 +2002,7 @@ public:
wxControlWithItems* lb; wxControlWithItems* lb;
wxAcceleratorEntry_v user_accels, accels; wxAcceleratorEntry_v user_accels, accels;
wxWindow *asb, *remb; wxWindow *asb, *remb;
wxKeyTextCtrl* key; wxJoyKeyTextCtrl* key;
wxControl* curas; wxControl* curas;
// since this is not the actual dialog, derived from wxDialog, which is // since this is not the actual dialog, derived from wxDialog, which is
@ -2076,10 +2063,17 @@ public:
asb->Enable(!key->GetValue().empty()); asb->Enable(!key->GetValue().empty());
int cmd = id->val; int cmd = id->val;
for (size_t i = 0; i < accels.size(); i++) for (size_t i = 0; i < accels.size(); ++i) {
if (accels[i].GetCommand() == cmdtab[cmd].cmd_id) if (accels[i].GetCommand() == cmdtab[cmd].cmd_id) {
lb->Append(wxKeyTextCtrl::ToString(accels[i].GetFlags(), if (accels[i].GetJoystick() == 0) {
accels[i].GetKeyCode())); wxString key = wxJoyKeyTextCtrl::ToCandidateString(accels[i].GetFlags(), accels[i].GetKeyCode());
lb->Append(key);
}
else {
lb->Append(accels[i].GetUkey());
}
}
}
} }
// after selecting a key in key list, enable Remove button // after selecting a key in key list, enable Remove button
@ -2099,10 +2093,11 @@ public:
return; return;
wxString selstr = lb->GetString(lsel); wxString selstr = lb->GetString(lsel);
int selmod, selkey; int selmod, selkey, seljoy;
if (!wxKeyTextCtrl::FromString(selstr, selmod, selkey)) if (!wxJoyKeyTextCtrl::FromString(selstr, selmod, selkey, seljoy))
return; // this should never happen // this should never happen
return;
remb->Enable(false); remb->Enable(false);
@ -2115,7 +2110,9 @@ public:
// first drop from user accels, if applicable // first drop from user accels, if applicable
for (wxAcceleratorEntry_v::iterator i = user_accels.begin(); for (wxAcceleratorEntry_v::iterator i = user_accels.begin();
i < user_accels.end(); ++i) i < user_accels.end(); ++i)
if (i->GetFlags() == selmod && i->GetKeyCode() == selkey) { if ((i->GetFlags() == selmod && i->GetKeyCode() == selkey)
|| (seljoy != 0 && i->GetUkey() == selstr))
{
user_accels.erase(i); user_accels.erase(i);
break; break;
} }
@ -2124,15 +2121,19 @@ public:
wxAcceleratorEntry_v& sys_accels = wxGetApp().frame->sys_accels; wxAcceleratorEntry_v& sys_accels = wxGetApp().frame->sys_accels;
for (size_t i = 0; i < sys_accels.size(); i++) for (size_t i = 0; i < sys_accels.size(); i++)
if (sys_accels[i].GetFlags() == selmod && sys_accels[i].GetKeyCode() == selkey) { if ((sys_accels[i].GetFlags() == selmod && sys_accels[i].GetKeyCode() == selkey)
wxAcceleratorEntry ne(selmod, selkey, XRCID("NOOP")); || (seljoy != 0 && sys_accels[i].GetUkey() == selstr)) // joystick system bindings?
{
wxAcceleratorEntryUnicode ne(sys_accels[i].GetUkey(), sys_accels[i].GetJoystick(), selmod, selkey, XRCID("NOOP"));
user_accels.push_back(ne); user_accels.push_back(ne);
} }
// finally, remove from accels instead of recomputing // finally, remove from accels instead of recomputing
for (wxAcceleratorEntry_v::iterator i = accels.begin(); for (wxAcceleratorEntry_v::iterator i = accels.begin();
i < accels.end(); ++i) i < accels.end(); ++i)
if (i->GetFlags() == selmod && i->GetKeyCode() == selkey) { if ((i->GetFlags() == selmod && i->GetKeyCode() == selkey)
|| (seljoy != 0 && i->GetUkey() == selstr))
{
accels.erase(i); accels.erase(i);
break; break;
} }
@ -2164,10 +2165,11 @@ public:
if (!csel.IsOk() || accel.empty()) if (!csel.IsOk() || accel.empty())
return; return;
int acmod, ackey; int acmod, ackey, acjoy;
if (!wxKeyTextCtrl::FromString(accel, acmod, ackey)) if (!wxJoyKeyTextCtrl::FromString(accel, acmod, ackey, acjoy))
return; // this should never happen // this should never happen
return;
for (unsigned int i = 0; i < lb->GetCount(); i++) for (unsigned int i = 0; i < lb->GetCount(); i++)
if (lb->GetString(i) == accel) if (lb->GetString(i) == accel)
@ -2178,15 +2180,17 @@ public:
// first drop from user accels, if applicable // first drop from user accels, if applicable
for (wxAcceleratorEntry_v::iterator i = user_accels.begin(); for (wxAcceleratorEntry_v::iterator i = user_accels.begin();
i < user_accels.end(); ++i) i < user_accels.end(); ++i)
if (i->GetFlags() == acmod && i->GetKeyCode() == ackey) { if ((i->GetFlags() == acmod && i->GetKeyCode() == ackey && i->GetJoystick() != acjoy)
|| (acjoy != 0 && i->GetUkey() == accel)) {
user_accels.erase(i); user_accels.erase(i);
break; break;
} }
// then assign to this command // then assign to this command
const TreeInt* id = static_cast<const TreeInt*>(tc->GetItemData(csel)); const TreeInt* id = static_cast<const TreeInt*>(tc->GetItemData(csel));
wxAcceleratorEntry ne(acmod, ackey, cmdtab[id->val].cmd_id); wxAcceleratorEntryUnicode ne(accel, acjoy, acmod, ackey, cmdtab[id->val].cmd_id);
user_accels.push_back(ne); user_accels.push_back(ne);
// now assigned to this cmd... // now assigned to this cmd...
wxString lab; wxString lab;
treeid_to_name(id->val, lab, tc, tc->GetRootItem()); treeid_to_name(id->val, lab, tc, tc->GetRootItem());
@ -2207,9 +2211,9 @@ public:
return; return;
} }
int acmod, ackey; int acmod, ackey, acjoy;
if (!wxKeyTextCtrl::FromString(nkey, acmod, ackey)) { if (!wxJoyKeyTextCtrl::FromString(nkey, acmod, ackey, acjoy)) {
// this should never happen // this should never happen
key->SetValue(wxT("")); key->SetValue(wxT(""));
asb->Enable(false); asb->Enable(false);
@ -2220,7 +2224,8 @@ public:
int cmd = -1; int cmd = -1;
for (size_t i = 0; i < accels.size(); i++) for (size_t i = 0; i < accels.size(); i++)
if (accels[i].GetFlags() == acmod && accels[i].GetKeyCode() == ackey) { if ((accels[i].GetFlags() == acmod && accels[i].GetKeyCode() == ackey)
|| (acjoy != 0 && accels[i].GetUkey() == nkey)) {
int cmdid = accels[i].GetCommand(); int cmdid = accels[i].GetCommand();
for (cmd = 0; cmd < ncmds; cmd++) for (cmd = 0; cmd < ncmds; cmd++)
@ -2547,10 +2552,11 @@ wxAcceleratorEntry_v MainFrame::get_accels(wxAcceleratorEntry_v user_accels)
// silently keep only last defined binding // silently keep only last defined binding
// same horribly inefficent O(n*m) search for duplicates as above.. // same horribly inefficent O(n*m) search for duplicates as above..
for (size_t i = 0; i < user_accels.size(); i++) { for (size_t i = 0; i < user_accels.size(); i++) {
const wxAcceleratorEntry& ae = user_accels[i]; const wxAcceleratorEntryUnicode& ae = user_accels[i];
for (wxAcceleratorEntry_v::iterator e = accels.begin(); e < accels.end(); ++e) for (wxAcceleratorEntry_v::iterator e = accels.begin(); e < accels.end(); ++e)
if (ae.GetFlags() == e->GetFlags() && ae.GetKeyCode() == e->GetKeyCode()) { if ((ae.GetFlags() == e->GetFlags() && ae.GetKeyCode() == e->GetKeyCode())
|| (ae.GetJoystick() == e->GetJoystick() && ae.GetUkey() == e->GetUkey())) {
accels.erase(e); accels.erase(e);
break; break;
} }
@ -2572,9 +2578,12 @@ void MainFrame::set_global_accels()
// the menus will be added now // the menus will be added now
// first, zero out menu item on all accels // first, zero out menu item on all accels
for (size_t i = 0; i < accels.size(); i++) for (size_t i = 0; i < accels.size(); ++i) {
accels[i].Set(accels[i].GetFlags(), accels[i].GetKeyCode(), accels[i].Set(accels[i].GetUkey(), accels[i].GetJoystick(), accels[i].GetFlags(), accels[i].GetKeyCode(), accels[i].GetCommand());
accels[i].GetCommand()); if (accels[i].GetJoystick()) {
joy.Add(accels[i].GetJoystick() - 1);
}
}
// yet another O(n*m) loop. I really ought to sort the accel arrays // yet another O(n*m) loop. I really ought to sort the accel arrays
for (int i = 0; i < ncmds; i++) { for (int i = 0; i < ncmds; i++) {
@ -2583,26 +2592,25 @@ void MainFrame::set_global_accels()
if (!mi) if (!mi)
continue; continue;
// only *last* accelerator is made visible in menu // only *last* accelerator is made visible in menu (non-unicode)
// and is flagged as such by setting menu item in accel // and is flagged as such by setting menu item in accel
// the last is chosen so menu overrides non-menu and user overrides // the last is chosen so menu overrides non-menu and user overrides
// system // system
int cmd = cmdtab[i].cmd_id; int cmd = cmdtab[i].cmd_id;
int last_accel = -1; int last_accel = -1;
for (size_t j = 0; j < accels.size(); j++) for (size_t j = 0; j < accels.size(); ++j)
if (cmd == accels[j].GetCommand()) if (cmd == accels[j].GetCommand())
last_accel = j; last_accel = j;
if (last_accel >= 0) { if (last_accel >= 0) {
DoSetAccel(mi, &accels[last_accel]); DoSetAccel(mi, &accels[last_accel]);
accels[last_accel].Set(accels[last_accel].GetFlags(), accels[last_accel].Set(accels[last_accel].GetUkey(), accels[last_accel].GetJoystick(), accels[last_accel].GetFlags(), accels[last_accel].GetKeyCode(), accels[last_accel].GetCommand(), mi);
accels[last_accel].GetKeyCode(), } else {
accels[last_accel].GetCommand(), mi);
} else
// clear out user-cleared menu items // clear out user-cleared menu items
DoSetAccel(mi, NULL); DoSetAccel(mi, NULL);
} }
}
// Finally, install a global accelerator table for any non-menu accels // Finally, install a global accelerator table for any non-menu accels
int len = 0; int len = 0;
@ -2612,7 +2620,7 @@ void MainFrame::set_global_accels()
len++; len++;
if (len) { if (len) {
wxAcceleratorEntry tab[1000]; wxAcceleratorEntryUnicode tab[1000];
for (size_t i = 0, j = 0; i < accels.size(); i++) for (size_t i = 0, j = 0; i < accels.size(); i++)
if (!accels[i].GetMenuItem()) if (!accels[i].GetMenuItem())
@ -2627,7 +2635,7 @@ void MainFrame::set_global_accels()
// save recent accels // save recent accels
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
recent_accel[i] = wxAcceleratorEntry(); recent_accel[i] = wxAcceleratorEntryUnicode();
for (size_t i = 0; i < accels.size(); i++) for (size_t i = 0; i < accels.size(); i++)
if (accels[i].GetCommand() >= wxID_FILE1 && accels[i].GetCommand() <= wxID_FILE10) if (accels[i].GetCommand() >= wxID_FILE1 && accels[i].GetCommand() <= wxID_FILE10)
@ -2922,7 +2930,7 @@ bool MainFrame::BindControls()
} }
if (a) if (a)
sys_accels.push_back(*a); sys_accels.push_back(wxAcceleratorEntryUnicode(a));
else else
// strip from label so user isn't confused // strip from label so user isn't confused
DoSetAccel(mi, NULL); DoSetAccel(mi, NULL);
@ -3830,13 +3838,6 @@ bool MainFrame::BindControls()
NULL, &JoyPadConfigHandler[i]); NULL, &JoyPadConfigHandler[i]);
} }
// poll the joystick
JoystickPoller* jpoll = new JoystickPoller();
joyDialog->Connect(wxID_ANY, wxEVT_SHOW,
wxShowEventHandler(JoystickPoller::ShowDialog),
jpoll, jpoll);
joyDialog->Fit(); joyDialog->Fit();
} }
@ -3860,7 +3861,7 @@ bool MainFrame::BindControls()
accel_config_handler.lb = lb; accel_config_handler.lb = lb;
accel_config_handler.asb = SafeXRCCTRL<wxButton>(d, "Assign"); accel_config_handler.asb = SafeXRCCTRL<wxButton>(d, "Assign");
accel_config_handler.remb = SafeXRCCTRL<wxButton>(d, "Remove"); accel_config_handler.remb = SafeXRCCTRL<wxButton>(d, "Remove");
accel_config_handler.key = SafeXRCCTRL<wxKeyTextCtrl>(d, "Shortcut"); accel_config_handler.key = SafeXRCCTRL<wxJoyKeyTextCtrl>(d, "Shortcut");
accel_config_handler.curas = SafeXRCCTRL<wxControl>(d, "AlreadyThere"); accel_config_handler.curas = SafeXRCCTRL<wxControl>(d, "AlreadyThere");
accel_config_handler.key->MoveBeforeInTabOrder(accel_config_handler.asb); accel_config_handler.key->MoveBeforeInTabOrder(accel_config_handler.asb);
accel_config_handler.key->SetMultikey(0); accel_config_handler.key->SetMultikey(0);

View File

@ -42,9 +42,9 @@
opts_t gopts; opts_t gopts;
// having the standard menu accels here means they will work even without menus // having the standard menu accels here means they will work even without menus
const wxAcceleratorEntry default_accels[] = { const wxAcceleratorEntryUnicode default_accels[] = {
wxAcceleratorEntry(wxMOD_CMD, wxT('C'), XRCID("CheatsList")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('C'), XRCID("CheatsList")),
wxAcceleratorEntry(wxMOD_CMD, wxT('N'), XRCID("NextFrame")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('N'), XRCID("NextFrame")),
// some ports add ctrl-q anyway, so may as well make it official // some ports add ctrl-q anyway, so may as well make it official
// maybe make alt-f4 universal as well... // maybe make alt-f4 universal as well...
// FIXME: ctrl-Q does not work on wxMSW // FIXME: ctrl-Q does not work on wxMSW
@ -56,79 +56,79 @@ const wxAcceleratorEntry default_accels[] = {
// this was annoying people #298 // this was annoying people #298
//wxAcceleratorEntry(wxMOD_CMD, wxT('X'), wxID_EXIT), //wxAcceleratorEntry(wxMOD_CMD, wxT('X'), wxID_EXIT),
wxAcceleratorEntry(wxMOD_CMD, wxT('Q'), wxID_EXIT), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('Q'), wxID_EXIT),
// FIXME: ctrl-W does not work on wxMSW // FIXME: ctrl-W does not work on wxMSW
wxAcceleratorEntry(wxMOD_CMD, wxT('W'), wxID_CLOSE), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('W'), wxID_CLOSE),
// load most recent is more commonly used than load other // load most recent is more commonly used than load other
//wxAcceleratorEntry(wxMOD_CMD, wxT('L'), XRCID("Load")), //wxAcceleratorEntry(wxMOD_CMD, wxT('L'), XRCID("Load")),
wxAcceleratorEntry(wxMOD_CMD, wxT('L'), XRCID("LoadGameRecent")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('L'), XRCID("LoadGameRecent")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F1, XRCID("LoadGame01")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F1, XRCID("LoadGame01")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F2, XRCID("LoadGame02")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F2, XRCID("LoadGame02")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F3, XRCID("LoadGame03")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F3, XRCID("LoadGame03")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F4, XRCID("LoadGame04")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F4, XRCID("LoadGame04")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F5, XRCID("LoadGame05")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F5, XRCID("LoadGame05")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F6, XRCID("LoadGame06")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F6, XRCID("LoadGame06")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F7, XRCID("LoadGame07")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F7, XRCID("LoadGame07")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F8, XRCID("LoadGame08")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F8, XRCID("LoadGame08")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F9, XRCID("LoadGame09")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F9, XRCID("LoadGame09")),
wxAcceleratorEntry(wxMOD_NONE, WXK_F10, XRCID("LoadGame10")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_F10, XRCID("LoadGame10")),
wxAcceleratorEntry(wxMOD_NONE, WXK_PAUSE, XRCID("Pause")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_PAUSE, XRCID("Pause")),
wxAcceleratorEntry(wxMOD_CMD, wxT('P'), XRCID("Pause")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('P'), XRCID("Pause")),
wxAcceleratorEntry(wxMOD_CMD, wxT('R'), XRCID("Reset")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('R'), XRCID("Reset")),
// add shortcuts for original size multiplier #415 // add shortcuts for original size multiplier #415
wxAcceleratorEntry(wxMOD_NONE, wxT('1'), XRCID("SetSize1x")), wxAcceleratorEntryUnicode(wxMOD_NONE, wxT('1'), XRCID("SetSize1x")),
wxAcceleratorEntry(wxMOD_NONE, wxT('2'), XRCID("SetSize2x")), wxAcceleratorEntryUnicode(wxMOD_NONE, wxT('2'), XRCID("SetSize2x")),
wxAcceleratorEntry(wxMOD_NONE, wxT('3'), XRCID("SetSize3x")), wxAcceleratorEntryUnicode(wxMOD_NONE, wxT('3'), XRCID("SetSize3x")),
wxAcceleratorEntry(wxMOD_NONE, wxT('4'), XRCID("SetSize4x")), wxAcceleratorEntryUnicode(wxMOD_NONE, wxT('4'), XRCID("SetSize4x")),
wxAcceleratorEntry(wxMOD_NONE, wxT('5'), XRCID("SetSize5x")), wxAcceleratorEntryUnicode(wxMOD_NONE, wxT('5'), XRCID("SetSize5x")),
wxAcceleratorEntry(wxMOD_NONE, wxT('6'), XRCID("SetSize6x")), wxAcceleratorEntryUnicode(wxMOD_NONE, wxT('6'), XRCID("SetSize6x")),
// save oldest is more commonly used than save other // save oldest is more commonly used than save other
//wxAcceleratorEntry(wxMOD_CMD, wxT('S'), XRCID("Save")), //wxAcceleratorEntry(wxMOD_CMD, wxT('S'), XRCID("Save")),
wxAcceleratorEntry(wxMOD_CMD, wxT('S'), XRCID("SaveGameOldest")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('S'), XRCID("SaveGameOldest")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F1, XRCID("SaveGame01")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F1, XRCID("SaveGame01")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F2, XRCID("SaveGame02")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F2, XRCID("SaveGame02")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F3, XRCID("SaveGame03")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F3, XRCID("SaveGame03")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F4, XRCID("SaveGame04")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F4, XRCID("SaveGame04")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F5, XRCID("SaveGame05")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F5, XRCID("SaveGame05")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F6, XRCID("SaveGame06")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F6, XRCID("SaveGame06")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F7, XRCID("SaveGame07")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F7, XRCID("SaveGame07")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F8, XRCID("SaveGame08")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F8, XRCID("SaveGame08")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F9, XRCID("SaveGame09")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F9, XRCID("SaveGame09")),
wxAcceleratorEntry(wxMOD_SHIFT, WXK_F10, XRCID("SaveGame10")), wxAcceleratorEntryUnicode(wxMOD_SHIFT, WXK_F10, XRCID("SaveGame10")),
// I prefer the SDL ESC key binding // I prefer the SDL ESC key binding
//wxAcceleratorEntry(wxMOD_NONE, WXK_ESCAPE, XRCID("ToggleFullscreen"), //wxAcceleratorEntry(wxMOD_NONE, WXK_ESCAPE, XRCID("ToggleFullscreen"),
// alt-enter is more standard anyway // alt-enter is more standard anyway
wxAcceleratorEntry(wxMOD_ALT, WXK_RETURN, XRCID("ToggleFullscreen")), wxAcceleratorEntryUnicode(wxMOD_ALT, WXK_RETURN, XRCID("ToggleFullscreen")),
wxAcceleratorEntry(wxMOD_ALT, wxT('1'), XRCID("JoypadAutofireA")), wxAcceleratorEntryUnicode(wxMOD_ALT, wxT('1'), XRCID("JoypadAutofireA")),
wxAcceleratorEntry(wxMOD_ALT, wxT('2'), XRCID("JoypadAutofireB")), wxAcceleratorEntryUnicode(wxMOD_ALT, wxT('2'), XRCID("JoypadAutofireB")),
wxAcceleratorEntry(wxMOD_ALT, wxT('3'), XRCID("JoypadAutofireL")), wxAcceleratorEntryUnicode(wxMOD_ALT, wxT('3'), XRCID("JoypadAutofireL")),
wxAcceleratorEntry(wxMOD_ALT, wxT('4'), XRCID("JoypadAutofireR")), wxAcceleratorEntryUnicode(wxMOD_ALT, wxT('4'), XRCID("JoypadAutofireR")),
wxAcceleratorEntry(wxMOD_CMD, wxT('1'), XRCID("VideoLayersBG0")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('1'), XRCID("VideoLayersBG0")),
wxAcceleratorEntry(wxMOD_CMD, wxT('2'), XRCID("VideoLayersBG1")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('2'), XRCID("VideoLayersBG1")),
wxAcceleratorEntry(wxMOD_CMD, wxT('3'), XRCID("VideoLayersBG2")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('3'), XRCID("VideoLayersBG2")),
wxAcceleratorEntry(wxMOD_CMD, wxT('4'), XRCID("VideoLayersBG3")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('4'), XRCID("VideoLayersBG3")),
wxAcceleratorEntry(wxMOD_CMD, wxT('5'), XRCID("VideoLayersOBJ")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('5'), XRCID("VideoLayersOBJ")),
wxAcceleratorEntry(wxMOD_CMD, wxT('6'), XRCID("VideoLayersWIN0")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('6'), XRCID("VideoLayersWIN0")),
wxAcceleratorEntry(wxMOD_CMD, wxT('7'), XRCID("VideoLayersWIN1")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('7'), XRCID("VideoLayersWIN1")),
wxAcceleratorEntry(wxMOD_CMD, wxT('8'), XRCID("VideoLayersOBJWIN")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('8'), XRCID("VideoLayersOBJWIN")),
wxAcceleratorEntry(wxMOD_CMD, wxT('B'), XRCID("Rewind")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('B'), XRCID("Rewind")),
// following are not in standard menus // following are not in standard menus
// FILExx are filled in when recent menu is filled // FILExx are filled in when recent menu is filled
wxAcceleratorEntry(wxMOD_CMD, WXK_F1, wxID_FILE1), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F1, wxID_FILE1),
wxAcceleratorEntry(wxMOD_CMD, WXK_F2, wxID_FILE2), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F2, wxID_FILE2),
wxAcceleratorEntry(wxMOD_CMD, WXK_F3, wxID_FILE3), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F3, wxID_FILE3),
wxAcceleratorEntry(wxMOD_CMD, WXK_F4, wxID_FILE4), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F4, wxID_FILE4),
wxAcceleratorEntry(wxMOD_CMD, WXK_F5, wxID_FILE5), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F5, wxID_FILE5),
wxAcceleratorEntry(wxMOD_CMD, WXK_F6, wxID_FILE6), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F6, wxID_FILE6),
wxAcceleratorEntry(wxMOD_CMD, WXK_F7, wxID_FILE7), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F7, wxID_FILE7),
wxAcceleratorEntry(wxMOD_CMD, WXK_F8, wxID_FILE8), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F8, wxID_FILE8),
wxAcceleratorEntry(wxMOD_CMD, WXK_F9, wxID_FILE9), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F9, wxID_FILE9),
wxAcceleratorEntry(wxMOD_CMD, WXK_F10, wxID_FILE10), wxAcceleratorEntryUnicode(wxMOD_CMD, WXK_F10, wxID_FILE10),
wxAcceleratorEntry(wxMOD_CMD, wxT('0'), XRCID("VideoLayersReset")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('0'), XRCID("VideoLayersReset")),
wxAcceleratorEntry(wxMOD_CMD, wxT('G'), XRCID("ChangeFilter")), wxAcceleratorEntryUnicode(wxMOD_CMD, wxT('G'), XRCID("ChangeFilter")),
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_ADD, XRCID("IncreaseVolume")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_NUMPAD_ADD, XRCID("IncreaseVolume")),
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_SUBTRACT, XRCID("DecreaseVolume")), wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_NUMPAD_SUBTRACT, XRCID("DecreaseVolume")),
wxAcceleratorEntry(wxMOD_NONE, WXK_NUMPAD_ENTER, XRCID("ToggleSound")) wxAcceleratorEntryUnicode(wxMOD_NONE, WXK_NUMPAD_ENTER, XRCID("ToggleSound"))
}; };
const int num_def_accels = sizeof(default_accels) / sizeof(default_accels[0]); const int num_def_accels = sizeof(default_accels) / sizeof(default_accels[0]);
@ -669,17 +669,15 @@ void load_opts()
kbopt.append(cmdtab[i].cmd); kbopt.append(cmdtab[i].cmd);
if (cfg->Read(kbopt, &s) && s.size()) { if (cfg->Read(kbopt, &s) && s.size()) {
wxAcceleratorEntry_v val = wxKeyTextCtrl::FromString(s); wxAcceleratorEntry_v val = wxJoyKeyTextCtrl::ToAccelFromString(s);
if (!val.size()) if (!val.size())
wxLogWarning(_("Invalid key binding %s for %s"), s.c_str(), kbopt.c_str()); wxLogWarning(_("Invalid key binding %s for %s"), s.c_str(), kbopt.c_str());
else { else {
for (size_t j = 0; j < val.size(); j++) for (size_t j = 0; j < val.size(); j++)
val[j].Set(val[j].GetFlags(), val[j].GetKeyCode(), val[j].Set(val[j].GetUkey(), val[j].GetJoystick(), val[j].GetFlags(), val[j].GetKeyCode(), cmdtab[i].cmd_id);
cmdtab[i].cmd_id);
gopts.accels.insert(gopts.accels.end(), gopts.accels.insert(gopts.accels.end(), val.begin(), val.end());
val.begin(), val.end());
} }
} }
} }
@ -763,7 +761,7 @@ void update_opts()
wxString s, o; wxString s, o;
wxString optname; wxString optname;
optname.Printf(wxT("Joypad/%d/%s"), i + 1, joynames[j].c_str()); optname.Printf(wxT("Joypad/%d/%s"), i + 1, joynames[j].c_str());
s = wxJoyKeyTextCtrl::ToString(gopts.joykey_bindings[i][j]); s = wxJoyKeyTextCtrl::ToString(gopts.joykey_bindings[i][j], wxT(','), true);
cfg->Read(optname, &o); cfg->Read(optname, &o);
if (o != s) if (o != s)
@ -825,7 +823,7 @@ void update_opts()
break; break;
wxAcceleratorEntry_v nv(i, j); wxAcceleratorEntry_v nv(i, j);
wxString nvs = wxKeyTextCtrl::ToString(nv); wxString nvs = wxJoyKeyTextCtrl::FromAccelToString(nv, wxT(','), true);
if (nvs != cfg->Read(command)) if (nvs != cfg->Read(command))
cfg->Write(command, nvs); cfg->Write(command, nvs);
@ -953,11 +951,10 @@ bool opt_set(const wxString& name, const wxString& val)
} }
if (!val.empty()) { if (!val.empty()) {
auto aval = wxKeyTextCtrl::FromString(val); auto aval = wxJoyKeyTextCtrl::ToAccelFromString(val);
for (size_t i = 0; i < aval.size(); i++) for (size_t i = 0; i < aval.size(); i++)
aval[i].Set(aval[i].GetFlags(), aval[i].GetKeyCode(), aval[i].Set(aval[i].GetUkey(), aval[i].GetJoystick(), aval[i].GetFlags(), aval[i].GetKeyCode(), cmd->cmd_id);
cmd->cmd_id);
if (!aval.size()) if (!aval.size())
wxLogWarning(_("Invalid key binding %s for %s"), val.c_str(), name.c_str()); wxLogWarning(_("Invalid key binding %s for %s"), val.c_str(), name.c_str());

View File

@ -119,7 +119,7 @@ opt_desc new_opt_desc(wxString opt = wxT(""), const char* cmd = NULL, wxString d
extern const int num_opts; extern const int num_opts;
extern const wxAcceleratorEntry default_accels[]; extern const wxAcceleratorEntryUnicode default_accels[];
extern const int num_def_accels; extern const int num_def_accels;
// call to setup default keys. // call to setup default keys.

View File

@ -16,6 +16,7 @@
#include "drawing.h" #include "drawing.h"
#include "filters.h" #include "filters.h"
#include "wxvbam.h" #include "wxvbam.h"
#include "wxutil.h"
#ifdef __WXMSW__ #ifdef __WXMSW__
#include <windows.h> #include <windows.h>
@ -317,6 +318,7 @@ void GameArea::LoadGame(const wxString& name)
emulating = true; emulating = true;
was_paused = true; was_paused = true;
MainFrame* mf = wxGetApp().frame; MainFrame* mf = wxGetApp().frame;
mf->StopPoll();
mf->SetJoystick(); mf->SetJoystick();
mf->cmd_enable &= ~(CMDEN_GB | CMDEN_GBA); mf->cmd_enable &= ~(CMDEN_GB | CMDEN_GBA);
mf->cmd_enable |= ONLOAD_CMDEN; mf->cmd_enable |= ONLOAD_CMDEN;
@ -567,6 +569,7 @@ void GameArea::UnloadGame(bool destruct)
mf->enable_menus(); mf->enable_menus();
mf->SetJoystick(); mf->SetJoystick();
mf->ResetCheatSearch(); mf->ResetCheatSearch();
mf->StartPoll();
if (rewind_mem) if (rewind_mem)
num_rewind_states = 0; num_rewind_states = 0;
@ -1032,8 +1035,6 @@ void GameArea::OnIdle(wxIdleEvent& event)
wxWindow* w = panel->GetWindow(); wxWindow* w = panel->GetWindow();
// set up event handlers // set up event handlers
// use both CHAR_HOOK and KEY_DOWN in case CHAR_HOOK does not work for whatever reason
w->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(GameArea::OnKeyDown), NULL, this);
w->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GameArea::OnKeyDown), NULL, this); w->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GameArea::OnKeyDown), NULL, this);
w->Connect(wxEVT_KEY_UP, wxKeyEventHandler(GameArea::OnKeyUp), NULL, this); w->Connect(wxEVT_KEY_UP, wxKeyEventHandler(GameArea::OnKeyUp), NULL, this);
w->Connect(wxEVT_PAINT, wxPaintEventHandler(GameArea::PaintEv), NULL, this); w->Connect(wxEVT_PAINT, wxPaintEventHandler(GameArea::PaintEv), NULL, this);
@ -1313,28 +1314,19 @@ static void draw_black_background(wxWindow* win) {
void GameArea::OnKeyDown(wxKeyEvent& ev) void GameArea::OnKeyDown(wxKeyEvent& ev)
{ {
// check if the key is pressed indeed and then process it int kc = getKeyboardKeyCode(ev);
wxKeyCode keyCode = (wxKeyCode)ev.GetKeyCode(); if (process_key_press(true, kc, ev.GetModifiers())) {
if (wxGetKeyState(keyCode) && process_key_press(true, ev.GetKeyCode(), ev.GetModifiers())) {
ev.Skip(false);
ev.StopPropagation();
wxWakeUpIdle(); wxWakeUpIdle();
} }
else {
ev.Skip(); ev.Skip();
}
} }
void GameArea::OnKeyUp(wxKeyEvent& ev) void GameArea::OnKeyUp(wxKeyEvent& ev)
{ {
if (process_key_press(false, ev.GetKeyCode(), ev.GetModifiers())) { int kc = getKeyboardKeyCode(ev);
ev.Skip(false); if (process_key_press(false, kc, ev.GetModifiers())) {
ev.StopPropagation();
wxWakeUpIdle(); wxWakeUpIdle();
} }
else {
ev.Skip();
}
} }
// these three are forwarded to the DrawingPanel instance // these three are forwarded to the DrawingPanel instance

View File

@ -1,4 +1,5 @@
#include "strutils.h" #include "strutils.h"
#include <wx/tokenzr.h>
// From: https://stackoverflow.com/a/7408245/262458 // From: https://stackoverflow.com/a/7408245/262458
// //
@ -21,6 +22,25 @@ wxArrayString str_split(const wxString& text, const wxString& sep) {
return tokens; return tokens;
} }
wxArrayString str_split_with_sep(const wxString& text, const wxString& sep)
{
wxArrayString tokens;
bool sepIsTokenToo = false;
wxStringTokenizer tokenizer(text, sep, wxTOKEN_RET_EMPTY_ALL);
while (tokenizer.HasMoreTokens()) {
wxString token = tokenizer.GetNextToken();
if (token.IsEmpty()) {
if (!sepIsTokenToo) {
sepIsTokenToo = true;
tokens.Add(sep);
}
continue;
}
tokens.Add(token);
}
return tokens;
}
size_t vec_find(wxArrayString& opts, const wxString& val) { size_t vec_find(wxArrayString& opts, const wxString& val) {
return opts.Index(val); return opts.Index(val);
} }

View File

@ -7,6 +7,11 @@
// From: https://stackoverflow.com/a/7408245/262458 // From: https://stackoverflow.com/a/7408245/262458
wxArrayString str_split(const wxString& text, const wxString& sep); wxArrayString str_split(const wxString& text, const wxString& sep);
// Same as above, but it includes the sep dir.
// If "A,,,B" is the text and "," is sep, then
// 'A', ',' and 'B' will be in the output.
wxArrayString str_split_with_sep(const wxString& text, const wxString& sep);
// From: https://stackoverflow.com/a/15099743/262458 // From: https://stackoverflow.com/a/15099743/262458
size_t vec_find(wxArrayString& opts, const wxString& val); size_t vec_find(wxArrayString& opts, const wxString& val);

View File

@ -1,6 +1,7 @@
#include "viewsupt.h" #include "viewsupt.h"
#include "../common/ConfigManager.h" #include "../common/ConfigManager.h"
#include "wxvbam.h" #include "wxvbam.h"
#include "wxutil.h"
namespace Viewers { namespace Viewers {
void Viewer::CloseDlg(wxCloseEvent& ev) void Viewer::CloseDlg(wxCloseEvent& ev)
@ -415,7 +416,7 @@ void MemView::ShowCaret()
void MemView::KeyEvent(wxKeyEvent& ev) void MemView::KeyEvent(wxKeyEvent& ev)
{ {
uint32_t k = ev.GetKeyCode(); uint32_t k = getKeyboardKeyCode(ev);
int nnib = 2 << fmt; int nnib = 2 << fmt;
switch (k) { switch (k) {

View File

@ -1,4 +1,6 @@
#include <wx/tokenzr.h>
#include "wx/joyedit.h" #include "wx/joyedit.h"
#include "strutils.h"
// FIXME: suppport analog/digital flag on per-axis basis // FIXME: suppport analog/digital flag on per-axis basis
@ -104,10 +106,10 @@ void wxJoyKeyTextCtrl::OnJoy(wxSDLJoyEvent& event)
Navigate(); Navigate();
} }
wxString wxJoyKeyTextCtrl::ToString(int mod, int key, int joy) wxString wxJoyKeyTextCtrl::ToString(int mod, int key, int joy, bool isConfig)
{ {
if (!joy) if (!joy)
return wxKeyTextCtrl::ToString(mod, key); return wxKeyTextCtrl::ToString(mod, key, isConfig);
wxString s; wxString s;
// Note: wx translates unconditionally (2.8.12, 2.9.1)! // Note: wx translates unconditionally (2.8.12, 2.9.1)!
@ -165,7 +167,7 @@ wxString wxJoyKeyTextCtrl::ToString(int mod, int key, int joy)
return s; return s;
} }
wxString wxJoyKeyTextCtrl::ToString(wxJoyKeyBinding_v keys, wxChar sep) wxString wxJoyKeyTextCtrl::ToString(wxJoyKeyBinding_v keys, wxChar sep, bool isConfig)
{ {
wxString ret; wxString ret;
@ -173,7 +175,26 @@ wxString wxJoyKeyTextCtrl::ToString(wxJoyKeyBinding_v keys, wxChar sep)
if (i > 0) if (i > 0)
ret += sep; ret += sep;
wxString key = ToString(keys[i].mod, keys[i].key, keys[i].joy); wxString key = ToString(keys[i].mod, keys[i].key, keys[i].joy, isConfig);
if (key.empty())
return wxEmptyString;
ret += key;
}
return ret;
}
wxString wxJoyKeyTextCtrl::FromAccelToString(wxAcceleratorEntry_v keys, wxChar sep, bool isConfig)
{
wxString ret;
for (size_t i = 0; i < keys.size(); i++) {
if (i > 0)
ret += sep;
wxString key = ToString(keys[i].GetFlags(), keys[i].GetKeyCode(), keys[i].GetJoystick(), isConfig);
if (key.empty()) if (key.empty())
return wxEmptyString; return wxEmptyString;
@ -262,19 +283,16 @@ static bool ParseJoy(const wxString& s, int len, int& mod, int& key, int& joy)
} else if (is_ctrl(hatre)) { } else if (is_ctrl(hatre)) {
hatre.GetMatch(&b, &l, 1); hatre.GetMatch(&b, &l, 1);
key = simple_atoi(p.Mid(b), l); key = simple_atoi(p.Mid(b), l);
#define check_dir(n, d) else if (hatre.GetMatch(&b, &l, n) && l > 0) mod = WXJB_HAT_##d #define check_dir(n, d) if (hatre.GetMatch(&b, &l, n) && l > 0) mod = WXJB_HAT_##d
if (0)
;
check_dir(3, N); check_dir(3, N);
check_dir(4, S); else check_dir(4, S);
check_dir(5, E); else check_dir(5, E);
check_dir(6, W); else check_dir(6, W);
check_dir(7, NE); else check_dir(7, NE);
check_dir(8, SE); else check_dir(8, SE);
check_dir(9, SW); else check_dir(9, SW);
check_dir(10, NW); else check_dir(10, NW);
} else { } else {
joy = 0; joy = 0;
return false; return false;
@ -300,34 +318,30 @@ wxJoyKeyBinding_v wxJoyKeyTextCtrl::FromString(const wxString& s, wxChar sep)
{ {
wxJoyKeyBinding_v ret, empty; wxJoyKeyBinding_v ret, empty;
int mod, key, joy; int mod, key, joy;
size_t len = s.size(); if (s.size() == 0)
if (!len)
return empty; return empty;
for (size_t lastkey = len - 1; (lastkey = s.rfind(sep, lastkey)) != wxString::npos; lastkey--) { for (const auto& token : str_split_with_sep(s, sep)) {
if (lastkey == len - 1) { if (!ParseString(token, token.size(), mod, key, joy))
// sep as accel
if (!lastkey)
break;
if (s[lastkey - 1] == wxT('-') || s[lastkey - 1] == wxT('+') || s[lastkey - 1] == sep)
continue;
}
if (!ParseString(s.Mid(lastkey + 1), len - lastkey - 1, mod, key, joy))
return empty; return empty;
wxJoyKeyBinding jb = { key, mod, joy }; wxJoyKeyBinding jb = { key, mod, joy };
ret.insert(ret.begin(), jb); ret.insert(ret.begin(), jb);
len = lastkey;
} }
return ret;
}
if (!ParseString(s, len, mod, key, joy)) wxAcceleratorEntry_v wxJoyKeyTextCtrl::ToAccelFromString(const wxString& s, wxChar sep)
{
wxAcceleratorEntry_v ret, empty;
int mod, key, joy;
if (s.size() == 0)
return empty; return empty;
wxJoyKeyBinding jb = { key, mod, joy }; for (const auto& token : str_split_with_sep(s, sep)) {
ret.insert(ret.begin(), jb); if (!ParseString(token, token.size(), mod, key, joy))
return empty;
ret.insert(ret.begin(), wxAcceleratorEntryUnicode(token, joy, mod, key));
}
return ret; return ret;
} }

View File

@ -1,9 +1,16 @@
#include <wx/tokenzr.h>
#include <wx/log.h> #include <wx/log.h>
#include "wx/keyedit.h" #include "wx/keyedit.h"
#include "strutils.h"
IMPLEMENT_DYNAMIC_CLASS(wxKeyTextCtrl, wxTextCtrl) IMPLEMENT_DYNAMIC_CLASS(wxKeyTextCtrl, wxTextCtrl)
BEGIN_EVENT_TABLE(wxKeyTextCtrl, wxTextCtrl) BEGIN_EVENT_TABLE(wxKeyTextCtrl, wxTextCtrl)
// EVT_CHAR is better than EVT_KEY_DOWN here because it is where
// the raw key events will have been cooked using whatever recipes
// are in effect from the os, locale, international keyboard
// settings, etc. But we shouldn't need it for now, otherwise
// we would have problems detecting letter cases.
EVT_KEY_DOWN(wxKeyTextCtrl::OnKeyDown) EVT_KEY_DOWN(wxKeyTextCtrl::OnKeyDown)
EVT_KEY_UP(wxKeyTextCtrl::OnKeyUp) EVT_KEY_UP(wxKeyTextCtrl::OnKeyUp)
END_EVENT_TABLE() END_EVENT_TABLE()
@ -11,7 +18,11 @@ END_EVENT_TABLE()
void wxKeyTextCtrl::OnKeyDown(wxKeyEvent& event) void wxKeyTextCtrl::OnKeyDown(wxKeyEvent& event)
{ {
lastmod = event.GetModifiers(); lastmod = event.GetModifiers();
lastkey = event.GetKeyCode(); lastkey = getKeyboardKeyCode(event);
if (lastkey != WXK_NONE) // not a control character
{
KeyboardInputMap::AddMap(ToCandidateString(lastmod, lastkey), lastkey, lastmod);
}
} }
void wxKeyTextCtrl::OnKeyUp(wxKeyEvent& event) void wxKeyTextCtrl::OnKeyUp(wxKeyEvent& event)
@ -47,7 +58,7 @@ void wxKeyTextCtrl::OnKeyUp(wxKeyEvent& event)
} else } else
Clear(); Clear();
} else { } else {
wxString nv = ToString(mod, key); wxString nv = ToCandidateString(mod, key);
if (nv.empty()) if (nv.empty())
return; return;
@ -66,18 +77,35 @@ void wxKeyTextCtrl::OnKeyUp(wxKeyEvent& event)
Navigate(); Navigate();
} }
wxString wxKeyTextCtrl::ToString(int mod, int key) wxString wxKeyTextCtrl::ToString(int mod, int key, bool isConfig)
{
wxString s = ToCandidateString(mod, key);
// Check for unicode char. It is not possible to use it for
// wxAcceleratorEntry `FromString`
wxLogNull disable_logging;
wxAcceleratorEntryUnicode aeTest;
if (!aeTest.FromString(s) || !s.IsAscii()) {
if (!KeyboardInputMap::GetMap(s, key, mod) || isConfig) {
wxString unicodeChar;
unicodeChar.Printf("%d:%d", key, mod);
return unicodeChar;
}
}
return s;
}
wxString wxKeyTextCtrl::ToCandidateString(int mod, int key)
{ {
// wx ignores non-alnum printable chars // wx ignores non-alnum printable chars
// actually, wx gives an assertion error, so it's best to filter out // actually, wx gives an assertion error, so it's best to filter out
// before passing to ToString() // before passing to ToString()
bool char_override = key > 32 && key < WXK_START && !wxIsalnum(key); bool char_override = key > 32 && key < WXK_START && !wxIsalnum(key) && key != WXK_DELETE;
// wx also ignores modifiers (and does not report meta at all) // wx also ignores modifiers (and does not report meta at all)
bool mod_override = key == WXK_SHIFT || key == WXK_CONTROL || key == WXK_ALT || key == WXK_RAW_CONTROL; bool mod_override = key == WXK_SHIFT || key == WXK_CONTROL || key == WXK_ALT || key == WXK_RAW_CONTROL;
wxAcceleratorEntry ae(mod, char_override || mod_override ? WXK_F1 : key); wxAcceleratorEntryUnicode ae(mod, char_override || mod_override ? WXK_F1 : key);
// Note: wx translates unconditionally (2.8.12, 2.9.1)! // Note: wx translates unconditionally (2.8.12, 2.9.1)!
// So any strings added below must also be translated unconditionally // So any strings added below must also be translated unconditionally
wxString s = ae.ToString(); wxString s = ae.ToRawString();
if (char_override || mod_override) { if (char_override || mod_override) {
size_t l = s.rfind(wxT('-')); size_t l = s.rfind(wxT('-'));
@ -150,11 +178,10 @@ wxString wxKeyTextCtrl::ToString(int mod, int key)
s.Replace(display_name_tr, name_tr, true); s.Replace(display_name_tr, name_tr, true);
s.Replace(display_name, name, true); s.Replace(display_name, name, true);
} }
return s; return s;
} }
wxString wxKeyTextCtrl::ToString(wxAcceleratorEntry_v keys, wxChar sep) wxString wxKeyTextCtrl::ToString(wxAcceleratorEntry_v keys, wxChar sep, bool isConfig)
{ {
wxString ret; wxString ret;
@ -162,7 +189,7 @@ wxString wxKeyTextCtrl::ToString(wxAcceleratorEntry_v keys, wxChar sep)
if (i > 0) if (i > 0)
ret += sep; ret += sep;
wxString key = ToString(keys[i].GetFlags(), keys[i].GetKeyCode()); wxString key = ToString(keys[i].GetFlags(), keys[i].GetKeyCode(), isConfig);
if (key.empty()) if (key.empty())
return wxEmptyString; return wxEmptyString;
@ -173,6 +200,21 @@ wxString wxKeyTextCtrl::ToString(wxAcceleratorEntry_v keys, wxChar sep)
return ret; return ret;
} }
static bool checkForPairKeyMod(const wxString& s, int& mod, int& key)
{
long ulkey, ulmod;
// key:mod as pair
auto pair = str_split(s, ":");
if (pair.size() == 2 && pair[0].ToLong(&ulkey) && pair[1].ToLong(&ulmod))
{
key = (int)ulkey;
mod = (int)ulmod;
KeyboardInputMap::AddMap(wxKeyTextCtrl::ToCandidateString(mod, key), key, mod);
return true;
}
return false;
}
bool wxKeyTextCtrl::ParseString(const wxString& s, int len, int& mod, int& key) bool wxKeyTextCtrl::ParseString(const wxString& s, int len, int& mod, int& key)
{ {
mod = key = 0; mod = key = 0;
@ -180,9 +222,15 @@ bool wxKeyTextCtrl::ParseString(const wxString& s, int len, int& mod, int& key)
if (!s || !len) if (!s || !len)
return false; return false;
if (checkForPairKeyMod(s, mod, key))
return true;
if (KeyboardInputMap::GetMap(s, key, mod))
return true;
wxString a = wxT('\t'); wxString a = wxT('\t');
a.Append(s.Left(len)); a.Append(s.Left(len));
wxAcceleratorEntry ae; wxAcceleratorEntryUnicode ae;
#ifndef __WXMAC__ #ifndef __WXMAC__
#define check_meta(str) \ #define check_meta(str) \
do { \ do { \
@ -260,29 +308,12 @@ wxAcceleratorEntry_v wxKeyTextCtrl::FromString(const wxString& s, wxChar sep)
{ {
wxAcceleratorEntry_v ret, empty; wxAcceleratorEntry_v ret, empty;
int mod, key; int mod, key;
size_t len = s.size();
for (size_t lastkey = len - 1; (lastkey = s.rfind(sep, lastkey)) != wxString::npos; lastkey--) { for (const auto& token : str_split_with_sep(s, sep)) {
if (lastkey == len - 1) { if (!ParseString(token, token.size(), mod, key))
// sep as accel
if (!lastkey)
break;
if (s[lastkey - 1] == wxT('-') || s[lastkey - 1] == wxT('+') || s[lastkey - 1] == sep)
continue;
}
if (!ParseString(s.Mid(lastkey + 1), len - lastkey - 1, mod, key))
return empty; return empty;
ret.insert(ret.begin(), wxAcceleratorEntryUnicode(mod, key));
ret.insert(ret.begin(), wxAcceleratorEntry(mod, key));
len = lastkey;
} }
if (!ParseString(s, len, mod, key))
return empty;
ret.insert(ret.begin(), wxAcceleratorEntry(mod, key));
return ret; return ret;
} }

View File

@ -51,9 +51,9 @@ public:
// convert wxSDLJoyEvent's type+val into mod (WXJB_*) // convert wxSDLJoyEvent's type+val into mod (WXJB_*)
static int DigitalButton(wxSDLJoyEvent& event); static int DigitalButton(wxSDLJoyEvent& event);
// convert mod+key to accel string, separated by - // convert mod+key to accel string, separated by -
static wxString ToString(int mod, int key, int joy); static wxString ToString(int mod, int key, int joy, bool isConfig = false);
// convert multiple keys, separated by multikey // convert multiple keys, separated by multikey
static wxString ToString(wxJoyKeyBinding_v keys, wxChar sep = wxT(',')); static wxString ToString(wxJoyKeyBinding_v keys, wxChar sep = wxT(','), bool isConfig = false);
// parses single key string into mod+key // parses single key string into mod+key
static bool FromString(const wxString& s, int& mod, int& key, int& joy); static bool FromString(const wxString& s, int& mod, int& key, int& joy);
// parse multi-key string into array // parse multi-key string into array
@ -61,6 +61,11 @@ public:
static wxJoyKeyBinding_v FromString(const wxString& s, wxChar sep = wxT(',')); static wxJoyKeyBinding_v FromString(const wxString& s, wxChar sep = wxT(','));
// parse a single key in given wxChar array up to given len // parse a single key in given wxChar array up to given len
static bool ParseString(const wxString& s, int len, int& mod, int& key, int& joy); static bool ParseString(const wxString& s, int len, int& mod, int& key, int& joy);
// parse multi-key string into array
// returns empty array on parse errors
static wxAcceleratorEntry_v ToAccelFromString(const wxString& s, wxChar sep = wxT(','));
// convert multiple keys, separated by multikey
static wxString FromAccelToString(wxAcceleratorEntry_v keys, wxChar sep = wxT(','), bool isConfig = false);
protected: protected:
void OnJoy(wxSDLJoyEvent&); void OnJoy(wxSDLJoyEvent&);

View File

@ -8,8 +8,9 @@
#include <vector> #include <vector>
#include <wx/accel.h> #include <wx/accel.h>
#include <wx/textctrl.h> #include <wx/textctrl.h>
#include "wxutil.h"
typedef std::vector<wxAcceleratorEntry> wxAcceleratorEntry_v; typedef std::vector<wxAcceleratorEntryUnicode> wxAcceleratorEntry_v;
class wxKeyTextCtrl : public wxTextCtrl { class wxKeyTextCtrl : public wxTextCtrl {
public: public:
@ -51,9 +52,12 @@ public:
} }
// convert mod+key to accel string, separated by - // convert mod+key to accel string, separated by -
static wxString ToString(int mod, int key); static wxString ToString(int mod, int key, bool isConfig = false);
// convert multiple keys, separated by multikey // convert multiple keys, separated by multikey
static wxString ToString(wxAcceleratorEntry_v keys, wxChar sep = wxT(',')); static wxString ToString(wxAcceleratorEntry_v keys, wxChar sep = wxT(','), bool isConfig = false);
// convert mod+key to candidate accel string, separated by -
// this *should* work, but may fail for unicode chars
static wxString ToCandidateString(int mod, int key);
// parses single key string into mod+key // parses single key string into mod+key
static bool FromString(const wxString& s, int& mod, int& key); static bool FromString(const wxString& s, int& mod, int& key);
// parse multi-key string into accelentry array // parse multi-key string into accelentry array
@ -115,6 +119,7 @@ const struct {
wxString display_name; wxString display_name;
} keys_with_display_names[] = { } keys_with_display_names[] = {
{ WXK_BACK, wxTRANSLATE("Back"), wxTRANSLATE("Backspace") }, { WXK_BACK, wxTRANSLATE("Back"), wxTRANSLATE("Backspace") },
{ WXK_DELETE, wxTRANSLATE("Delete"), wxTRANSLATE("Delete") },
{ WXK_PAGEUP, wxTRANSLATE("PageUp"), wxTRANSLATE("Page Up") }, { WXK_PAGEUP, wxTRANSLATE("PageUp"), wxTRANSLATE("Page Up") },
{ WXK_PAGEDOWN, wxTRANSLATE("PageDown"), wxTRANSLATE("Page Down") }, { WXK_PAGEDOWN, wxTRANSLATE("PageDown"), wxTRANSLATE("Page Down") },
{ WXK_NUMLOCK, wxTRANSLATE("Num_lock"), wxTRANSLATE("Num Lock") }, { WXK_NUMLOCK, wxTRANSLATE("Num_lock"), wxTRANSLATE("Num Lock") },

View File

@ -65,8 +65,11 @@
#include "wx/keyedit.h" #include "wx/keyedit.h"
static inline void DoSetAccel(wxMenuItem* mi, wxAcceleratorEntry* acc) static inline void DoSetAccel(wxMenuItem* mi, wxAcceleratorEntryUnicode* acc)
{ {
// cannot use SDL keybinding as text without wx assertion error
if (!acc || acc->GetJoystick() != 0) return;
wxString lab = mi->GetItemLabel(); wxString lab = mi->GetItemLabel();
size_t tab = lab.find(wxT('\t')); size_t tab = lab.find(wxT('\t'));

84
src/wx/wxutil.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "wxutil.h"
#include "../common/contains.h"
int getKeyboardKeyCode(wxKeyEvent& event)
{
int uc = event.GetUnicodeKey();
if (uc != WXK_NONE) {
if (uc < 32)
return WXK_NONE;
return uc;
}
else {
return event.GetKeyCode();
}
}
wxAcceleratorEntryUnicode::wxAcceleratorEntryUnicode(wxAcceleratorEntry *accel)
: wxAcceleratorEntry(accel->GetFlags(), accel->GetKeyCode(), accel->GetCommand(), accel->GetMenuItem())
{
init(accel->GetFlags(), accel->GetKeyCode());
}
wxAcceleratorEntryUnicode::wxAcceleratorEntryUnicode(int flags, int keyCode, int cmd, wxMenuItem *item)
: wxAcceleratorEntry(flags, keyCode, cmd, item)
{
init(flags, keyCode);
}
wxAcceleratorEntryUnicode::wxAcceleratorEntryUnicode(wxString uKey, int joy, int flags, int keyCode, int cmd, wxMenuItem *item)
: wxAcceleratorEntry(flags, keyCode, cmd, item)
{
ukey = uKey;
joystick = joy;
}
void wxAcceleratorEntryUnicode::Set(wxString uKey, int joy, int flags, int keyCode, int cmd, wxMenuItem *item)
{
ukey = uKey;
joystick = joy;
wxAcceleratorEntry::Set(flags, keyCode, cmd, item);
}
void wxAcceleratorEntryUnicode::init(int flags, int keyCode)
{
joystick = 0;
if (!(flags == 0 && keyCode == 0)) {
ukey.Printf("%d:%d", keyCode, flags);
}
}
KeyboardInputMap* KeyboardInputMap::getInstance()
{
static KeyboardInputMap instance;
return &instance;
}
KeyboardInputMap::KeyboardInputMap(){}
void KeyboardInputMap::AddMap(wxString keyStr, int key, int mod)
{
KeyboardInputMap* singleton = getInstance();
singleton->keysMap[keyStr.wc_str()] = singleton->newPair(key, mod);
}
bool KeyboardInputMap::GetMap(wxString keyStr, int &key, int &mod)
{
KeyboardInputMap* singleton = getInstance();
if (contains(singleton->keysMap, keyStr.wc_str())) {
key = singleton->keysMap.at(keyStr.wc_str()).key;
mod = singleton->keysMap.at(keyStr.wc_str()).mod;
return true;
}
return false;
}

59
src/wx/wxutil.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef _WX_UTIL_H
#define _WX_UTIL_H
#include <wx/event.h>
int getKeyboardKeyCode(wxKeyEvent& event);
#include <wx/accel.h>
class wxAcceleratorEntryUnicode : public wxAcceleratorEntry
{
public:
wxAcceleratorEntryUnicode(wxAcceleratorEntry *accel);
wxAcceleratorEntryUnicode(int flags=0, int keyCode=0, int cmd=0, wxMenuItem *item=nullptr);
wxAcceleratorEntryUnicode(wxString uKey, int joy=0, int flags=0, int keyCode=0, int cmd=0, wxMenuItem *item=nullptr);
void Set(wxString uKey, int joy, int flags, int keyCode, int cmd, wxMenuItem *item=nullptr);
int GetJoystick() const { return joystick; };
wxString GetUkey() const { return ukey; };
private:
void init(int flags, int keyCode);
wxString ukey;
int joystick;
};
#include <unordered_map>
#include <wx/string.h>
#include "widgets/wx/keyedit.h"
class KeyboardInputMap
{
public:
static KeyboardInputMap* getInstance();
static void AddMap(wxString keyStr, int key, int mod);
static bool GetMap(wxString keyStr, int &key, int &mod);
private:
KeyboardInputMap();
// We want to keep track of this pair for
// almost all keypresses.
typedef struct KeyMod {
int key;
int mod;
} KeyMod;
KeyMod newPair(int key, int mod)
{
KeyMod tmp;
tmp.key = key;
tmp.mod = mod;
return tmp;
}
// Map accel string to pair
std::unordered_map<std::wstring, KeyMod> keysMap;
};
#endif

View File

@ -720,6 +720,8 @@ MainFrame::MainFrame()
, dialog_opened(0) , dialog_opened(0)
, focused(false) , focused(false)
{ {
jpoll = new JoystickPoller();
this->Connect(wxID_ANY, wxEVT_SHOW, wxShowEventHandler(JoystickPoller::ShowDialog), jpoll, jpoll);
} }
MainFrame::~MainFrame() MainFrame::~MainFrame()
@ -846,7 +848,7 @@ int MainFrame::FilterEvent(wxEvent& event)
if (event.GetEventType() == wxEVT_KEY_DOWN && !menus_opened && !dialog_opened) if (event.GetEventType() == wxEVT_KEY_DOWN && !menus_opened && !dialog_opened)
{ {
wxKeyEvent& ke = (wxKeyEvent&)event; wxKeyEvent& ke = (wxKeyEvent&)event;
int keyCode = ke.GetKeyCode(); int keyCode = getKeyboardKeyCode(ke);
int keyMod = ke.GetModifiers(); int keyMod = ke.GetModifiers();
wxAcceleratorEntry_v accels = wxGetApp().GetAccels(); wxAcceleratorEntry_v accels = wxGetApp().GetAccels();
for (size_t i = 0; i < accels.size(); ++i) for (size_t i = 0; i < accels.size(); ++i)
@ -858,6 +860,25 @@ int MainFrame::FilterEvent(wxEvent& event)
return true; return true;
} }
} }
else if (event.GetEventType() == wxEVT_SDLJOY && !menus_opened && !dialog_opened)
{
wxSDLJoyEvent& je = (wxSDLJoyEvent&)event;
if (je.GetControlValue() == 0) return -1; // joystick button UP
int key = je.GetControlIndex();
int mod = wxJoyKeyTextCtrl::DigitalButton(je);
int joy = je.GetJoy() + 1;
wxString label = wxJoyKeyTextCtrl::ToString(mod, key, joy);
wxAcceleratorEntry_v accels = wxGetApp().GetAccels();
for (size_t i = 0; i < accels.size(); ++i) {
if (label == accels[i].GetUkey())
{
wxCommandEvent evh(wxEVT_COMMAND_MENU_SELECTED, accels[i].GetCommand());
evh.SetEventObject(this);
GetEventHandler()->ProcessEvent(evh);
return true;
}
}
}
return -1; return -1;
} }
@ -886,31 +907,39 @@ wxString MainFrame::GetGamePath(wxString path)
void MainFrame::SetJoystick() void MainFrame::SetJoystick()
{ {
bool anyjoy = false; /* Remove all attached joysticks to avoid errors while
* destroying and creating the GameArea `panel`. */
joy.Remove(); joy.Remove();
set_global_accels();
if (!emulating) if (!emulating)
return; return;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
for (int j = 0; j < NUM_KEYS; j++) { for (int j = 0; j < NUM_KEYS; j++) {
wxJoyKeyBinding_v b = gopts.joykey_bindings[i][j]; wxJoyKeyBinding_v b = gopts.joykey_bindings[i][j];
for (size_t k = 0; k < b.size(); k++) { for (size_t k = 0; k < b.size(); k++) {
int jn = b[k].joy; int jn = b[k].joy;
if (jn) { if (jn) {
if (!anyjoy) {
anyjoy = true;
joy.Attach(panel);
}
joy.Add(jn - 1); joy.Add(jn - 1);
} }
} }
} }
} }
void MainFrame::StopPoll()
{
if (jpoll && jpoll->IsRunning())
jpoll->Stop();
}
void MainFrame::StartPoll()
{
if (jpoll && !jpoll->IsRunning())
jpoll->Start();
}
void MainFrame::enable_menus() void MainFrame::enable_menus()
{ {
for (int i = 0; i < ncmds; i++) for (int i = 0; i < ncmds; i++)

View File

@ -33,6 +33,7 @@
#include "../gba/Sound.h" #include "../gba/Sound.h"
#include "wxlogdebug.h" #include "wxlogdebug.h"
#include "wxutil.h"
template <typename T> template <typename T>
void CheckPointer(T pointer) void CheckPointer(T pointer)
@ -203,6 +204,8 @@ class GameArea;
class LogDialog; class LogDialog;
class JoystickPoller;
// true if pause should happen at next frame // true if pause should happen at next frame
extern bool pause_next; extern bool pause_next;
@ -320,6 +323,10 @@ public:
void PollJoysticks() { joy.Poll(); } void PollJoysticks() { joy.Poll(); }
// poll joysticks with timer
void StopPoll();
void StartPoll();
// required for building from xrc // required for building from xrc
DECLARE_DYNAMIC_CLASS(MainFrame); DECLARE_DYNAMIC_CLASS(MainFrame);
// required for event handling // required for event handling
@ -345,9 +352,10 @@ private:
checkable_mi_array_t checkable_mi; checkable_mi_array_t checkable_mi;
// recent menu item accels // recent menu item accels
wxMenu* recent; wxMenu* recent;
wxAcceleratorEntry recent_accel[10]; wxAcceleratorEntryUnicode recent_accel[10];
// joystick reader // joystick reader
wxSDLJoy joy; wxSDLJoy joy;
JoystickPoller* jpoll = nullptr;
// helper function for adding menu to accel editor // helper function for adding menu to accel editor
void add_menu_accels(wxTreeCtrl* tc, wxTreeItemId& parent, wxMenu* menu); void add_menu_accels(wxTreeCtrl* tc, wxTreeItemId& parent, wxMenu* menu);
@ -383,6 +391,20 @@ private:
double hidpi_scale_factor; double hidpi_scale_factor;
}; };
// a class for polling joystick keys
class JoystickPoller : public wxTimer {
public:
void Notify() {
wxGetApp().frame->PollJoysticks();
}
void ShowDialog(wxShowEvent& ev) {
if (ev.IsShown())
Start(50);
else
Stop();
}
};
// a helper class to avoid forgetting StopModal() // a helper class to avoid forgetting StopModal()
class ModalPause { class ModalPause {
public: public:

View File

@ -99,7 +99,7 @@
<border>5</border> <border>5</border>
</object> </object>
<object class="sizeritem"> <object class="sizeritem">
<object class="wxTextCtrl" name="Shortcut" subclass="wxKeyTextCtrl"> <object class="wxTextCtrl" name="Shortcut" subclass="wxJoyKeyTextCtrl">
<style>wxTE_PROCESS_ENTER|wxTE_PROCESS_TAB</style> <style>wxTE_PROCESS_ENTER|wxTE_PROCESS_TAB</style>
</object> </object>
<flag>wxALL|wxEXPAND</flag> <flag>wxALL|wxEXPAND</flag>