Skip to content

Commit 37b6a94

Browse files
author
Ben Ten
committed
input: configurable hotkeys
1 parent 8b5cafa commit 37b6a94

File tree

7 files changed

+239
-7
lines changed

7 files changed

+239
-7
lines changed

src/config/CemuConfig.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,13 @@ void CemuConfig::Load(XMLConfigParser& parser)
343343
dsu_client.host = dsuc.get_attribute("host", dsu_client.host);
344344
dsu_client.port = dsuc.get_attribute("port", dsu_client.port);
345345

346+
// hotkeys
347+
auto xml_hotkeys = parser.get("Hotkeys");
348+
hotkeys.exit_fullscreen.raw = xml_hotkeys.get("exit_fullscreen", WXK_ESCAPE);
349+
hotkeys.toggle_fullscreen.raw = xml_hotkeys.get("toggle_fullscreen", WXK_F11);
350+
hotkeys.toggle_fullscreen_alt.raw = xml_hotkeys.get("toggle_fullscreen_alt", uHotkey{WXK_NUMPAD_ENTER, true}.raw);
351+
hotkeys.take_screenshot.raw = xml_hotkeys.get("take_screenshot", WXK_F12);
352+
346353
// emulatedusbdevices
347354
auto usbdevices = parser.get("EmulatedUsbDevices");
348355
emulated_usb_devices.emulate_skylander_portal = usbdevices.get("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal);
@@ -544,6 +551,13 @@ void CemuConfig::Save(XMLConfigParser& parser)
544551
dsuc.set_attribute("host", dsu_client.host);
545552
dsuc.set_attribute("port", dsu_client.port);
546553

554+
// hotkeys
555+
auto xml_hotkeys = config.set("Hotkeys");
556+
xml_hotkeys.set("exit_fullscreen", hotkeys.exit_fullscreen.raw);
557+
xml_hotkeys.set("toggle_fullscreen", hotkeys.toggle_fullscreen.raw);
558+
xml_hotkeys.set("toggle_fullscreen_alt", hotkeys.toggle_fullscreen_alt.raw);
559+
xml_hotkeys.set("take_screenshot", hotkeys.take_screenshot.raw);
560+
547561
// emulated usb devices
548562
auto usbdevices = config.set("EmulatedUsbDevices");
549563
usbdevices.set("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal.GetValue());

src/config/CemuConfig.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,18 @@ enum class CrashDump
191191
ENABLE_ENUM_ITERATORS(CrashDump, CrashDump::Disabled, CrashDump::Enabled);
192192
#endif
193193

194+
typedef union
195+
{
196+
struct
197+
{
198+
uint16_t key : 13; // enough bits for all keycodes
199+
uint16_t alt : 1;
200+
uint16_t ctrl : 1;
201+
uint16_t shift : 1;
202+
};
203+
uint16_t raw;
204+
} uHotkey;
205+
194206
template <>
195207
struct fmt::formatter<const PrecompiledShaderOption> : formatter<string_view> {
196208
template <typename FormatContext>
@@ -499,6 +511,15 @@ struct CemuConfig
499511
ConfigValue<uint16> port{ 26760 };
500512
}dsu_client{};
501513

514+
// hotkeys
515+
struct
516+
{
517+
uHotkey toggle_fullscreen{};
518+
uHotkey toggle_fullscreen_alt{};
519+
uHotkey exit_fullscreen{};
520+
uHotkey take_screenshot{};
521+
} hotkeys{};
522+
502523
// debug
503524
ConfigValueBounds<CrashDump> crash_dump{ CrashDump::Disabled };
504525
ConfigValue<uint16> gdb_port{ 1337 };

src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ add_library(CemuGui
7171
helpers/wxLogEvent.h
7272
helpers/wxWayland.cpp
7373
helpers/wxWayland.h
74+
input/HotkeySettings.cpp
75+
input/HotkeySettings.h
7476
input/InputAPIAddWindow.cpp
7577
input/InputAPIAddWindow.h
7678
input/InputSettings2.cpp

src/gui/CemuApp.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "input/InputManager.h"
1212
#include "gui/helpers/wxHelpers.h"
1313
#include "Cemu/ncrypto/ncrypto.h"
14+
#include "gui/input/HotkeySettings.h"
1415

1516
#if BOOST_OS_LINUX && HAS_WAYLAND
1617
#include "gui/helpers/wxWayland.h"
@@ -331,6 +332,8 @@ bool CemuApp::OnInit()
331332
std::unique_lock lock(g_mutex);
332333
g_window_info.app_active = true;
333334

335+
HotkeySettings::init(m_mainFrame);
336+
334337
SetTopWindow(m_mainFrame);
335338
m_mainFrame->Show();
336339

src/gui/MainWindow.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "gui/helpers/wxHelpers.h"
4141
#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h"
4242
#include "gui/input/InputSettings2.h"
43+
#include "gui/input/HotkeySettings.h"
4344
#include "input/InputManager.h"
4445

4546
#if BOOST_OS_WINDOWS
@@ -91,6 +92,7 @@ enum
9192
MAINFRAME_MENU_ID_OPTIONS_GENERAL2,
9293
MAINFRAME_MENU_ID_OPTIONS_AUDIO,
9394
MAINFRAME_MENU_ID_OPTIONS_INPUT,
95+
MAINFRAME_MENU_ID_OPTIONS_HOTKEY,
9496
// options -> account
9597
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
9698
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
@@ -186,6 +188,7 @@ EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL, MainWindow::OnOptionsInput)
186188
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, MainWindow::OnOptionsInput)
187189
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_AUDIO, MainWindow::OnOptionsInput)
188190
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput)
191+
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_HOTKEY, MainWindow::OnOptionsInput)
189192
// tools menu
190193
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
191194
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
@@ -922,6 +925,12 @@ void MainWindow::OnOptionsInput(wxCommandEvent& event)
922925
break;
923926
}
924927

