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