1 // Copyright 2015 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Core/HotkeyManager.h"
6 
7 #include <algorithm>
8 #include <array>
9 #include <string>
10 #include <vector>
11 
12 #include <fmt/format.h>
13 
14 #include "Common/Common.h"
15 #include "Common/CommonTypes.h"
16 #include "Common/FileUtil.h"
17 #include "Common/IniFile.h"
18 #include "Common/StringUtil.h"
19 
20 #include "InputCommon/ControllerEmu/Control/Input.h"
21 #include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
22 #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
23 #include "InputCommon/ControllerInterface/ControllerInterface.h"
24 #include "InputCommon/GCPadStatus.h"
25 
26 // clang-format off
27 constexpr std::array<const char*, 138> s_hotkey_labels{{
28     _trans("Open"),
29     _trans("Change Disc"),
30     _trans("Eject Disc"),
31     _trans("Refresh Game List"),
32     _trans("Toggle Pause"),
33     _trans("Stop"),
34     _trans("Reset"),
35     _trans("Toggle Fullscreen"),
36     _trans("Take Screenshot"),
37     _trans("Exit"),
38     _trans("Activate NetPlay Chat"),
39     _trans("Control NetPlay Golf Mode"),
40 
41     _trans("Volume Down"),
42     _trans("Volume Up"),
43     _trans("Volume Toggle Mute"),
44 
45     _trans("Decrease Emulation Speed"),
46     _trans("Increase Emulation Speed"),
47     _trans("Disable Emulation Speed Limit"),
48 
49     _trans("Frame Advance"),
50     _trans("Frame Advance Decrease Speed"),
51     _trans("Frame Advance Increase Speed"),
52     _trans("Frame Advance Reset Speed"),
53 
54     _trans("Start Recording"),
55     _trans("Play Recording"),
56     _trans("Export Recording"),
57     _trans("Read-Only Mode"),
58 
59     // i18n: Here, "Step" is a verb. This feature is used for
60     // going through code step by step.
61     _trans("Step Into"),
62     // i18n: Here, "Step" is a verb. This feature is used for
63     // going through code step by step.
64     _trans("Step Over"),
65     // i18n: Here, "Step" is a verb. This feature is used for
66     // going through code step by step.
67     _trans("Step Out"),
68     _trans("Skip"),
69 
70     // i18n: Here, PC is an acronym for program counter, not personal computer.
71     _trans("Show PC"),
72     // i18n: Here, PC is an acronym for program counter, not personal computer.
73     _trans("Set PC"),
74 
75     _trans("Toggle Breakpoint"),
76     _trans("Add a Breakpoint"),
77     _trans("Add a Memory Breakpoint"),
78 
79     _trans("Press Sync Button"),
80     _trans("Connect Wii Remote 1"),
81     _trans("Connect Wii Remote 2"),
82     _trans("Connect Wii Remote 3"),
83     _trans("Connect Wii Remote 4"),
84     _trans("Connect Balance Board"),
85     _trans("Toggle SD Card"),
86     _trans("Toggle USB Keyboard"),
87 
88     _trans("Next Profile"),
89     _trans("Previous Profile"),
90     _trans("Next Game Profile"),
91     _trans("Previous Game Profile"),
92     _trans("Next Profile"),
93     _trans("Previous Profile"),
94     _trans("Next Game Profile"),
95     _trans("Previous Game Profile"),
96     _trans("Next Profile"),
97     _trans("Previous Profile"),
98     _trans("Next Game Profile"),
99     _trans("Previous Game Profile"),
100     _trans("Next Profile"),
101     _trans("Previous Profile"),
102     _trans("Next Game Profile"),
103     _trans("Previous Game Profile"),
104 
105     _trans("Toggle Crop"),
106     _trans("Toggle Aspect Ratio"),
107     _trans("Toggle EFB Copies"),
108     _trans("Toggle XFB Copies"),
109     _trans("Toggle XFB Immediate Mode"),
110     _trans("Toggle Fog"),
111     _trans("Toggle Texture Dumping"),
112     _trans("Toggle Custom Textures"),
113 
114     // i18n: IR stands for internal resolution
115     _trans("Increase IR"),
116     // i18n: IR stands for internal resolution
117     _trans("Decrease IR"),
118 
119     _trans("Freelook Decrease Speed"),
120     _trans("Freelook Increase Speed"),
121     _trans("Freelook Reset Speed"),
122     _trans("Freelook Move Up"),
123     _trans("Freelook Move Down"),
124     _trans("Freelook Move Left"),
125     _trans("Freelook Move Right"),
126     _trans("Freelook Zoom In"),
127     _trans("Freelook Zoom Out"),
128     _trans("Freelook Reset"),
129     _trans("Freelook Toggle"),
130     _trans("Freelook Increase Field of View X"),
131     _trans("Freelook Decrease Field of View X"),
132     _trans("Freelook Increase Field of View Y"),
133     _trans("Freelook Decrease Field of View Y"),
134 
135     _trans("Toggle 3D Side-by-Side"),
136     _trans("Toggle 3D Top-Bottom"),
137     _trans("Toggle 3D Anaglyph"),
138     _trans("Decrease Depth"),
139     _trans("Increase Depth"),
140     _trans("Decrease Convergence"),
141     _trans("Increase Convergence"),
142 
143     _trans("Load State Slot 1"),
144     _trans("Load State Slot 2"),
145     _trans("Load State Slot 3"),
146     _trans("Load State Slot 4"),
147     _trans("Load State Slot 5"),
148     _trans("Load State Slot 6"),
149     _trans("Load State Slot 7"),
150     _trans("Load State Slot 8"),
151     _trans("Load State Slot 9"),
152     _trans("Load State Slot 10"),
153     _trans("Load from Selected Slot"),
154 
155     _trans("Save State Slot 1"),
156     _trans("Save State Slot 2"),
157     _trans("Save State Slot 3"),
158     _trans("Save State Slot 4"),
159     _trans("Save State Slot 5"),
160     _trans("Save State Slot 6"),
161     _trans("Save State Slot 7"),
162     _trans("Save State Slot 8"),
163     _trans("Save State Slot 9"),
164     _trans("Save State Slot 10"),
165     _trans("Save to Selected Slot"),
166 
167     _trans("Select State Slot 1"),
168     _trans("Select State Slot 2"),
169     _trans("Select State Slot 3"),
170     _trans("Select State Slot 4"),
171     _trans("Select State Slot 5"),
172     _trans("Select State Slot 6"),
173     _trans("Select State Slot 7"),
174     _trans("Select State Slot 8"),
175     _trans("Select State Slot 9"),
176     _trans("Select State Slot 10"),
177 
178     _trans("Load State Last 1"),
179     _trans("Load State Last 2"),
180     _trans("Load State Last 3"),
181     _trans("Load State Last 4"),
182     _trans("Load State Last 5"),
183     _trans("Load State Last 6"),
184     _trans("Load State Last 7"),
185     _trans("Load State Last 8"),
186     _trans("Load State Last 9"),
187     _trans("Load State Last 10"),
188 
189     _trans("Save Oldest State"),
190     _trans("Undo Load State"),
191     _trans("Undo Save State"),
192     _trans("Save State"),
193     _trans("Load State"),
194 }};
195 // clang-format on
196 static_assert(NUM_HOTKEYS == s_hotkey_labels.size(), "Wrong count of hotkey_labels");
197 
198 namespace HotkeyManagerEmu
199 {
200 static std::array<u32, NUM_HOTKEY_GROUPS> s_hotkey_down;
201 static HotkeyStatus s_hotkey;
202 static bool s_enabled;
203 
204 static InputConfig s_config("Hotkeys", _trans("Hotkeys"), "Hotkeys");
205 
GetConfig()206 InputConfig* GetConfig()
207 {
208   return &s_config;
209 }
210 
GetStatus()211 void GetStatus()
212 {
213   // Get input
214   static_cast<HotkeyManager*>(s_config.GetController(0))->GetInput(&s_hotkey);
215 }
216 
IsEnabled()217 bool IsEnabled()
218 {
219   return s_enabled;
220 }
221 
Enable(bool enable_toggle)222 void Enable(bool enable_toggle)
223 {
224   s_enabled = enable_toggle;
225 }
226 
IsPressed(int id,bool held)227 bool IsPressed(int id, bool held)
228 {
229   unsigned int group = static_cast<HotkeyManager*>(s_config.GetController(0))->FindGroupByID(id);
230   unsigned int group_key =
231       static_cast<HotkeyManager*>(s_config.GetController(0))->GetIndexForGroup(group, id);
232   if (s_hotkey.button[group] & (1 << group_key))
233   {
234     const bool pressed = !!(s_hotkey_down[group] & (1 << group_key));
235     s_hotkey_down[group] |= (1 << group_key);
236     if (!pressed || held)
237       return true;
238   }
239   else
240   {
241     s_hotkey_down[group] &= ~(1 << group_key);
242   }
243 
244   return false;
245 }
246 
247 // This function exists to load the old "Keys" group so pre-existing configs don't break.
248 // TODO: Remove this at a future date when we're confident most configs are migrated.
LoadLegacyConfig(ControllerEmu::EmulatedController * controller)249 static void LoadLegacyConfig(ControllerEmu::EmulatedController* controller)
250 {
251   IniFile inifile;
252   if (inifile.Load(File::GetUserPath(D_CONFIG_IDX) + "Hotkeys.ini"))
253   {
254     if (!inifile.Exists("Hotkeys") && inifile.Exists("Hotkeys1"))
255     {
256       auto sec = inifile.GetOrCreateSection("Hotkeys1");
257 
258       {
259         std::string defdev;
260         sec->Get("Device", &defdev, "");
261         controller->SetDefaultDevice(defdev);
262       }
263 
264       for (auto& group : controller->groups)
265       {
266         for (auto& control : group->controls)
267         {
268           std::string key("Keys/" + control->name);
269 
270           if (sec->Exists(key))
271           {
272             std::string expression;
273             sec->Get(key, &expression, "");
274             control->control_ref->SetExpression(std::move(expression));
275           }
276         }
277       }
278 
279       controller->UpdateReferences(g_controller_interface);
280     }
281   }
282 }
283 
Initialize()284 void Initialize()
285 {
286   if (s_config.ControllersNeedToBeCreated())
287     s_config.CreateController<HotkeyManager>();
288 
289   s_config.RegisterHotplugCallback();
290 
291   // load the saved controller config
292   LoadConfig();
293 
294   s_hotkey_down = {};
295 
296   s_enabled = true;
297 }
298 
LoadConfig()299 void LoadConfig()
300 {
301   s_config.LoadConfig(true);
302   LoadLegacyConfig(s_config.GetController(0));
303 }
304 
GetHotkeyGroup(HotkeyGroup group)305 ControllerEmu::ControlGroup* GetHotkeyGroup(HotkeyGroup group)
306 {
307   return static_cast<HotkeyManager*>(s_config.GetController(0))->GetHotkeyGroup(group);
308 }
309 
Shutdown()310 void Shutdown()
311 {
312   s_config.UnregisterHotplugCallback();
313 
314   s_config.ClearControllers();
315 }
316 }  // namespace HotkeyManagerEmu
317 
318 struct HotkeyGroupInfo
319 {
320   const char* name;
321   Hotkey first;
322   Hotkey last;
323 };
324 
325 constexpr std::array<HotkeyGroupInfo, NUM_HOTKEY_GROUPS> s_groups_info = {
326     {{_trans("General"), HK_OPEN, HK_REQUEST_GOLF_CONTROL},
327      {_trans("Volume"), HK_VOLUME_DOWN, HK_VOLUME_TOGGLE_MUTE},
328      {_trans("Emulation Speed"), HK_DECREASE_EMULATION_SPEED, HK_TOGGLE_THROTTLE},
329      {_trans("Frame Advance"), HK_FRAME_ADVANCE, HK_FRAME_ADVANCE_RESET_SPEED},
330      {_trans("Movie"), HK_START_RECORDING, HK_READ_ONLY_MODE},
331      {_trans("Stepping"), HK_STEP, HK_SKIP},
332      {_trans("Program Counter"), HK_SHOW_PC, HK_SET_PC},
333      {_trans("Breakpoint"), HK_BP_TOGGLE, HK_MBP_ADD},
334      {_trans("Wii"), HK_TRIGGER_SYNC_BUTTON, HK_TOGGLE_USB_KEYBOARD},
335      {_trans("Controller Profile 1"), HK_NEXT_WIIMOTE_PROFILE_1, HK_PREV_GAME_WIIMOTE_PROFILE_1},
336      {_trans("Controller Profile 2"), HK_NEXT_WIIMOTE_PROFILE_2, HK_PREV_GAME_WIIMOTE_PROFILE_2},
337      {_trans("Controller Profile 3"), HK_NEXT_WIIMOTE_PROFILE_3, HK_PREV_GAME_WIIMOTE_PROFILE_3},
338      {_trans("Controller Profile 4"), HK_NEXT_WIIMOTE_PROFILE_4, HK_PREV_GAME_WIIMOTE_PROFILE_4},
339      {_trans("Graphics Toggles"), HK_TOGGLE_CROP, HK_TOGGLE_TEXTURES},
340      {_trans("Internal Resolution"), HK_INCREASE_IR, HK_DECREASE_IR},
341      {_trans("Freelook"), HK_FREELOOK_DECREASE_SPEED, HK_FREELOOK_DECREASE_FOV_Y},
342      // i18n: Stereoscopic 3D
343      {_trans("3D"), HK_TOGGLE_STEREO_SBS, HK_TOGGLE_STEREO_ANAGLYPH},
344      // i18n: Stereoscopic 3D
345      {_trans("3D Depth"), HK_DECREASE_DEPTH, HK_INCREASE_CONVERGENCE},
346      {_trans("Load State"), HK_LOAD_STATE_SLOT_1, HK_LOAD_STATE_SLOT_SELECTED},
347      {_trans("Save State"), HK_SAVE_STATE_SLOT_1, HK_SAVE_STATE_SLOT_SELECTED},
348      {_trans("Select State"), HK_SELECT_STATE_SLOT_1, HK_SELECT_STATE_SLOT_10},
349      {_trans("Load Last State"), HK_LOAD_LAST_STATE_1, HK_LOAD_LAST_STATE_10},
350      {_trans("Other State Hotkeys"), HK_SAVE_FIRST_STATE, HK_LOAD_STATE_FILE}}};
351 
HotkeyManager()352 HotkeyManager::HotkeyManager()
353 {
354   for (std::size_t group = 0; group < m_hotkey_groups.size(); group++)
355   {
356     m_hotkey_groups[group] =
357         (m_keys[group] = new ControllerEmu::Buttons(s_groups_info[group].name));
358     groups.emplace_back(m_hotkey_groups[group]);
359     for (int key = s_groups_info[group].first; key <= s_groups_info[group].last; key++)
360     {
361       m_keys[group]->AddInput(ControllerEmu::Translate, s_hotkey_labels[key]);
362     }
363   }
364 }
365 
~HotkeyManager()366 HotkeyManager::~HotkeyManager()
367 {
368 }
369 
GetName() const370 std::string HotkeyManager::GetName() const
371 {
372   return "Hotkeys";
373 }
374 
GetInput(HotkeyStatus * const kb)375 void HotkeyManager::GetInput(HotkeyStatus* const kb)
376 {
377   const auto lock = GetStateLock();
378   for (std::size_t group = 0; group < s_groups_info.size(); group++)
379   {
380     const int group_count = (s_groups_info[group].last - s_groups_info[group].first) + 1;
381     std::vector<u32> bitmasks(group_count);
382     for (size_t key = 0; key < bitmasks.size(); key++)
383       bitmasks[key] = static_cast<u32>(1 << key);
384 
385     kb->button[group] = 0;
386     m_keys[group]->GetState(&kb->button[group], bitmasks.data());
387   }
388 }
389 
GetHotkeyGroup(HotkeyGroup group) const390 ControllerEmu::ControlGroup* HotkeyManager::GetHotkeyGroup(HotkeyGroup group) const
391 {
392   return m_hotkey_groups[group];
393 }
394 
FindGroupByID(int id) const395 int HotkeyManager::FindGroupByID(int id) const
396 {
397   const auto i = std::find_if(s_groups_info.begin(), s_groups_info.end(),
398                               [id](const auto& entry) { return entry.last >= id; });
399 
400   return static_cast<int>(std::distance(s_groups_info.begin(), i));
401 }
402 
GetIndexForGroup(int group,int id) const403 int HotkeyManager::GetIndexForGroup(int group, int id) const
404 {
405   return id - s_groups_info[group].first;
406 }
407 
LoadDefaults(const ControllerInterface & ciface)408 void HotkeyManager::LoadDefaults(const ControllerInterface& ciface)
409 {
410   EmulatedController::LoadDefaults(ciface);
411 
412   auto set_key_expression = [this](int index, const std::string& expression) {
413     m_keys[FindGroupByID(index)]
414         ->controls[GetIndexForGroup(FindGroupByID(index), index)]
415         ->control_ref->SetExpression(expression);
416   };
417 
418   auto hotkey_string = [](std::vector<std::string> inputs) {
419     return "@(" + JoinStrings(inputs, "+") + ')';
420   };
421 
422   // General hotkeys
423   set_key_expression(HK_OPEN, hotkey_string({"Ctrl", "O"}));
424   set_key_expression(HK_PLAY_PAUSE, "F10");
425 #ifdef _WIN32
426   set_key_expression(HK_STOP, "ESCAPE");
427   set_key_expression(HK_FULLSCREEN, hotkey_string({"Alt", "RETURN"}));
428 #else
429   set_key_expression(HK_STOP, "Escape");
430   set_key_expression(HK_FULLSCREEN, hotkey_string({"Alt", "Return"}));
431 #endif
432   set_key_expression(HK_STEP, "F11");
433   set_key_expression(HK_STEP_OVER, hotkey_string({"Shift", "F10"}));
434   set_key_expression(HK_STEP_OUT, hotkey_string({"Shift", "F11"}));
435   set_key_expression(HK_BP_TOGGLE, hotkey_string({"Shift", "F9"}));
436   set_key_expression(HK_SCREENSHOT, "F9");
437   set_key_expression(HK_WIIMOTE1_CONNECT, hotkey_string({"Alt", "F5"}));
438   set_key_expression(HK_WIIMOTE2_CONNECT, hotkey_string({"Alt", "F6"}));
439   set_key_expression(HK_WIIMOTE3_CONNECT, hotkey_string({"Alt", "F7"}));
440   set_key_expression(HK_WIIMOTE4_CONNECT, hotkey_string({"Alt", "F8"}));
441   set_key_expression(HK_BALANCEBOARD_CONNECT, hotkey_string({"Alt", "F9"}));
442 #ifdef _WIN32
443   set_key_expression(HK_TOGGLE_THROTTLE, "TAB");
444 #else
445   set_key_expression(HK_TOGGLE_THROTTLE, "Tab");
446 #endif
447 
448   // Freelook
449   set_key_expression(HK_FREELOOK_DECREASE_SPEED, hotkey_string({"Shift", "`1`"}));
450   set_key_expression(HK_FREELOOK_INCREASE_SPEED, hotkey_string({"Shift", "`2`"}));
451   set_key_expression(HK_FREELOOK_RESET_SPEED, hotkey_string({"Shift", "F"}));
452   set_key_expression(HK_FREELOOK_UP, hotkey_string({"Shift", "E"}));
453   set_key_expression(HK_FREELOOK_DOWN, hotkey_string({"Shift", "Q"}));
454   set_key_expression(HK_FREELOOK_LEFT, hotkey_string({"Shift", "A"}));
455   set_key_expression(HK_FREELOOK_RIGHT, hotkey_string({"Shift", "D"}));
456   set_key_expression(HK_FREELOOK_ZOOM_IN, hotkey_string({"Shift", "W"}));
457   set_key_expression(HK_FREELOOK_ZOOM_OUT, hotkey_string({"Shift", "S"}));
458   set_key_expression(HK_FREELOOK_RESET, hotkey_string({"Shift", "R"}));
459   set_key_expression(HK_FREELOOK_INCREASE_FOV_X, hotkey_string({"Shift", "`Axis Z+`"}));
460   set_key_expression(HK_FREELOOK_DECREASE_FOV_X, hotkey_string({"Shift", "`Axis Z-`"}));
461   set_key_expression(HK_FREELOOK_INCREASE_FOV_Y, hotkey_string({"Shift", "`Axis Z+`"}));
462   set_key_expression(HK_FREELOOK_DECREASE_FOV_Y, hotkey_string({"Shift", "`Axis Z-`"}));
463 
464   // Savestates
465   for (int i = 0; i < 8; i++)
466   {
467     set_key_expression(HK_LOAD_STATE_SLOT_1 + i, fmt::format("F{}", i + 1));
468     set_key_expression(HK_SAVE_STATE_SLOT_1 + i,
469                        hotkey_string({"Shift", fmt::format("F{}", i + 1)}));
470   }
471   set_key_expression(HK_UNDO_LOAD_STATE, "F12");
472   set_key_expression(HK_UNDO_SAVE_STATE, hotkey_string({"Shift", "F12"}));
473 }
474