Skip to content

Commit 64a34e3

Browse files
author
Ben Ten
committed
input: configurable hotkeys
1 parent 8b5cafa commit 64a34e3

File tree

7 files changed

+249
-7
lines changed

7 files changed

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

src/gui/input/HotkeySettings.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
wxButton* active_hotkey_input_btn = nullptr;
22+
bool m_need_to_save = false;
23+
24+
/* helpers */
25+
void create_hotkey(const wxString& label, uHotkey& hotkey);
26+
wxString hotkey_to_wxstring(uHotkey hotkey);
27+
bool is_valid_keycode_up(int keycode);
28+
29+
/* events */
30+
void on_hotkey_input_click(wxCommandEvent& event);
31+
void on_key_up(wxKeyEvent& event);
32+
};

0 commit comments

Comments
 (0)