1 /*
2 C-Dogs SDL
3 A port of the legendary (and fun) action/arcade cdogs.
4 Copyright (c) 2013-2016, 2018-2021 Cong Xu
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12 Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include "events.h"
29
30 #include <assert.h>
31 #include <stdlib.h>
32
33 #include <SDL_timer.h>
34
35 #include "config_io.h"
36 #include "files.h"
37 #include "gamedata.h"
38 #include "log.h"
39 #include "music.h"
40 #include "pic_manager.h"
41
42 EventHandlers gEventHandlers;
43
EventInit(EventHandlers * handlers)44 void EventInit(EventHandlers *handlers)
45 {
46 memset(handlers, 0, sizeof *handlers);
47 KeyInit(&handlers->keyboard);
48 JoyInit(&handlers->joysticks);
49 MouseInit(&handlers->mouse);
50 }
EventTerminate(EventHandlers * handlers)51 void EventTerminate(EventHandlers *handlers)
52 {
53 JoyTerminate(&handlers->joysticks);
54 MouseTerminate(&handlers->mouse);
55 }
EventReset(EventHandlers * handlers)56 void EventReset(EventHandlers *handlers)
57 {
58 KeyInit(&handlers->keyboard);
59 JoyReset(&handlers->joysticks);
60 MouseReset(&handlers->mouse);
61 }
62
EventPoll(EventHandlers * handlers,const Uint32 ticks,int (* onEvent)(SDL_Event *))63 void EventPoll(
64 EventHandlers *handlers, const Uint32 ticks, int (*onEvent)(SDL_Event *))
65 {
66 SDL_Event e;
67 handlers->HasResolutionChanged = false;
68 handlers->HasLostFocus = false;
69 KeyPrePoll(&handlers->keyboard);
70 MousePrePoll(&handlers->mouse);
71 JoyPrePoll(&handlers->joysticks);
72 SDL_free(handlers->DropFile);
73 handlers->DropFile = NULL;
74 // Don't process mouse events if focus just regained this cycle
75 // This is to prevent bogus click events outside the window, e.g. in the
76 // title bar
77 bool regainedFocus = false;
78 while (SDL_PollEvent(&e))
79 {
80 switch (e.type)
81 {
82 case SDL_AUDIODEVICEADDED: // fallthrough
83 case SDL_AUDIODEVICEREMOVED:
84 if (e.adevice.iscapture)
85 {
86 // Ignore capture devices
87 break;
88 }
89 SoundReopen(&gSoundDevice);
90 break;
91
92 case SDL_KEYDOWN:
93 if (e.key.repeat)
94 {
95 break;
96 }
97 KeyOnKeyDown(&handlers->keyboard, e.key.keysym);
98 break;
99 case SDL_KEYUP:
100 KeyOnKeyUp(&handlers->keyboard, e.key.keysym);
101 break;
102 case SDL_TEXTINPUT:
103 strcpy(handlers->keyboard.Typed, e.text.text);
104 break;
105
106 case SDL_CONTROLLERDEVICEADDED: {
107 const SDL_JoystickID jid = JoyAdded(e.cdevice.which);
108 if (jid == -1)
109 {
110 break;
111 }
112 // If there are players with unset devices,
113 // set this controller to them
114 CA_FOREACH(PlayerData, p, gPlayerDatas)
115 if (p->inputDevice == INPUT_DEVICE_UNSET)
116 {
117 PlayerTrySetInputDevice(p, INPUT_DEVICE_JOYSTICK, jid);
118 LOG(LM_INPUT, LL_INFO, "Joystick %d assigned to player %d",
119 jid, p->UID);
120 break;
121 }
122 CA_FOREACH_END()
123 }
124 break;
125
126 case SDL_CONTROLLERDEVICEREMOVED:
127 JoyRemoved(e.cdevice.which);
128 // If there was a player using this joystick,
129 // set their input device to nothing
130 CA_FOREACH(PlayerData, p, gPlayerDatas)
131 if (p->inputDevice == INPUT_DEVICE_JOYSTICK &&
132 p->deviceIndex == e.cdevice.which)
133 {
134 PlayerTrySetInputDevice(p, INPUT_DEVICE_UNSET, 0);
135 LOG(LM_INPUT, LL_WARN, "Joystick for player %d removed",
136 p->UID);
137 break;
138 }
139 CA_FOREACH_END()
140 break;
141
142 case SDL_CONTROLLERBUTTONDOWN:
143 JoyOnButtonDown(e.cbutton);
144 break;
145
146 case SDL_CONTROLLERBUTTONUP:
147 JoyOnButtonUp(e.cbutton);
148 break;
149
150 case SDL_CONTROLLERAXISMOTION:
151 JoyOnAxis(e.caxis);
152 break;
153
154 case SDL_MOUSEBUTTONDOWN:
155 if (regainedFocus)
156 break;
157 MouseOnButtonDown(&handlers->mouse, e.button.button);
158 break;
159 case SDL_MOUSEBUTTONUP:
160 if (regainedFocus)
161 break;
162 MouseOnButtonUp(&handlers->mouse, e.button.button);
163 break;
164 case SDL_MOUSEWHEEL:
165 if (regainedFocus)
166 break;
167 MouseOnWheel(&handlers->mouse, e.wheel.x, e.wheel.y);
168 break;
169 case SDL_WINDOWEVENT:
170 switch (e.window.event)
171 {
172 case SDL_WINDOWEVENT_FOCUS_GAINED:
173 regainedFocus = true;
174 MusicSetPlaying(&gSoundDevice.music, true);
175 break;
176 case SDL_WINDOWEVENT_FOCUS_LOST:
177 if (!gCampaign.IsClient &&
178 !ConfigGetBool(&gConfig, "StartServer"))
179 {
180 MusicSetPlaying(&gSoundDevice.music, false);
181 handlers->HasLostFocus = true;
182 }
183 // Reset input handlers
184 EventReset(handlers);
185 break;
186 case SDL_WINDOWEVENT_SIZE_CHANGED: {
187 const int w = ConfigGetInt(&gConfig, "Graphics.WindowWidth");
188 const int h = ConfigGetInt(&gConfig, "Graphics.WindowHeight");
189 if (w == e.window.data1 && h == e.window.data2)
190 {
191 // macOS sends spurious size changed events when
192 // switching between fullscreen; don't reinitialise
193 // graphics
194 break;
195 }
196 handlers->HasResolutionChanged = true;
197 const int scale =
198 ConfigGetInt(&gConfig, "Graphics.ScaleFactor");
199 if (!gGraphicsDevice.cachedConfig.IsEditor)
200 {
201 ConfigSetInt(
202 &gConfig, "Graphics.WindowWidth", e.window.data1);
203 ConfigSetInt(
204 &gConfig, "Graphics.WindowHeight", e.window.data2);
205 ConfigSave(&gConfig, GetConfigFilePath(CONFIG_FILE));
206 }
207 GraphicsConfigSet(
208 &gGraphicsDevice.cachedConfig,
209 svec2i(e.window.data1, e.window.data2), false, scale,
210 gGraphicsDevice.cachedConfig.ScaleMode,
211 gGraphicsDevice.cachedConfig.Brightness,
212 gGraphicsDevice.cachedConfig.SecondWindow);
213 GraphicsInitialize(&gGraphicsDevice);
214 }
215 break;
216 case SDL_WINDOWEVENT_CLOSE:
217 handlers->HasQuit = true;
218 break;
219 default:
220 // do nothing
221 break;
222 }
223 break;
224 case SDL_QUIT:
225 handlers->HasQuit = true;
226 break;
227 case SDL_DROPFILE:
228 handlers->DropFile = e.drop.file;
229 break;
230 default:
231 break;
232 }
233 if (onEvent)
234 {
235 onEvent(&e);
236 }
237 }
238 KeyPostPoll(&handlers->keyboard, ticks);
239 MousePostPoll(&handlers->mouse, ticks);
240
241 // Toggle fullscreen
242 if (KeyIsPressed(&handlers->keyboard, SDL_SCANCODE_RETURN) &&
243 (KeyIsDown(&handlers->keyboard, SDL_SCANCODE_LALT) ||
244 KeyIsDown(&handlers->keyboard, SDL_SCANCODE_RALT)))
245 {
246 ConfigGet(&gConfig, "Graphics.Fullscreen")->u.Bool.Value =
247 !ConfigGet(&gConfig, "Graphics.Fullscreen")->u.Bool.Value;
248 GraphicsConfigSetFromConfig(&gGraphicsDevice.cachedConfig, &gConfig);
249 GraphicsInitialize(&gGraphicsDevice);
250 }
251
252 // Auto quit on timer
253 if (handlers->DemoQuitTimer > 0)
254 {
255 handlers->DemoQuitTimer -= ticks;
256 if (handlers->DemoQuitTimer <= 0)
257 {
258 LOG(LM_MAIN, LL_INFO, "Auto-quitting");
259 handlers->HasQuit = true;
260 }
261 }
262 }
263
GetKeyboardCmd(keyboard_t * keyboard,const int kbIndex,const bool isPressed)264 int GetKeyboardCmd(
265 keyboard_t *keyboard, const int kbIndex, const bool isPressed)
266 {
267 int cmd = 0;
268 bool (*keyFunc)(const keyboard_t *, const int) =
269 isPressed ? KeyIsPressed : KeyIsDown;
270 const InputKeys *keys = &keyboard->PlayerKeys[kbIndex];
271
272 if (keyFunc(keyboard, keys->left))
273 cmd |= CMD_LEFT;
274 else if (keyFunc(keyboard, keys->right))
275 cmd |= CMD_RIGHT;
276
277 if (keyFunc(keyboard, keys->up))
278 cmd |= CMD_UP;
279 else if (keyFunc(keyboard, keys->down))
280 cmd |= CMD_DOWN;
281
282 if (keyFunc(keyboard, keys->button1))
283 cmd |= CMD_BUTTON1;
284 if (keyFunc(keyboard, keys->button2))
285 cmd |= CMD_BUTTON2;
286 if (keyFunc(keyboard, keys->grenade))
287 cmd |= CMD_GRENADE;
288
289 return cmd;
290 }
GetMouseCmd(Mouse * mouse,bool isPressed,int useMouseMove,struct vec2i pos)291 static int GetMouseCmd(
292 Mouse *mouse, bool isPressed, int useMouseMove, struct vec2i pos)
293 {
294 int cmd = 0;
295 bool (*mouseFunc)(const Mouse *, const int) =
296 isPressed ? MouseIsPressed : MouseIsDown;
297
298 if (useMouseMove)
299 {
300 cmd |= MouseGetMove(mouse, pos);
301 }
302 else
303 {
304 if (MouseWheel(mouse).y > 0)
305 cmd |= CMD_UP;
306 else if (MouseWheel(mouse).y < 0)
307 cmd |= CMD_DOWN;
308 }
309
310 if (mouseFunc(mouse, SDL_BUTTON_LEFT))
311 cmd |= CMD_BUTTON1;
312 if (mouseFunc(mouse, SDL_BUTTON_RIGHT))
313 cmd |= CMD_BUTTON2;
314 if (mouseFunc(mouse, SDL_BUTTON_MIDDLE))
315 cmd |= CMD_GRENADE;
316 if (mouseFunc(mouse, SDL_BUTTON_X1))
317 cmd |= CMD_MAP;
318
319 return cmd;
320 }
321
GetJoystickCmd(const SDL_JoystickID id,bool isPressed)322 static int GetJoystickCmd(const SDL_JoystickID id, bool isPressed)
323 {
324 int cmd = 0;
325 bool (*joyFunc)(const SDL_JoystickID, const int) =
326 isPressed ? JoyIsPressed : JoyIsDown;
327
328 if (joyFunc(id, CMD_LEFT))
329 cmd |= CMD_LEFT;
330 else if (joyFunc(id, CMD_RIGHT))
331 cmd |= CMD_RIGHT;
332
333 if (joyFunc(id, CMD_UP))
334 cmd |= CMD_UP;
335 else if (joyFunc(id, CMD_DOWN))
336 cmd |= CMD_DOWN;
337
338 if (joyFunc(id, CMD_BUTTON1))
339 cmd |= CMD_BUTTON1;
340 if (joyFunc(id, CMD_BUTTON2))
341 cmd |= CMD_BUTTON2;
342 if (joyFunc(id, CMD_GRENADE))
343 cmd |= CMD_GRENADE;
344
345 if (joyFunc(id, CMD_MAP))
346 cmd |= CMD_MAP;
347
348 if (joyFunc(id, CMD_ESC))
349 cmd |= CMD_ESC;
350
351 return cmd;
352 }
353
GetGameCmd(EventHandlers * handlers,const PlayerData * playerData,const struct vec2i playerPos)354 int GetGameCmd(
355 EventHandlers *handlers, const PlayerData *playerData,
356 const struct vec2i playerPos)
357 {
358 int cmd = 0;
359
360 switch (playerData->inputDevice)
361 {
362 case INPUT_DEVICE_KEYBOARD:
363 cmd = GetKeyboardCmd(
364 &handlers->keyboard, playerData->deviceIndex, false);
365 break;
366 case INPUT_DEVICE_MOUSE:
367 cmd = GetMouseCmd(&handlers->mouse, false, 1, playerPos);
368 break;
369 case INPUT_DEVICE_JOYSTICK:
370 cmd = GetJoystickCmd(playerData->deviceIndex, false);
371 break;
372 default:
373 // do nothing
374 break;
375 }
376
377 return cmd;
378 }
379
GetOnePlayerCmd(EventHandlers * handlers,const bool isPressed,const input_device_e device,const int deviceIndex)380 int GetOnePlayerCmd(
381 EventHandlers *handlers, const bool isPressed, const input_device_e device,
382 const int deviceIndex)
383 {
384 int cmd = 0;
385 switch (device)
386 {
387 case INPUT_DEVICE_KEYBOARD:
388 cmd = GetKeyboardCmd(&handlers->keyboard, deviceIndex, isPressed);
389 break;
390 case INPUT_DEVICE_MOUSE:
391 cmd = GetMouseCmd(&handlers->mouse, isPressed, 0, svec2i_zero());
392 break;
393 case INPUT_DEVICE_JOYSTICK:
394 cmd = GetJoystickCmd(deviceIndex, isPressed);
395 break;
396 default:
397 // Do nothing
398 break;
399 }
400 return cmd;
401 }
402
GetPlayerCmds(EventHandlers * handlers,int (* cmds)[MAX_LOCAL_PLAYERS])403 void GetPlayerCmds(EventHandlers *handlers, int (*cmds)[MAX_LOCAL_PLAYERS])
404 {
405 int idx = 0;
406 for (int i = 0; i < (int)gPlayerDatas.size; i++, idx++)
407 {
408 const PlayerData *p = CArrayGet(&gPlayerDatas, i);
409 if (!p->IsLocal)
410 {
411 idx--;
412 continue;
413 }
414 if (p->inputDevice != INPUT_DEVICE_UNSET)
415 {
416 (*cmds)[idx] = GetOnePlayerCmd(
417 handlers, true, p->inputDevice, p->deviceIndex);
418 }
419 }
420 }
421
GetMenuCmd(EventHandlers * handlers)422 int GetMenuCmd(EventHandlers *handlers)
423 {
424 keyboard_t *kb = &handlers->keyboard;
425 bool firstJoyPressedEsc = false;
426 SDL_JoystickID firstJoyId = 0;
427 if (handlers->joysticks.size > 0)
428 {
429 const Joystick *firstJoy = CArrayGet(&handlers->joysticks, 0);
430 firstJoyId = firstJoy->id;
431 firstJoyPressedEsc = JoyIsPressed(firstJoyId, CMD_ESC);
432 }
433 if (KeyIsPressed(kb, SDL_SCANCODE_ESCAPE) || firstJoyPressedEsc)
434 {
435 return CMD_ESC;
436 }
437
438 // Check first player keyboard
439 int cmd = GetOnePlayerCmd(handlers, true, INPUT_DEVICE_KEYBOARD, 0);
440 if (!cmd)
441 {
442 // Check keyboard
443 if (KeyIsPressed(kb, SDL_SCANCODE_LEFT))
444 cmd |= CMD_LEFT;
445 else if (KeyIsPressed(kb, SDL_SCANCODE_RIGHT))
446 cmd |= CMD_RIGHT;
447
448 if (KeyIsPressed(kb, SDL_SCANCODE_UP))
449 cmd |= CMD_UP;
450 else if (KeyIsPressed(kb, SDL_SCANCODE_DOWN))
451 cmd |= CMD_DOWN;
452
453 if (KeyIsPressed(kb, SDL_SCANCODE_RETURN))
454 cmd |= CMD_BUTTON1;
455
456 if (KeyIsPressed(kb, SDL_SCANCODE_BACKSPACE))
457 cmd |= CMD_BUTTON2;
458 }
459 if (!cmd && handlers->joysticks.size > 0)
460 {
461 // Check joystick 1
462 cmd =
463 GetOnePlayerCmd(handlers, true, INPUT_DEVICE_JOYSTICK, firstJoyId);
464 }
465 if (!cmd)
466 {
467 // Check mouse
468 cmd = GetOnePlayerCmd(handlers, true, INPUT_DEVICE_MOUSE, 0);
469 }
470
471 return cmd;
472 }
473
InputGetButtonNameColor(const input_device_e d,const int dIndex,const int cmd,char * buf,color_t * color)474 void InputGetButtonNameColor(
475 const input_device_e d, const int dIndex, const int cmd, char *buf,
476 color_t *color)
477 {
478 switch (d)
479 {
480 case INPUT_DEVICE_KEYBOARD:
481 #ifdef __GCWZERO__
482 if (color != NULL)
483 {
484 *color = colorBlue;
485 }
486 switch (cmd)
487 {
488 case CMD_LEFT:
489 strcpy(buf, "left");
490 return;
491 case CMD_RIGHT:
492 strcpy(buf, "right");
493 return;
494 case CMD_UP:
495 strcpy(buf, "up");
496 return;
497 case CMD_DOWN:
498 strcpy(buf, "down");
499 return;
500 case CMD_BUTTON1:
501 strcpy(buf, "A");
502 return;
503 case CMD_BUTTON2:
504 strcpy(buf, "B");
505 return;
506 case CMD_GRENADE:
507 *color = colorGray;
508 strcpy(buf, "R");
509 return;
510 case CMD_MAP:
511 *color = colorGray;
512 strcpy(buf, "L");
513 return;
514 case CMD_ESC:
515 strcpy(buf, "SELECT");
516 return;
517 default:
518 CASSERT(false, "unknown button");
519 return;
520 }
521 #else
522 {
523 const InputKeys *keys = &gEventHandlers.keyboard.PlayerKeys[dIndex];
524 switch (cmd)
525 {
526 case CMD_LEFT:
527 strcpy(buf, SDL_GetScancodeName(keys->left));
528 return;
529 case CMD_RIGHT:
530 strcpy(buf, SDL_GetScancodeName(keys->right));
531 return;
532 case CMD_UP:
533 strcpy(buf, SDL_GetScancodeName(keys->up));
534 return;
535 case CMD_DOWN:
536 strcpy(buf, SDL_GetScancodeName(keys->down));
537 return;
538 case CMD_BUTTON1:
539 strcpy(buf, SDL_GetScancodeName(keys->button1));
540 return;
541 case CMD_BUTTON2:
542 strcpy(buf, SDL_GetScancodeName(keys->button2));
543 return;
544 case CMD_GRENADE:
545 strcpy(buf, SDL_GetScancodeName(keys->grenade));
546 return;
547 case CMD_MAP:
548 strcpy(buf, SDL_GetScancodeName(keys->map));
549 return;
550 case CMD_ESC:
551 strcpy(buf, SDL_GetScancodeName(SDL_SCANCODE_ESCAPE));
552 return;
553 default:
554 CASSERT(false, "unknown button");
555 return;
556 }
557 }
558 #endif
559 break;
560 case INPUT_DEVICE_MOUSE:
561 switch (cmd)
562 {
563 case CMD_LEFT:
564 strcpy(buf, "left");
565 return;
566 case CMD_RIGHT:
567 strcpy(buf, "right");
568 return;
569 case CMD_UP:
570 strcpy(buf, "up");
571 return;
572 case CMD_DOWN:
573 strcpy(buf, "down");
574 return;
575 case CMD_BUTTON1:
576 strcpy(buf, "left click");
577 return;
578 case CMD_BUTTON2:
579 strcpy(buf, "right click");
580 return;
581 case CMD_MAP:
582 strcpy(buf, "middle click");
583 return;
584 case CMD_ESC:
585 strcpy(buf, "");
586 return;
587 default:
588 CASSERT(false, "unknown button");
589 return;
590 }
591 break;
592 case INPUT_DEVICE_JOYSTICK:
593 JoyButtonNameColor(dIndex, cmd, buf, color);
594 return;
595 case INPUT_DEVICE_AI:
596 return;
597 default:
598 CASSERT(false, "unknown input device");
599 return;
600 }
601 }
InputGetDirectionNames(char * buf,const input_device_e d,const int dIndex)602 void InputGetDirectionNames(
603 char *buf, const input_device_e d, const int dIndex)
604 {
605 strcpy(buf, "");
606 switch (d)
607 {
608 case INPUT_DEVICE_KEYBOARD: {
609 char left[256], right[256], up[256], down[256];
610 InputGetButtonName(d, dIndex, CMD_LEFT, left);
611 InputGetButtonName(d, dIndex, CMD_RIGHT, right),
612 InputGetButtonName(d, dIndex, CMD_UP, up),
613 InputGetButtonName(d, dIndex, CMD_DOWN, down);
614 sprintf(buf, "%s, %s, %s, %s", left, right, up, down);
615 }
616 break;
617 case INPUT_DEVICE_MOUSE:
618 strcpy(buf, "mouse wheel");
619 break;
620 case INPUT_DEVICE_JOYSTICK:
621 strcpy(buf, "directions");
622 break;
623 case INPUT_DEVICE_AI:
624 break;
625 case INPUT_DEVICE_UNSET:
626 break;
627 default:
628 CASSERT(false, "unknown device");
629 break;
630 }
631 }
InputHasGrenadeButton(const input_device_e d,const int dIndex)632 bool InputHasGrenadeButton(const input_device_e d, const int dIndex)
633 {
634 switch (d)
635 {
636 case INPUT_DEVICE_UNSET:
637 return false;
638 case INPUT_DEVICE_KEYBOARD:
639 return KeyGet(
640 &gEventHandlers.keyboard.PlayerKeys[dIndex],
641 KEY_CODE_GRENADE) != SDL_SCANCODE_UNKNOWN;
642 case INPUT_DEVICE_MOUSE:
643 return true;
644 case INPUT_DEVICE_JOYSTICK:
645 return true;
646 case INPUT_DEVICE_AI:
647 return false;
648 default:
649 CASSERT(false, "unknown input device");
650 return true;
651 }
652 }
653
GetKey(EventHandlers * handlers)654 SDL_Scancode GetKey(EventHandlers *handlers)
655 {
656 SDL_Scancode k = SDL_SCANCODE_UNKNOWN;
657 Uint32 ticksNow = SDL_GetTicks();
658 do
659 {
660 const Uint32 ticksThen = ticksNow;
661 ticksNow = SDL_GetTicks();
662 const Uint32 ticksElapsed = ticksNow - ticksThen;
663 EventPoll(handlers, ticksElapsed, NULL);
664 k = KeyGetPressed(&handlers->keyboard);
665 SDL_Delay(10);
666 } while (k == SDL_SCANCODE_UNKNOWN);
667 return k;
668 }
669
EventWaitKeyOrText(EventHandlers * handlers)670 SDL_Scancode EventWaitKeyOrText(EventHandlers *handlers)
671 {
672 EventPoll(handlers, 0, NULL);
673 const SDL_Scancode k = KeyGetPressed(&handlers->keyboard);
674 SDL_Delay(10);
675 return k;
676 }
677
678 static EventWaitResult WaitResult(const bool result);
EventWaitForAnyKeyOrButton(void)679 EventWaitResult EventWaitForAnyKeyOrButton(void)
680 {
681 int cmds[MAX_LOCAL_PLAYERS];
682 memset(cmds, 0, sizeof cmds);
683 GetPlayerCmds(&gEventHandlers, &cmds);
684 for (int i = 0; i < MAX_LOCAL_PLAYERS; i++)
685 {
686 if (cmds[i] & (CMD_BUTTON1 | CMD_BUTTON2 | CMD_GRENADE))
687 {
688 // Interpret anything other than CMD_BUTTON1 as cancel
689 return WaitResult(cmds[i] & CMD_BUTTON1);
690 }
691 }
692
693 // Check menu commands
694 const int menuCmd = GetMenuCmd(&gEventHandlers);
695 if (menuCmd & (CMD_BUTTON1 | CMD_BUTTON2 | CMD_GRENADE))
696 {
697 // Interpret anything other than CMD_BUTTON1 as cancel
698 return WaitResult(menuCmd & CMD_BUTTON1);
699 }
700
701 // Check if anyone pressed escape
702 if (EventIsEscape(&gEventHandlers, cmds, menuCmd))
703 {
704 return WaitResult(false);
705 }
706
707 return EVENT_WAIT_CONTINUE;
708 }
WaitResult(const bool result)709 static EventWaitResult WaitResult(const bool result)
710 {
711 return result ? EVENT_WAIT_OK : EVENT_WAIT_CANCEL;
712 }
713
EventIsEscape(EventHandlers * handlers,const int cmds[MAX_LOCAL_PLAYERS],const int menuCmd)714 bool EventIsEscape(
715 EventHandlers *handlers, const int cmds[MAX_LOCAL_PLAYERS],
716 const int menuCmd)
717 {
718 for (int i = 0; i < MAX_LOCAL_PLAYERS; i++)
719 {
720 if (cmds[i] & CMD_ESC)
721 {
722 return true;
723 }
724 }
725
726 // Check keyboard escape
727 if (KeyIsPressed(&handlers->keyboard, SDL_SCANCODE_ESCAPE) ||
728 handlers->HasQuit)
729 {
730 return true;
731 }
732
733 // Check menu commands
734 if (menuCmd & CMD_ESC)
735 {
736 return true;
737 }
738
739 return false;
740 }
741