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