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 return false; //Quitting overrides all other events.
127 }
128 else if (event.key.repeat == 0 && (this->type == SDL_KEYDOWN || this->type == SDL_KEYUP))
129 {
130 this->keyCode = event.key.keysym.sym;
131 this->mod = event.key.keysym.mod;
132 }
133 else if (this->type == SDL_JOYBUTTONDOWN || this->type == SDL_JOYBUTTONUP)
134 {
135 this->keyCode = event.jbutton.button;
136 }
137 else if (this->type == SDL_MOUSEMOTION || this->type == SDL_MOUSEBUTTONUP || this->type == SDL_MOUSEBUTTONDOWN)
138 {
139 bool isMotion = this->type == SDL_MOUSEMOTION;
140
141 this->yPos = isMotion ? event.motion.y : event.button.y;
142 this->xPos = isMotion ? event.motion.x : event.button.x;
143 }
144 else if (allowTouch && (this->type == SDL_FINGERMOTION || this->type == SDL_FINGERUP || this->type == SDL_FINGERDOWN))
145 {
146 this->yPos = event.tfinger.y * SCREEN_HEIGHT;
147 this->xPos = event.tfinger.x * SCREEN_WIDTH;
148 }
149
150 if (this->type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
151 printf("Window resized to %dx%d\n", event.window.data1, event.window.data2);
152 RootDisplay::mainDisplay->setScreenResolution(
153 event.window.data1 * RootDisplay::dpiScale,
154 event.window.data2 * RootDisplay::dpiScale
155 );
156
157 // callback to alert the app that the window changed resolution
158 if (RootDisplay::mainDisplay->windowResizeCallback)
159 RootDisplay::mainDisplay->windowResizeCallback();
160
161 RootDisplay::mainDisplay->needsRedraw = true;
162 }
163
164 // offset the x, y positions by the dpi scale
165 this->xPos = (int)(this->xPos * RootDisplay::dpiScale);
166 this->yPos = (int)(this->yPos * RootDisplay::dpiScale);
167
168 toggleHeldButtons();
169
170 return true;
171}
172
173bool InputEvents::update()
174{
175 this->type = 0;
176 this->keyCode = -1;
177 this->noop = true;
178
179 // process SDL or directional events
180 return processSDLEvents() || processDirectionalButtons();
181}
182
183void InputEvents::toggleHeldButtons()
184{
185 int directionCode = directionForKeycode();
186
187 if (directionCode >= 0)
188 {
189 if (isKeyDown())
190 {
191 // make sure it's not already down
192 if (!held_directions[directionCode])
193 {
194 // on key down, set the corresponding held boolean to true
195 held_directions[directionCode] = true;
196 held_type = this->type;
197
198 // reset the frame counter so we don't fire on this frame
199 // (initial reset is lower to add a slight delay when they first start holding)
200 curFrame = -25;
201 }
202 }
203
204 if (isKeyUp())
205 {
206 // release the corresponding key too
207 held_directions[directionCode] = false;
208 }
209 }
210}
211
212// returns true if a directional event was fire (so that we know to keep consuming later)
213bool InputEvents::processDirectionalButtons()
214{
215 // up the counter
216 curFrame++;
217
218 // if one of the four direction keys is true, fire off repeat events for it
219 // (when rapidFire lines up only)
220 if (curFrame > 0 && curFrame % rapidFireRate == 0)
221 {
222 for (int x = 0; x < 4; x++)
223 {
224 if (!held_directions[x])
225 continue;
226
227 // send a corresponding directional event
228 this->type = held_type;
229 bool isGamepad = (this->type == SDL_JOYBUTTONDOWN || this->type == SDL_JOYBUTTONUP);
230 this->keyCode = isGamepad ? pad_buttons[4 + x] : key_buttons[4 + x]; // send up through right directions
231 this->noop = false;
232
233 return true;
234 }
235 }
236
237 return false;
238}
239
240int InputEvents::directionForKeycode()
241{
242 // this keycode overlaps with some other constants, so just return asap
243 if (this->type == SDL_KEYDOWN && this->keyCode == SDLK_RETURN)
244 return -1;
245
246 // returns 0 1 2 or 3 for up down left or right
247 switch (this->keyCode)
248 {
249 case SDL_UP_STICK:
250 case SDL_UP:
251 case SDLK_UP:
252 return 0;
253 case SDL_DOWN_STICK:
254 case SDL_DOWN:
255 case SDLK_DOWN:
256 return 1;
257 case SDL_LEFT_STICK:
258 case SDL_LEFT:
259 case SDLK_LEFT:
260 return 2;
261 case SDL_RIGHT_STICK:
262 case SDL_RIGHT:
263 case SDLK_RIGHT:
264 return 3;
265 default:
266 return -1;
267 }
268 return -1;
269}
270
271bool InputEvents::held(int buttons)
272{
273 // if it's a key event
274 if ((this->type == SDL_KEYDOWN || this->type == SDL_KEYUP) && !InputEvents::bypassKeyEvents)
275 {
276 for (int x = 0; x < TOTAL_BUTTONS; x++)
277 if (key_buttons[x] == keyCode && (buttons & currentButtons[x]))
278 return true;
279 }
280
281 // if it's a controller event
282 else if (this->type == SDL_JOYBUTTONDOWN || this->type == SDL_JOYBUTTONUP)
283 {
284 for (int x = 0; x < TOTAL_BUTTONS; x++)
285 if (pad_buttons[x] == keyCode && (buttons & currentButtons[x]))
286 return true;
287 }
288
289 return false;
290}
291
292bool InputEvents::pressed(int buttons)
293{
294 return isKeyDown() && held(buttons);
295}
296
297bool InputEvents::released(int buttons)
298{
299 return isKeyUp() && held(buttons);
300}
301
302bool InputEvents::touchIn(int x, int y, int width, int height)
303{
304 return (this->xPos >= x && this->xPos <= x + width && this->yPos >= y && this->yPos <= y + height);
305}
306
307bool InputEvents::isTouchDown()
308{
309 return this->type == SDL_MOUSEBUTTONDOWN || (allowTouch && this->type == SDL_FINGERDOWN);
310}
311
312bool InputEvents::isTouchDrag()
313{
314 return this->type == SDL_MOUSEMOTION || (allowTouch && this->type == SDL_FINGERMOTION);
315}
316
317bool InputEvents::isTouchUp()
318{
319 return this->type == SDL_MOUSEBUTTONUP || (allowTouch && this->type == SDL_FINGERUP);
320}
321
322bool InputEvents::isTouch()
323{
324 return isTouchDown() || isTouchDrag() || isTouchUp();
325}
326
327bool InputEvents::isScroll()
328{
329 return this->isScrolling;
330}
331
332bool InputEvents::isKeyDown()
333{
334 return this->type == SDL_KEYDOWN || this->type == SDL_JOYBUTTONDOWN;
335}
336
337bool InputEvents::isKeyUp()
338{
339 return this->type == SDL_KEYUP || this->type == SDL_JOYBUTTONUP;
340}
341
343{
344 SDL_Joystick *j;
345 switch(event->type)
346 {
347 case SDL_JOYDEVICEADDED:
348 j = SDL_JoystickOpen(event->jdevice.which);
349 if (j)
350 printf("Added joystick device: %s, with ID %d\n", SDL_JoystickName(j), SDL_JoystickInstanceID(j));
351 break;
352 case SDL_JOYDEVICEREMOVED:
353 j = SDL_JoystickFromInstanceID(event->jdevice.which);
354 if (j && SDL_JoystickGetAttached(j))
355 {
356 printf("Removed joystick device: %s\n", SDL_JoystickName(j));
357 SDL_JoystickClose(j);
358 }
359 break;
360 default:
361 break;
362 }
363}
364
365GamepadInfo& InputEvents::getLastGamepadInfo()
366{
367 return gamepadMap[lastGamepadKey];
368}
bool needsRedraw
whether or not this element needs the screen redrawn next time it's processed
Definition: Element.hpp:86
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