928+
case MAINFRAME_MENU_ID_OPTIONS_HOTKEY:
929+
{
930+
auto* frame = new HotkeySettings(this);
931+
frame->Show();
932+
break;
933+
}
925934
}
926935
}
927936

@@ -1437,13 +1446,15 @@ void MainWindow::OnKeyUp(wxKeyEvent& event)
14371446
if (swkbd_hasKeyboardInputHook())
14381447
return;
14391448

1440-
const auto code = event.GetKeyCode();
1441-
if (code == WXK_ESCAPE)
1442-
SetFullScreen(false);
1443-
else if (code == WXK_RETURN && event.AltDown() || code == WXK_F11)
1444-
SetFullScreen(!IsFullScreen());
1445-
else if (code == WXK_F12)
1446-
g_window_info.has_screenshot_request = true; // async screenshot request
1449+
uHotkey hotkey{};
1450+
hotkey.key = event.GetKeyCode();
1451+
hotkey.alt = event.AltDown();
1452+
hotkey.ctrl = event.ControlDown();
1453+
hotkey.shift = event.ShiftDown();
1454+
const auto& hotkey_map = HotkeySettings::hotkey_to_func_map;
1455+
const auto it = hotkey_map.find(hotkey.raw);
1456+
if (it != hotkey_map.end())
1457+
it->second();
14471458
}
14481459

14491460
void MainWindow::OnKeyDown(wxKeyEvent& event)
@@ -2159,6 +2170,7 @@ void MainWindow::RecreateMenu()
21592170
optionsMenu->AppendSeparator();
21602171
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, _("&General settings"));
21612172
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings"));
2173+
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_HOTKEY, _("&Hotkey settings"));
21622174

21632175
optionsMenu->AppendSeparator();
21642176
optionsMenu->AppendSubMenu(m_optionsAccountMenu, _("&Active account"));

