Chesto 0.9
A declarative and element-based library for creating GUIs on homebrew'd consoles
InputEvents.cpp
1#include "InputEvents.hpp"
2#include "RootDisplay.hpp"
3#include <map>
4
5// computer key mappings
6CST_Keycode key_buttons[] = { SDLK_a, SDLK_b, SDLK_x, SDLK_y, SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, SDLK_RETURN, SDLK_l, SDLK_r, SDLK_z, SDLK_BACKSPACE, SDLK_UP, SDLK_DOWN, SDLK_q };
7
8SDL_GameControllerButton pad_buttons[] = { SDL_A, SDL_B, SDL_X, SDL_Y, SDL_UP, SDL_DOWN, SDL_LEFT, SDL_RIGHT, SDL_PLUS, SDL_L, SDL_R, SDL_ZL, SDL_MINUS, SDL_UP_STICK, SDL_DOWN_STICK, SDL_LEFT_STICK, SDL_RIGHT_STICK, SDL_ZR, };
9
10#if defined(__WIIU__) && defined(USE_KEYBOARD)
11#include "../libs/wiiu_kbd/keybdwrapper.h"
12#endif
13
14// our own "buttons" that correspond to the above SDL ones
15unsigned int nintendo_buttons[] = { A_BUTTON, B_BUTTON, X_BUTTON, Y_BUTTON, UP_BUTTON, DOWN_BUTTON, LEFT_BUTTON, RIGHT_BUTTON, START_BUTTON, L_BUTTON, R_BUTTON, ZL_BUTTON, SELECT_BUTTON, UP_BUTTON, DOWN_BUTTON, LEFT_BUTTON, RIGHT_BUTTON, ZR_BUTTON };
16
17// human readable lowercase names for the buttons (used by the UI)
18std::string nintendoButtonNames[] = { "a", "b", "x", "y", "up", "down", "left", "right", "plus", "l", "r", "zl", "minus", "up", "down", "left", "right", "zr" };
19
20// human readable lowercase keyboard buttons
21std::string keyButtonNames[] = { "a", "b", "x", "y", "up", "down", "left", "right", "return", "l", "r", "z", "backspace", "up", "down", "left", "right", "q" };
22
23// wii remote (alone) buttons, a smaller set of actions
24// (buttons that aren't available will need to be pressed using IR sensor)
25unsigned int wii_buttons[] = { A_BUTTON, B_BUTTON, L_BUTTON, R_BUTTON, UP_BUTTON, DOWN_BUTTON, LEFT_BUTTON, RIGHT_BUTTON, START_BUTTON, 0, 0, 0, SELECT_BUTTON, 0, 0, 0, 0, 0 };
26
27std::string wiiButtonNames[] = { "a", "b", "1", "2", "up", "down", "left", "right", "plus", "", "", "", "minus", "", "", "", "", "" };
28
29// wii remote and nunchuk, separate and more actions (but still not all) available
30unsigned int nunchuk_buttons[] = { A_BUTTON, B_BUTTON, L_BUTTON, R_BUTTON, UP_BUTTON, DOWN_BUTTON, LEFT_BUTTON, RIGHT_BUTTON, START_BUTTON, 0, 0, X_BUTTON, SELECT_BUTTON, UP_BUTTON, DOWN_BUTTON, LEFT_BUTTON, RIGHT_BUTTON, Y_BUTTON };
31
32std::string nunchukButtonNames[] = { "a", "b", "1", "2", "up", "down", "left", "right", "plus", "", "", "z", "minus", "up", "down", "left", "right", "c" };
33
34// if true, don't count key inputs (PC/usb keyboard) as button events for us
35bool InputEvents::bypassKeyEvents = false;
36
37auto defaultKeyName = "WiiU Gamepad";
38std::string InputEvents::lastGamepadKey = defaultKeyName;
39unsigned int* currentButtons = nintendo_buttons;
40std::string* currentButtonNames = nintendoButtonNames;
41
42// map of controller name to buttons, names, prefix, and controller type
43std::map<std::string, GamepadInfo> gamepadMap = {
44 /* Non-controller types, like keyboard */
45 { "Keyboard", GamepadInfo(nintendo_buttons, keyButtonNames, "keyboard", "key") },
46 /* These 5 names are returned by the wiiu SDL2 port */
47 { "WiiU Gamepad", GamepadInfo(nintendo_buttons, nintendoButtonNames, "wiiu_button", "gamepad") },
48 { "WiiU Pro Controller", { nintendo_buttons, nintendoButtonNames, "wiiu_button", "pro" } },
49 { "Wii Remote", { wii_buttons, wiiButtonNames, "wii_button", "remote" } },
50 { "Wii Remote and Nunchuk", { nunchuk_buttons, nunchukButtonNames, "wii_button", "nunchuk" } },
51 { "Wii Classic Controller", { nintendo_buttons, nintendoButtonNames, "wii_button", "classic"} },
52 /* The switch SDL2 port only returns this string for all controller types*/
53 { "Switch Controller", { nintendo_buttons, nintendoButtonNames, "switch_button", "pro" } },
54 /* For PC platforms, more specific Switch controller types can be recognized */
55 // { "Pro Controller", { nintendo_buttons, nintendoButtonNames, "switch" } },
56 // { "Joy-Con (L)", { nintendo_buttons, nintendoButtonNames, "switch" } },
57 // { "Joy-Con (R)", { nintendo_buttons, nintendoButtonNames, "switch" } },
58 // { "Switch Pro Controller", { nintendo_buttons, nintendoButtonNames, "switch" } },
59 /* Other controller types */
60 // { "Xbox 360 Controller", { xbox_buttons, xboxButtonNames, "xbox" } },
61 // { "Xbox One Controller", { xbox_buttons, xboxButtonNames, "xbox" } },
62 // { "Xbox Series X Controller", { xbox_buttons, xboxButtonNames, "xbox" } },
63 // { "PS4 Controller", { ps_buttons, psButtonNames, "playstation" } },
64 // { "PS5 Controller", { ps_buttons, psButtonNames, "playstation" } }
65};
66
67InputEvents::InputEvents()
68{
69#if defined(__WIIU__) && defined(USE_KEYBOARD)
70 // hook up keyboard events for wiiu and SDL (TODO: have these fired by SDL2 port itself)
71 KBWrapper* kbdwrapper = new KBWrapper(true, true);
72#endif
73}
74
76{
77 // get an event from SDL
78 if (!SDL_PollEvent(&event))
79 return false;
80
81 // update our variables
82 this->type = event.type;
83 this->noop = false;
84
85 // process joystick hotplugging events
87
88 std::string curControllerName= lastGamepadKey;
89
90 // get the controller name
91 if (this->type == SDL_KEYDOWN || this->type == SDL_KEYUP) {
92 // keyboard event
93 lastGamepadKey = "Keyboard";
94 } else if (this->type == SDL_JOYBUTTONDOWN || this->type == SDL_JOYBUTTONUP) {
95 SDL_Joystick* joystickId = SDL_JoystickFromInstanceID(event.jbutton.which);
96 if (joystickId != NULL) {
97 std::string controllerName = SDL_JoystickName(joystickId);
98 lastGamepadKey = defaultKeyName; // default in case no match is found
99 if (!controllerName.empty() && gamepadMap.find(controllerName) != gamepadMap.end()){
100 lastGamepadKey = controllerName;
101 }
102 }
103 }
104 if (curControllerName != lastGamepadKey) {
105 printf("Switched to controller profile: %s\n", lastGamepadKey.c_str());
106 GamepadInfo& gamepadInfo = gamepadMap[lastGamepadKey];
107 if (gamepadInfo.buttons != nullptr) {
108 currentButtons = gamepadInfo.buttons;
109 }
110 // keyButtonNames = gamepadInfo.names;
111 // TODO: callback to update all buttons on the UI
112 }
113
114 this->isScrolling = false;
115
116#ifdef PC
117 this->allowTouch = false;
118 if (event.type == SDL_MOUSEWHEEL) {
119 this->wheelScroll = event.wheel.y;
120 this->isScrolling = true;
121 }
122#endif
123
124 if (this->type == SDL_QUIT)
125 {
126 if (this->quitaction != NULL) this->quitaction();
127 return false; //Quitting overrides all other events.
128 }
129 else if (event.key.repeat == 0 && (this->type == SDL_KEYDOWN || this->type == SDL_KEYUP))
130 {
131 this->keyCode = event.key.keysym.sym;
132 this->mod = event.key.keysym.mod;
133 }
134 else if (this->type == SDL_JOYBUTTONDOWN || this->type == SDL_JOYBUTTONUP)
135 {
136 this->keyCode = event.jbutton.button;
137 }
138 else if (this->type == SDL_MOUSEMOTION || this->type == SDL_MOUSEBUTTONUP || this->type == SDL_MOUSEBUTTONDOWN)
139 {
140 bool isMotion = this->type == SDL_MOUSEMOTION;
141
142 this->yPos = isMotion ? event.motion.y : event.button.y;
143 this->xPos = isMotion ? event.motion.x : event.button.x;
144 }
145 else if (allowTouch && (this->type == SDL_FINGERMOTION || this->type == SDL_FINGERUP || this->type == SDL_FINGERDOWN))
146 {
147 this->yPos = event.tfinger.y * SCREEN_HEIGHT;
148 this->xPos = event.tfinger.x * SCREEN_WIDTH;
149 }
150
151 if (this->type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
152 printf("Window resized to %dx%d\n", event.window.data1, event.window.data2);
153 RootDisplay::mainDisplay->setScreenResolution(
154 event.window.data1 * RootDisplay::dpiScale,
155 event.window.data2 * RootDisplay::dpiScale
156 );
157
158 // callback to alert the app that the window changed resolution
159 if (RootDisplay::mainDisplay->windowResizeCallback)
160 RootDisplay::mainDisplay->windowResizeCallback();
161
162 RootDisplay::mainDisplay->needsRedraw = true;
163 }
164
165 // offset the x, y positions by the dpi scale
166 this->xPos = (int)(this->xPos * RootDisplay::dpiScale);
167 this->yPos = (int)(this->yPos * RootDisplay::dpiScale);
168
169 toggleHeldButtons();
170
171 return true;
172}
173
174bool InputEvents::update()
175{
176 this->type = 0;
177 this->keyCode = -1;
178 this->noop = true;
179
180 // process SDL or directional events
181 return processSDLEvents() || processDirectionalButtons();
182}
183
184void InputEvents::toggleHeldButtons()
185{
186 int directionCode = directionForKeycode();
187
188 if (directionCode >= 0)
189 {
190 if (isKeyDown())
191 {
192 // make sure it's not already down
193 if (!held_directions[directionCode])
194 {
195 // on key down, set the corresponding held boolean to true
196 held_directions[directionCode] = true;
197 held_type = this->type;
198
199 // reset the frame counter so we don't fire on this frame
200 // (initial reset is lower to add a slight delay when they first start holding)
201 curFrame = -25;
202 }
203 }
204
205 if (isKeyUp())
206 {
207 // release the corresponding key too
208 held_directions[directionCode] = false;
209 }
210 }
211}
212
213// returns true if a directional event was fire (so that we know to keep consuming later)
214bool InputEvents::processDirectionalButtons()
215{
216 // up the counter
217 curFrame++;
218
219 // if one of the four direction keys is true, fire off repeat events for it
220 // (when rapidFire lines up only)
221 if (curFrame > 0 && curFrame % rapidFireRate == 0)
222 {
223 for (int x = 0; x < 4; x++)
224 {
225 if (!held_directions[x])
226 continue;
227
228 // send a corresponding directional event
229 this->type = held_type;
230 bool isGamepad = (this->type == SDL_JOYBUTTONDOWN || this->type == SDL_JOYBUTTONUP);
231 this->keyCode = isGamepad ? pad_buttons[4 + x] : key_buttons[4 + x]; // send up through right directions
232 this->noop = false;
233
234 return true;
235 }
236 }
237
238 return false;
239}
240
241int InputEvents::directionForKeycode()
242{
243 // this keycode overlaps with some other constants, so just return asap
244 if (this->type == SDL_KEYDOWN && this->keyCode == SDLK_RETURN)
245 return -1;
246
247 // returns 0 1 2 or 3 for up down left or right
248 switch (this->keyCode)
249 {
250 case SDL_UP_STICK:
251 case SDL_UP:
252 case SDLK_UP:
253 return 0;
254 case SDL_DOWN_STICK:
255 case SDL_DOWN:
256 case SDLK_DOWN:
257 return 1;
258 case SDL_LEFT_STICK:
259 case SDL_LEFT:
260 case SDLK_LEFT:
261 return 2;
262 case SDL_RIGHT_STICK:
263 case SDL_RIGHT:
264 case SDLK_RIGHT:
265 return 3;
266 default:
267 return -1;
268 }
269 return -1;
270}
271
272bool InputEvents::held(int buttons)
273{
274 // if it's a key event
275 if ((this->type == SDL_KEYDOWN || this->type == SDL_KEYUP) && !InputEvents::bypassKeyEvents)
276 {
277 for (int x = 0; x < TOTAL_BUTTONS; x++)
278 if (key_buttons[x] == keyCode && (buttons & currentButtons[x]))
279 return true;
280 }
281
282 // if it's a controller event
283 else if (this->type == SDL_JOYBUTTONDOWN || this->type == SDL_JOYBUTTONUP)
284 {
285 for (int x = 0; x < TOTAL_BUTTONS; x++)
286 if (pad_buttons[x] == keyCode && (buttons & currentButtons[x]))
287 return true;
288 }
289
290 return false;
291}
292
293bool InputEvents::pressed(int buttons)
294{
295 return isKeyDown() && held(buttons);
296}
297
298bool InputEvents::released(int buttons)
299{
300 return isKeyUp() && held(buttons);
301}
302
303bool InputEvents::touchIn(int x, int y, int width, int height)
304{
305 return (this->xPos >= x && this->xPos <= x + width && this->yPos >= y && this->yPos <= y + height);
306}
307
308bool InputEvents::isTouchDown()
309{
310 return this->type == SDL_MOUSEBUTTONDOWN || (allowTouch && this->type == SDL_FINGERDOWN);
311}
312
313bool InputEvents::isTouchDrag()
314{
315 return this->type == SDL_MOUSEMOTION || (allowTouch && this->type == SDL_FINGERMOTION);
316}
317
318bool InputEvents::isTouchUp()
319{
320 return this->type == SDL_MOUSEBUTTONUP || (allowTouch && this->type == SDL_FINGERUP);
321}
322
323bool InputEvents::isTouch()
324{
325 return isTouchDown() || isTouchDrag() || isTouchUp();
326}
327
328bool InputEvents::isScroll()
329{
330 return this->isScrolling;
331}
332
333bool InputEvents::isKeyDown()
334{
335 return this->type == SDL_KEYDOWN || this->type == SDL_JOYBUTTONDOWN;
336}
337
338bool InputEvents::isKeyUp()
339{
340 return this->type == SDL_KEYUP || this->type == SDL_JOYBUTTONUP;
341}
342
344{
345 SDL_Joystick *j;
346 switch(event->type)
347 {
348 case SDL_JOYDEVICEADDED:
349 j = SDL_JoystickOpen(event->jdevice.which);
350 if (j)
351 printf("Added joystick device: %s, with ID %d\n", SDL_JoystickName(j), SDL_JoystickInstanceID(j));
352 break;
353 case SDL_JOYDEVICEREMOVED:
354 j = SDL_JoystickFromInstanceID(event->jdevice.which);
355 if (j && SDL_JoystickGetAttached(j))
356 {
357 printf("Removed joystick device: %s\n", SDL_JoystickName(j));
358 SDL_JoystickClose(j);
359 }
360 break;
361 default:
362 break;
363 }
364}
365
366GamepadInfo& InputEvents::getLastGamepadInfo()
367{
368 return gamepadMap[lastGamepadKey];
369}
bool needsRedraw
whether or not this element needs the screen redrawn next time it's processed
Definition: Element.hpp:84
bool touchIn(int x, int width, int y, int height)
whether or not a touch is detected within the specified rect in this cycle
bool processSDLEvents()
update which buttons are pressed
Definition: InputEvents.cpp:75
bool held(int buttons)
whether or not a button is pressed during this cycle
void processJoystickHotplugging(SDL_Event *event)
joystick device events processing