src/gui/input/HotkeySettings.cpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#include "gui/input/HotkeySettings.h"
2+
#include <unordered_map>
3+
#include <config/ActiveSettings.h>
4+
#include <gui/guiWrapper.h>
5+
6+
extern WindowInfo g_window_info;
7+
wxFrame* s_main_window = nullptr;
8+
9+
static auto& cfg_hotkeys = GetConfig().hotkeys;
10+
static std::unordered_map<uHotkey*, std::function<void(void)>> cfg_hotkey_to_func_map{
11+
{&cfg_hotkeys.toggle_fullscreen, [](void) {s_main_window->ShowFullScreen(!s_main_window->IsFullScreen());}},
12+
{&cfg_hotkeys.toggle_fullscreen_alt, [](void) {s_main_window->ShowFullScreen(!s_main_window->IsFullScreen());}},
13+
{&cfg_hotkeys.exit_fullscreen, [](void) {s_main_window->ShowFullScreen(false);}},
14+
{&cfg_hotkeys.take_screenshot, [](void) {g_window_info.has_screenshot_request = true;}},
15+
};
16+
17+
struct HotkeyEntry
18+
{
19+
std::unique_ptr<wxStaticText> name;
20+
std::unique_ptr<wxButton> key_input;
21+
uHotkey& hotkey;
22+
23+
HotkeyEntry(wxStaticText* name, wxButton* key_input, uHotkey& hotkey)
24+
: name(name), key_input(key_input), hotkey(hotkey) {
25+
key_input->SetClientData(&hotkey);
26+
}
27+
};
28+
29+
HotkeySettings::HotkeySettings(wxWindow* parent)
30+
: wxFrame(parent, wxID_ANY, "Hotkey Settings")
31+
{
32+
m_panel = new wxPanel(this);
33+
m_sizer = new wxFlexGridSizer(0, 2, wxSize(0, 0));
34+
35+
m_panel->SetSizer(m_sizer);
36+
Center();
37+
38+
create_hotkey("Toggle fullscreen", cfg_hotkeys.toggle_fullscreen);
39+
create_hotkey("Take screenshot", cfg_hotkeys.take_screenshot);
40+
}
41+
42+
HotkeySettings::~HotkeySettings()
43+
{
44+
if (m_need_to_save)
45+
{
46+
g_config.Save();
47+
}
48+
}
49+
50+
void HotkeySettings::init(wxFrame* main_window_frame)
51+
{
52+
hotkey_to_func_map.reserve(cfg_hotkey_to_func_map.size());
53+
for (const auto& [cfg_hotkey, func] : cfg_hotkey_to_func_map)
54+
{
55+
auto hotkey_raw = cfg_hotkey->raw;
56+
if (hotkey_raw > 0)
57+
{
58+
hotkey_to_func_map[hotkey_raw] = func;
59+
}
60+
}
61+
s_main_window = main_window_frame;
62+
}
63+
64+
void HotkeySettings::create_hotkey(const wxString& label, uHotkey& hotkey)
65+
{
66+
/* add new hotkey */
67+
{
68+
auto* name = new wxStaticText(m_panel, wxID_ANY, label, wxDefaultPosition, wxSize(240, 30), wxALIGN_CENTER);
69+
auto* key_input = new wxButton(m_panel, wxID_ANY, hotkey_to_wxstring(hotkey), wxDefaultPosition, wxSize(120, 30), wxWANTS_CHARS);
70+
71+
key_input->Bind(wxEVT_BUTTON, &HotkeySettings::on_hotkey_input_click, this);
72+
73+
auto flags = wxSizerFlags().Expand();
74+
m_sizer->Add(name, flags);
75+
m_sizer->Add(key_input, flags);
76+
m_sizer->SetSizeHints(this);
77+
78+
m_hotkeys.emplace_back(name, key_input, hotkey);
79+
}
80+
}
81+
82+
void HotkeySettings::on_hotkey_input_click(wxCommandEvent& event)
83+
{
84+
auto* obj = static_cast<wxButton*>(event.GetEventObject());
85+
obj->Bind(wxEVT_KEY_UP, &HotkeySettings::on_key_up, this);
86+
obj->SetLabelText('_');
87+
}
88+
89+
void HotkeySettings::on_key_up(wxKeyEvent& event)
90+
{
91+
auto* obj = static_cast<wxButton*>(event.GetEventObject());
92+
obj->Unbind(wxEVT_KEY_UP, &HotkeySettings::on_key_up, this);
93+
auto& cfg_hotkey = *static_cast<uHotkey*>(obj->GetClientData());
94+
if (auto keycode = event.GetKeyCode(); is_valid_keycode_up(keycode))
95+
{
96+
auto old_hotkey = cfg_hotkey;
97+
uHotkey new_hotkey{};
98+
new_hotkey.key = keycode;
99+
new_hotkey.alt = event.AltDown();
100+
new_hotkey.ctrl = event.ControlDown();
101+
new_hotkey.shift = event.ShiftDown();
102+
if ((new_hotkey.raw != old_hotkey.raw) &&
103+
(hotkey_to_func_map.find(new_hotkey.raw) == hotkey_to_func_map.end()))
104+
{
105+
m_need_to_save |= true;
106+
cfg_hotkey = new_hotkey;
107+
hotkey_to_func_map.erase(old_hotkey.raw);
108+
hotkey_to_func_map[cfg_hotkey.raw] = cfg_hotkey_to_func_map[&cfg_hotkey];
109+
}
110+
}
111+
obj->SetLabelText(hotkey_to_wxstring(cfg_hotkey));
112+
}
113+
114+
bool HotkeySettings::is_valid_keycode_up(int keycode)
115+
{
116+
switch (keycode)
117+
{
118+
case WXK_ESCAPE:
119+
case WXK_ALT:
120+
case WXK_CONTROL:
121+
case WXK_SHIFT:
122+
return false;
123+
default:
124+
return true;
125+
}
126+
}
127+
128+
wxString HotkeySettings::hotkey_to_wxstring(uHotkey hotkey)
129+
{
130+
if (hotkey.raw <= 0)
131+
{
132+
return "";
133+
}
134+
wxString ret_val;
135+
if (hotkey.alt)
136+
{
137+
ret_val.append("ALT + ");
138+
}
139+
if (hotkey.ctrl)
140+
{
141+
ret_val.append("CTRL + ");
142+
}
143+
if (hotkey.shift)
144+
{
145+
ret_val.append("SHIFT + ");
146+
}
147+
ret_val.append(wxAcceleratorEntry(0, hotkey.key).ToString());
148+
return ret_val;
149+
}

src/gui/input/HotkeySettings.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
3+
#include <wx/wx.h>
4+
#include "config/CemuConfig.h"
5+
6+
class HotkeyEntry;
7+
8+
class HotkeySettings : public wxFrame
9+
{
10+
public:
11+
static void init(wxFrame* main_window_frame);
12+
inline static std::unordered_map<int, std::function<void(void)>> hotkey_to_func_map{};
13+
14+
HotkeySettings(wxWindow* parent);
15+
~HotkeySettings();
16+
17+
private:
18+
wxPanel* m_panel;
19+
wxFlexGridSizer* m_sizer;
20+
std::vector<HotkeyEntry> m_hotkeys;
21+
bool m_need_to_save = false;
22+
23+
/* helpers */
24+
void create_hotkey(const wxString& label, uHotkey& hotkey);
25+
wxString hotkey_to_wxstring(uHotkey hotkey);
26+
bool is_valid_keycode_up(int keycode);
27+
28+
/* events */
29+
void on_hotkey_input_click(wxCommandEvent& event);
30+
void on_key_up(wxKeyEvent& event);
31+
};

0 commit comments

Comments
 (0)