1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_EVENTS 0
28 #define DEBUG_EVENTS_BUTTON (DEBUG_EVENTS * 0)
29 #define DEBUG_EVENTS_MOTION (DEBUG_EVENTS * 0)
30 #define DEBUG_EVENTS_WHEEL (DEBUG_EVENTS * 1)
31 #define DEBUG_EVENTS_WINDOW (DEBUG_EVENTS * 0)
32 #define DEBUG_EVENTS_FINGER (DEBUG_EVENTS * 0)
33 #define DEBUG_EVENTS_TEXT (DEBUG_EVENTS * 1)
34 #define DEBUG_EVENTS_KEY (DEBUG_EVENTS * 1)
37 static boolean cursor_inside_playfield = FALSE;
38 static int cursor_mode_last = CURSOR_DEFAULT;
39 static unsigned int special_cursor_delay = 0;
40 static unsigned int special_cursor_delay_value = 1000;
43 /* forward declarations for internal use */
44 static void HandleNoEvent(void);
45 static void HandleEventActions(void);
48 /* event filter especially needed for SDL event filtering due to
49 delay problems with lots of mouse motion events when mouse button
50 not pressed (X11 can handle this with 'PointerMotionHintMask') */
52 /* event filter addition for SDL2: as SDL2 does not have a function to enable
53 or disable keyboard auto-repeat, filter repeated keyboard events instead */
55 static int FilterEvents(const Event *event)
59 #if defined(TARGET_SDL2)
60 /* skip repeated key press events if keyboard auto-repeat is disabled */
61 if (event->type == EVENT_KEYPRESS &&
67 if (event->type == EVENT_BUTTONPRESS ||
68 event->type == EVENT_BUTTONRELEASE)
70 ((ButtonEvent *)event)->x -= video.screen_xoffset;
71 ((ButtonEvent *)event)->y -= video.screen_yoffset;
73 else if (event->type == EVENT_MOTIONNOTIFY)
75 ((MotionEvent *)event)->x -= video.screen_xoffset;
76 ((MotionEvent *)event)->y -= video.screen_yoffset;
79 /* non-motion events are directly passed to event handler functions */
80 if (event->type != EVENT_MOTIONNOTIFY)
83 motion = (MotionEvent *)event;
84 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
85 motion->y >= SY && motion->y < SY + SYSIZE);
87 /* do no reset mouse cursor before all pending events have been processed */
88 if (gfx.cursor_mode == cursor_mode_last &&
89 ((game_status == GAME_MODE_TITLE &&
90 gfx.cursor_mode == CURSOR_NONE) ||
91 (game_status == GAME_MODE_PLAYING &&
92 gfx.cursor_mode == CURSOR_PLAYFIELD)))
94 SetMouseCursor(CURSOR_DEFAULT);
96 DelayReached(&special_cursor_delay, 0);
98 cursor_mode_last = CURSOR_DEFAULT;
101 /* skip mouse motion events without pressed button outside level editor */
102 if (button_status == MB_RELEASED &&
103 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
109 /* to prevent delay problems, skip mouse motion events if the very next
110 event is also a mouse motion event (and therefore effectively only
111 handling the last of a row of mouse motion events in the event queue) */
113 static boolean SkipPressedMouseMotionEvent(const Event *event)
115 /* nothing to do if the current event is not a mouse motion event */
116 if (event->type != EVENT_MOTIONNOTIFY)
119 /* only skip motion events with pressed button outside the game */
120 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
127 PeekEvent(&next_event);
129 /* if next event is also a mouse motion event, skip the current one */
130 if (next_event.type == EVENT_MOTIONNOTIFY)
137 static boolean WaitValidEvent(Event *event)
141 if (!FilterEvents(event))
144 if (SkipPressedMouseMotionEvent(event))
150 /* this is especially needed for event modifications for the Android target:
151 if mouse coordinates should be modified in the event filter function,
152 using a properly installed SDL event filter does not work, because in
153 the event filter, mouse coordinates in the event structure are still
154 physical pixel positions, not logical (scaled) screen positions, so this
155 has to be handled at a later stage in the event processing functions
156 (when device pixel positions are already converted to screen positions) */
158 boolean NextValidEvent(Event *event)
160 while (PendingEvent())
161 if (WaitValidEvent(event))
170 unsigned int event_frame_delay = 0;
171 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
173 ResetDelayCounter(&event_frame_delay);
175 while (NextValidEvent(&event))
179 case EVENT_BUTTONPRESS:
180 case EVENT_BUTTONRELEASE:
181 HandleButtonEvent((ButtonEvent *) &event);
184 case EVENT_MOTIONNOTIFY:
185 HandleMotionEvent((MotionEvent *) &event);
188 #if defined(TARGET_SDL2)
189 case EVENT_WHEELMOTION:
190 HandleWheelEvent((WheelEvent *) &event);
193 case SDL_WINDOWEVENT:
194 HandleWindowEvent((WindowEvent *) &event);
197 case EVENT_FINGERPRESS:
198 case EVENT_FINGERRELEASE:
199 case EVENT_FINGERMOTION:
200 HandleFingerEvent((FingerEvent *) &event);
203 case EVENT_TEXTINPUT:
204 HandleTextEvent((TextEvent *) &event);
207 case SDL_APP_WILLENTERBACKGROUND:
208 case SDL_APP_DIDENTERBACKGROUND:
209 case SDL_APP_WILLENTERFOREGROUND:
210 case SDL_APP_DIDENTERFOREGROUND:
211 HandlePauseResumeEvent((PauseResumeEvent *) &event);
216 case EVENT_KEYRELEASE:
217 HandleKeyEvent((KeyEvent *) &event);
221 HandleOtherEvents(&event);
225 // do not handle events for longer than standard frame delay period
226 if (DelayReached(&event_frame_delay, event_frame_delay_value))
231 void HandleOtherEvents(Event *event)
236 HandleExposeEvent((ExposeEvent *) event);
239 case EVENT_UNMAPNOTIFY:
241 /* This causes the game to stop not only when iconified, but also
242 when on another virtual desktop, which might be not desired. */
243 SleepWhileUnmapped();
249 HandleFocusEvent((FocusChangeEvent *) event);
252 case EVENT_CLIENTMESSAGE:
253 HandleClientMessageEvent((ClientMessageEvent *) event);
256 #if defined(TARGET_SDL)
257 #if defined(TARGET_SDL2)
258 case SDL_CONTROLLERBUTTONDOWN:
259 case SDL_CONTROLLERBUTTONUP:
260 // for any game controller button event, disable overlay buttons
261 SetOverlayEnabled(FALSE);
263 HandleSpecialGameControllerButtons(event);
266 case SDL_CONTROLLERDEVICEADDED:
267 case SDL_CONTROLLERDEVICEREMOVED:
268 case SDL_CONTROLLERAXISMOTION:
270 case SDL_JOYAXISMOTION:
271 case SDL_JOYBUTTONDOWN:
272 case SDL_JOYBUTTONUP:
273 HandleJoystickEvent(event);
277 HandleWindowManagerEvent(event);
286 void HandleMouseCursor()
288 if (game_status == GAME_MODE_TITLE)
290 /* when showing title screens, hide mouse pointer (if not moved) */
292 if (gfx.cursor_mode != CURSOR_NONE &&
293 DelayReached(&special_cursor_delay, special_cursor_delay_value))
295 SetMouseCursor(CURSOR_NONE);
298 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
301 /* when playing, display a special mouse pointer inside the playfield */
303 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
304 cursor_inside_playfield &&
305 DelayReached(&special_cursor_delay, special_cursor_delay_value))
307 if (level.game_engine_type != GAME_ENGINE_TYPE_MM ||
309 SetMouseCursor(CURSOR_PLAYFIELD);
312 else if (gfx.cursor_mode != CURSOR_DEFAULT)
314 SetMouseCursor(CURSOR_DEFAULT);
317 /* this is set after all pending events have been processed */
318 cursor_mode_last = gfx.cursor_mode;
330 /* execute event related actions after pending events have been processed */
331 HandleEventActions();
333 /* don't use all CPU time when idle; the main loop while playing
334 has its own synchronization and is CPU friendly, too */
336 if (game_status == GAME_MODE_PLAYING)
339 /* always copy backbuffer to visible screen for every video frame */
342 /* reset video frame delay to default (may change again while playing) */
343 SetVideoFrameDelay(MenuFrameDelay);
345 if (game_status == GAME_MODE_QUIT)
350 void ClearEventQueue()
354 while (NextValidEvent(&event))
358 case EVENT_BUTTONRELEASE:
359 button_status = MB_RELEASED;
362 case EVENT_KEYRELEASE:
366 #if defined(TARGET_SDL2)
367 case SDL_CONTROLLERBUTTONUP:
368 HandleJoystickEvent(&event);
374 HandleOtherEvents(&event);
380 void ClearPlayerMouseAction()
382 local_player->mouse_action.lx = 0;
383 local_player->mouse_action.ly = 0;
384 local_player->mouse_action.button = 0;
387 void ClearPlayerAction()
391 /* simulate key release events for still pressed keys */
392 key_joystick_mapping = 0;
393 for (i = 0; i < MAX_PLAYERS; i++)
394 stored_player[i].action = 0;
396 ClearJoystickState();
397 ClearPlayerMouseAction();
400 void SetPlayerMouseAction(int mx, int my, int button)
402 int lx = getLevelFromScreenX(mx);
403 int ly = getLevelFromScreenY(my);
404 int new_button = (!local_player->mouse_action.button && button);
406 if (local_player->mouse_action.button_hint)
407 button = local_player->mouse_action.button_hint;
409 ClearPlayerMouseAction();
411 if (!IN_GFX_FIELD_PLAY(mx, my) || !IN_LEV_FIELD(lx, ly))
414 local_player->mouse_action.lx = lx;
415 local_player->mouse_action.ly = ly;
416 local_player->mouse_action.button = button;
418 if (tape.recording && tape.pausing && tape.use_mouse)
420 /* un-pause a paused game only if mouse button was newly pressed down */
422 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
425 SetTileCursorXY(lx, ly);
428 void SleepWhileUnmapped()
430 boolean window_unmapped = TRUE;
432 KeyboardAutoRepeatOn();
434 while (window_unmapped)
438 if (!WaitValidEvent(&event))
443 case EVENT_BUTTONRELEASE:
444 button_status = MB_RELEASED;
447 case EVENT_KEYRELEASE:
448 key_joystick_mapping = 0;
451 #if defined(TARGET_SDL2)
452 case SDL_CONTROLLERBUTTONUP:
453 HandleJoystickEvent(&event);
454 key_joystick_mapping = 0;
458 case EVENT_MAPNOTIFY:
459 window_unmapped = FALSE;
462 case EVENT_UNMAPNOTIFY:
463 /* this is only to surely prevent the 'should not happen' case
464 * of recursively looping between 'SleepWhileUnmapped()' and
465 * 'HandleOtherEvents()' which usually calls this funtion.
470 HandleOtherEvents(&event);
475 if (game_status == GAME_MODE_PLAYING)
476 KeyboardAutoRepeatOffUnlessAutoplay();
479 void HandleExposeEvent(ExposeEvent *event)
483 void HandleButtonEvent(ButtonEvent *event)
485 #if DEBUG_EVENTS_BUTTON
486 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
488 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
492 // for any mouse button event, disable playfield tile cursor
493 SetTileCursorEnabled(FALSE);
495 #if defined(HAS_SCREEN_KEYBOARD)
496 if (video.shifted_up)
497 event->y += video.shifted_up_pos;
500 motion_status = FALSE;
502 if (event->type == EVENT_BUTTONPRESS)
503 button_status = event->button;
505 button_status = MB_RELEASED;
507 HandleButton(event->x, event->y, button_status, event->button);
510 void HandleMotionEvent(MotionEvent *event)
512 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
515 motion_status = TRUE;
517 #if DEBUG_EVENTS_MOTION
518 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
519 button_status, event->x, event->y);
522 HandleButton(event->x, event->y, button_status, button_status);
525 #if defined(TARGET_SDL2)
527 void HandleWheelEvent(WheelEvent *event)
531 #if DEBUG_EVENTS_WHEEL
533 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
534 event->which, event->x, event->y);
536 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
537 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
538 event->which, event->x, event->y,
539 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
540 "SDL_MOUSEWHEEL_FLIPPED"));
544 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
545 event->x > 0 ? MB_WHEEL_RIGHT :
546 event->y < 0 ? MB_WHEEL_DOWN :
547 event->y > 0 ? MB_WHEEL_UP : 0);
549 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
550 // accelerated mouse wheel available on Mac and Windows
551 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
553 // no accelerated mouse wheel available on Unix/Linux
554 wheel_steps = DEFAULT_WHEEL_STEPS;
557 motion_status = FALSE;
559 button_status = button_nr;
560 HandleButton(0, 0, button_status, -button_nr);
562 button_status = MB_RELEASED;
563 HandleButton(0, 0, button_status, -button_nr);
566 void HandleWindowEvent(WindowEvent *event)
568 #if DEBUG_EVENTS_WINDOW
569 int subtype = event->event;
572 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
573 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
574 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
575 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
576 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
577 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
578 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
579 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
580 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
581 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
582 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
583 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
584 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
585 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
588 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
589 event_name, event->data1, event->data2);
593 // (not needed, as the screen gets redrawn every 20 ms anyway)
594 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
595 event->event == SDL_WINDOWEVENT_RESIZED ||
596 event->event == SDL_WINDOWEVENT_EXPOSED)
600 if (event->event == SDL_WINDOWEVENT_RESIZED)
602 if (!video.fullscreen_enabled)
604 int new_window_width = event->data1;
605 int new_window_height = event->data2;
607 // if window size has changed after resizing, calculate new scaling factor
608 if (new_window_width != video.window_width ||
609 new_window_height != video.window_height)
611 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
612 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
614 // (extreme window scaling allowed, but cannot be saved permanently)
615 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
616 setup.window_scaling_percent =
617 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
618 MAX_WINDOW_SCALING_PERCENT);
620 video.window_width = new_window_width;
621 video.window_height = new_window_height;
623 if (game_status == GAME_MODE_SETUP)
624 RedrawSetupScreenAfterFullscreenToggle();
629 #if defined(PLATFORM_ANDROID)
632 int new_display_width = event->data1;
633 int new_display_height = event->data2;
635 // if fullscreen display size has changed, device has been rotated
636 if (new_display_width != video.display_width ||
637 new_display_height != video.display_height)
639 int nr = GRID_ACTIVE_NR(); // previous screen orientation
641 video.display_width = new_display_width;
642 video.display_height = new_display_height;
644 SDLSetScreenProperties();
646 // check if screen orientation has changed (should always be true here)
647 if (nr != GRID_ACTIVE_NR())
651 if (game_status == GAME_MODE_SETUP)
652 RedrawSetupScreenAfterScreenRotation(nr);
654 nr = GRID_ACTIVE_NR();
656 overlay.grid_xsize = setup.touch.grid_xsize[nr];
657 overlay.grid_ysize = setup.touch.grid_ysize[nr];
659 for (x = 0; x < MAX_GRID_XSIZE; x++)
660 for (y = 0; y < MAX_GRID_YSIZE; y++)
661 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
669 #define NUM_TOUCH_FINGERS 3
674 SDL_FingerID finger_id;
677 } touch_info[NUM_TOUCH_FINGERS];
679 void HandleFingerEvent_VirtualButtons(FingerEvent *event)
682 int x = event->x * overlay.grid_xsize;
683 int y = event->y * overlay.grid_ysize;
684 int grid_button = overlay.grid_button[x][y];
685 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
686 Key key = (grid_button == CHAR_GRID_BUTTON_LEFT ? setup.input[0].key.left :
687 grid_button == CHAR_GRID_BUTTON_RIGHT ? setup.input[0].key.right :
688 grid_button == CHAR_GRID_BUTTON_UP ? setup.input[0].key.up :
689 grid_button == CHAR_GRID_BUTTON_DOWN ? setup.input[0].key.down :
690 grid_button == CHAR_GRID_BUTTON_SNAP ? setup.input[0].key.snap :
691 grid_button == CHAR_GRID_BUTTON_DROP ? setup.input[0].key.drop :
694 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
695 float event_x = (event->x);
696 float event_y = (event->y - ypos) / (1 - ypos);
697 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
698 event_y > 2.0 / 3.0 && event_y < 1 ?
699 setup.input[0].key.snap :
700 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
701 event_y > 2.0 / 3.0 && event_y < 1 ?
702 setup.input[0].key.drop :
703 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
704 event_y > 0 && event_y < 1.0 / 3.0 ?
705 setup.input[0].key.up :
706 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
707 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
708 setup.input[0].key.left :
709 event_x > 8.0 / 9.0 && event_x < 1 &&
710 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
711 setup.input[0].key.right :
712 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
713 event_y > 2.0 / 3.0 && event_y < 1 ?
714 setup.input[0].key.down :
717 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
719 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
723 // for any touch input event, enable overlay buttons (if activated)
724 SetOverlayEnabled(TRUE);
726 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
727 getKeyNameFromKey(key), key_status_name, event->fingerId);
729 if (key_status == KEY_PRESSED)
730 overlay.grid_button_action |= grid_button_action;
732 overlay.grid_button_action &= ~grid_button_action;
734 // check if we already know this touch event's finger id
735 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
737 if (touch_info[i].touched &&
738 touch_info[i].finger_id == event->fingerId)
740 // Error(ERR_DEBUG, "MARK 1: %d", i);
746 if (i >= NUM_TOUCH_FINGERS)
748 if (key_status == KEY_PRESSED)
750 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
752 // unknown finger id -- get new, empty slot, if available
753 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
755 if (touch_info[i].counter < oldest_counter)
758 oldest_counter = touch_info[i].counter;
760 // Error(ERR_DEBUG, "MARK 2: %d", i);
763 if (!touch_info[i].touched)
765 // Error(ERR_DEBUG, "MARK 3: %d", i);
771 if (i >= NUM_TOUCH_FINGERS)
773 // all slots allocated -- use oldest slot
776 // Error(ERR_DEBUG, "MARK 4: %d", i);
781 // release of previously unknown key (should not happen)
783 if (key != KSYM_UNDEFINED)
785 HandleKey(key, KEY_RELEASED);
787 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
788 getKeyNameFromKey(key), "KEY_RELEASED", i);
793 if (i < NUM_TOUCH_FINGERS)
795 if (key_status == KEY_PRESSED)
797 if (touch_info[i].key != key)
799 if (touch_info[i].key != KSYM_UNDEFINED)
801 HandleKey(touch_info[i].key, KEY_RELEASED);
803 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
804 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
807 if (key != KSYM_UNDEFINED)
809 HandleKey(key, KEY_PRESSED);
811 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
812 getKeyNameFromKey(key), "KEY_PRESSED", i);
816 touch_info[i].touched = TRUE;
817 touch_info[i].finger_id = event->fingerId;
818 touch_info[i].counter = Counter();
819 touch_info[i].key = key;
823 if (touch_info[i].key != KSYM_UNDEFINED)
825 HandleKey(touch_info[i].key, KEY_RELEASED);
827 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
828 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
831 touch_info[i].touched = FALSE;
832 touch_info[i].finger_id = 0;
833 touch_info[i].counter = 0;
834 touch_info[i].key = 0;
839 void HandleFingerEvent_WipeGestures(FingerEvent *event)
841 static Key motion_key_x = KSYM_UNDEFINED;
842 static Key motion_key_y = KSYM_UNDEFINED;
843 static Key button_key = KSYM_UNDEFINED;
844 static float motion_x1, motion_y1;
845 static float button_x1, button_y1;
846 static SDL_FingerID motion_id = -1;
847 static SDL_FingerID button_id = -1;
848 int move_trigger_distance_percent = setup.touch.move_distance;
849 int drop_trigger_distance_percent = setup.touch.drop_distance;
850 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
851 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
852 float event_x = event->x;
853 float event_y = event->y;
855 if (event->type == EVENT_FINGERPRESS)
857 if (event_x > 1.0 / 3.0)
861 motion_id = event->fingerId;
866 motion_key_x = KSYM_UNDEFINED;
867 motion_key_y = KSYM_UNDEFINED;
869 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
875 button_id = event->fingerId;
880 button_key = setup.input[0].key.snap;
882 HandleKey(button_key, KEY_PRESSED);
884 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
887 else if (event->type == EVENT_FINGERRELEASE)
889 if (event->fingerId == motion_id)
893 if (motion_key_x != KSYM_UNDEFINED)
894 HandleKey(motion_key_x, KEY_RELEASED);
895 if (motion_key_y != KSYM_UNDEFINED)
896 HandleKey(motion_key_y, KEY_RELEASED);
898 motion_key_x = KSYM_UNDEFINED;
899 motion_key_y = KSYM_UNDEFINED;
901 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
903 else if (event->fingerId == button_id)
907 if (button_key != KSYM_UNDEFINED)
908 HandleKey(button_key, KEY_RELEASED);
910 button_key = KSYM_UNDEFINED;
912 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
915 else if (event->type == EVENT_FINGERMOTION)
917 if (event->fingerId == motion_id)
919 float distance_x = ABS(event_x - motion_x1);
920 float distance_y = ABS(event_y - motion_y1);
921 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
922 event_x > motion_x1 ? setup.input[0].key.right :
924 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
925 event_y > motion_y1 ? setup.input[0].key.down :
928 if (distance_x < move_trigger_distance / 2 ||
929 distance_x < distance_y)
930 new_motion_key_x = KSYM_UNDEFINED;
932 if (distance_y < move_trigger_distance / 2 ||
933 distance_y < distance_x)
934 new_motion_key_y = KSYM_UNDEFINED;
936 if (distance_x > move_trigger_distance ||
937 distance_y > move_trigger_distance)
939 if (new_motion_key_x != motion_key_x)
941 if (motion_key_x != KSYM_UNDEFINED)
942 HandleKey(motion_key_x, KEY_RELEASED);
943 if (new_motion_key_x != KSYM_UNDEFINED)
944 HandleKey(new_motion_key_x, KEY_PRESSED);
947 if (new_motion_key_y != motion_key_y)
949 if (motion_key_y != KSYM_UNDEFINED)
950 HandleKey(motion_key_y, KEY_RELEASED);
951 if (new_motion_key_y != KSYM_UNDEFINED)
952 HandleKey(new_motion_key_y, KEY_PRESSED);
958 motion_key_x = new_motion_key_x;
959 motion_key_y = new_motion_key_y;
961 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
964 else if (event->fingerId == button_id)
966 float distance_x = ABS(event_x - button_x1);
967 float distance_y = ABS(event_y - button_y1);
969 if (distance_x < drop_trigger_distance / 2 &&
970 distance_y > drop_trigger_distance)
972 if (button_key == setup.input[0].key.snap)
973 HandleKey(button_key, KEY_RELEASED);
978 button_key = setup.input[0].key.drop;
980 HandleKey(button_key, KEY_PRESSED);
982 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
988 void HandleFingerEvent(FingerEvent *event)
990 #if DEBUG_EVENTS_FINGER
991 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
992 event->type == EVENT_FINGERPRESS ? "pressed" :
993 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
997 event->dx, event->dy,
1001 if (game_status != GAME_MODE_PLAYING)
1004 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1006 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_OFF))
1007 local_player->mouse_action.button_hint =
1008 (event->type == EVENT_FINGERRELEASE ? MB_NOT_PRESSED :
1009 event->x < 0.5 ? MB_LEFTBUTTON :
1010 event->x > 0.5 ? MB_RIGHTBUTTON :
1016 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1017 HandleFingerEvent_VirtualButtons(event);
1018 else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
1019 HandleFingerEvent_WipeGestures(event);
1024 static void HandleButtonOrFinger_WipeGestures_MM(int mx, int my, int button)
1026 static int old_mx = 0, old_my = 0;
1027 static int last_button = MB_LEFTBUTTON;
1028 static boolean touched = FALSE;
1029 static boolean tapped = FALSE;
1031 // screen tile was tapped (but finger not touching the screen anymore)
1032 // (this point will also be reached without receiving a touch event)
1033 if (tapped && !touched)
1035 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1040 // stop here if this function was not triggered by a touch event
1044 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
1046 // finger started touching the screen
1056 ClearPlayerMouseAction();
1058 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
1061 else if (button == MB_RELEASED && touched)
1063 // finger stopped touching the screen
1068 SetPlayerMouseAction(old_mx, old_my, last_button);
1070 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1072 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
1077 // finger moved while touching the screen
1079 int old_x = getLevelFromScreenX(old_mx);
1080 int old_y = getLevelFromScreenY(old_my);
1081 int new_x = getLevelFromScreenX(mx);
1082 int new_y = getLevelFromScreenY(my);
1084 if (new_x != old_x || new_y != old_y)
1089 // finger moved left or right from (horizontal) starting position
1091 int button_nr = (new_x < old_x ? MB_LEFTBUTTON : MB_RIGHTBUTTON);
1093 SetPlayerMouseAction(old_mx, old_my, button_nr);
1095 last_button = button_nr;
1097 Error(ERR_DEBUG, "---------- TOUCH ACTION: ROTATING ----------");
1101 // finger stays at or returned to (horizontal) starting position
1103 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1105 Error(ERR_DEBUG, "---------- TOUCH ACTION PAUSED ----------");
1110 static void HandleButtonOrFinger_FollowFinger_MM(int mx, int my, int button)
1112 static int old_mx = 0, old_my = 0;
1113 static int last_button = MB_LEFTBUTTON;
1114 static boolean touched = FALSE;
1115 static boolean tapped = FALSE;
1117 // screen tile was tapped (but finger not touching the screen anymore)
1118 // (this point will also be reached without receiving a touch event)
1119 if (tapped && !touched)
1121 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1126 // stop here if this function was not triggered by a touch event
1130 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
1132 // finger started touching the screen
1142 ClearPlayerMouseAction();
1144 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
1147 else if (button == MB_RELEASED && touched)
1149 // finger stopped touching the screen
1154 SetPlayerMouseAction(old_mx, old_my, last_button);
1156 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1158 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
1163 // finger moved while touching the screen
1165 int old_x = getLevelFromScreenX(old_mx);
1166 int old_y = getLevelFromScreenY(old_my);
1167 int new_x = getLevelFromScreenX(mx);
1168 int new_y = getLevelFromScreenY(my);
1170 if (new_x != old_x || new_y != old_y)
1172 // finger moved away from starting position
1174 int button_nr = getButtonFromTouchPosition(old_x, old_y, mx, my);
1176 // quickly alternate between clicking and releasing for maximum speed
1177 if (FrameCounter % 2 == 0)
1178 button_nr = MB_RELEASED;
1180 SetPlayerMouseAction(old_mx, old_my, button_nr);
1183 last_button = button_nr;
1187 Error(ERR_DEBUG, "---------- TOUCH ACTION: ROTATING ----------");
1191 // finger stays at or returned to starting position
1193 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1195 Error(ERR_DEBUG, "---------- TOUCH ACTION PAUSED ----------");
1200 static void HandleButtonOrFinger_FollowFinger(int mx, int my, int button)
1202 static int old_mx = 0, old_my = 0;
1203 static Key motion_key_x = KSYM_UNDEFINED;
1204 static Key motion_key_y = KSYM_UNDEFINED;
1205 static boolean touched = FALSE;
1206 static boolean started_on_player = FALSE;
1207 static boolean player_is_dropping = FALSE;
1208 static int player_drop_count = 0;
1209 static int last_player_x = -1;
1210 static int last_player_y = -1;
1212 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
1221 started_on_player = FALSE;
1222 player_is_dropping = FALSE;
1223 player_drop_count = 0;
1227 motion_key_x = KSYM_UNDEFINED;
1228 motion_key_y = KSYM_UNDEFINED;
1230 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
1233 else if (button == MB_RELEASED && touched)
1240 if (motion_key_x != KSYM_UNDEFINED)
1241 HandleKey(motion_key_x, KEY_RELEASED);
1242 if (motion_key_y != KSYM_UNDEFINED)
1243 HandleKey(motion_key_y, KEY_RELEASED);
1245 if (started_on_player)
1247 if (player_is_dropping)
1249 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
1251 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1255 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
1257 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
1261 motion_key_x = KSYM_UNDEFINED;
1262 motion_key_y = KSYM_UNDEFINED;
1264 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
1269 int src_x = local_player->jx;
1270 int src_y = local_player->jy;
1271 int dst_x = getLevelFromScreenX(old_mx);
1272 int dst_y = getLevelFromScreenY(old_my);
1273 int dx = dst_x - src_x;
1274 int dy = dst_y - src_y;
1275 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
1276 dx > 0 ? setup.input[0].key.right :
1278 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
1279 dy > 0 ? setup.input[0].key.down :
1282 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1283 (last_player_x != local_player->jx ||
1284 last_player_y != local_player->jy))
1286 // in case of asymmetric diagonal movement, use "preferred" direction
1288 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1290 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1291 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1293 local_player->last_move_dir = last_move_dir;
1295 // (required to prevent accidentally forcing direction for next movement)
1296 last_player_x = local_player->jx;
1297 last_player_y = local_player->jy;
1300 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1302 started_on_player = TRUE;
1303 player_drop_count = getPlayerInventorySize(0);
1304 player_is_dropping = (player_drop_count > 0);
1306 if (player_is_dropping)
1308 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1310 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1314 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1316 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1319 else if (dx != 0 || dy != 0)
1321 if (player_is_dropping &&
1322 player_drop_count == getPlayerInventorySize(0))
1324 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1326 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1327 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1329 player_is_dropping = FALSE;
1333 if (new_motion_key_x != motion_key_x)
1335 Error(ERR_DEBUG, "---------- %s %s ----------",
1336 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1337 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1339 if (motion_key_x != KSYM_UNDEFINED)
1340 HandleKey(motion_key_x, KEY_RELEASED);
1341 if (new_motion_key_x != KSYM_UNDEFINED)
1342 HandleKey(new_motion_key_x, KEY_PRESSED);
1345 if (new_motion_key_y != motion_key_y)
1347 Error(ERR_DEBUG, "---------- %s %s ----------",
1348 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1349 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1351 if (motion_key_y != KSYM_UNDEFINED)
1352 HandleKey(motion_key_y, KEY_RELEASED);
1353 if (new_motion_key_y != KSYM_UNDEFINED)
1354 HandleKey(new_motion_key_y, KEY_PRESSED);
1357 motion_key_x = new_motion_key_x;
1358 motion_key_y = new_motion_key_y;
1362 static void HandleButtonOrFinger(int mx, int my, int button)
1364 if (game_status != GAME_MODE_PLAYING)
1367 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1369 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
1370 HandleButtonOrFinger_WipeGestures_MM(mx, my, button);
1371 else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
1372 HandleButtonOrFinger_FollowFinger_MM(mx, my, button);
1373 else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1374 SetPlayerMouseAction(mx, my, button); /* special case */
1378 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
1379 HandleButtonOrFinger_FollowFinger(mx, my, button);
1383 #if defined(TARGET_SDL2)
1385 static boolean checkTextInputKeyModState()
1387 // when playing, only handle raw key events and ignore text input
1388 if (game_status == GAME_MODE_PLAYING)
1391 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1394 void HandleTextEvent(TextEvent *event)
1396 char *text = event->text;
1397 Key key = getKeyFromKeyName(text);
1399 #if DEBUG_EVENTS_TEXT
1400 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1403 text[0], (int)(text[0]),
1405 getKeyNameFromKey(key),
1409 #if !defined(HAS_SCREEN_KEYBOARD)
1410 // non-mobile devices: only handle key input with modifier keys pressed here
1411 // (every other key input is handled directly as physical key input event)
1412 if (!checkTextInputKeyModState())
1416 // process text input as "classic" (with uppercase etc.) key input event
1417 HandleKey(key, KEY_PRESSED);
1418 HandleKey(key, KEY_RELEASED);
1421 void HandlePauseResumeEvent(PauseResumeEvent *event)
1423 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1427 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1435 void HandleKeyEvent(KeyEvent *event)
1437 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1438 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1439 Key key = GetEventKey(event, with_modifiers);
1440 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1442 #if DEBUG_EVENTS_KEY
1443 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1444 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1445 event->keysym.scancode,
1450 getKeyNameFromKey(key));
1453 #if defined(PLATFORM_ANDROID)
1454 if (key == KSYM_Back)
1456 // always map the "back" button to the "escape" key on Android devices
1461 // for any key event other than "back" button, disable overlay buttons
1462 SetOverlayEnabled(FALSE);
1466 HandleKeyModState(keymod, key_status);
1468 #if defined(TARGET_SDL2)
1469 // only handle raw key input without text modifier keys pressed
1470 if (!checkTextInputKeyModState())
1471 HandleKey(key, key_status);
1473 HandleKey(key, key_status);
1477 void HandleFocusEvent(FocusChangeEvent *event)
1479 static int old_joystick_status = -1;
1481 if (event->type == EVENT_FOCUSOUT)
1483 KeyboardAutoRepeatOn();
1484 old_joystick_status = joystick.status;
1485 joystick.status = JOYSTICK_NOT_AVAILABLE;
1487 ClearPlayerAction();
1489 else if (event->type == EVENT_FOCUSIN)
1491 /* When there are two Rocks'n'Diamonds windows which overlap and
1492 the player moves the pointer from one game window to the other,
1493 a 'FocusOut' event is generated for the window the pointer is
1494 leaving and a 'FocusIn' event is generated for the window the
1495 pointer is entering. In some cases, it can happen that the
1496 'FocusIn' event is handled by the one game process before the
1497 'FocusOut' event by the other game process. In this case the
1498 X11 environment would end up with activated keyboard auto repeat,
1499 because unfortunately this is a global setting and not (which
1500 would be far better) set for each X11 window individually.
1501 The effect would be keyboard auto repeat while playing the game
1502 (game_status == GAME_MODE_PLAYING), which is not desired.
1503 To avoid this special case, we just wait 1/10 second before
1504 processing the 'FocusIn' event.
1507 if (game_status == GAME_MODE_PLAYING)
1510 KeyboardAutoRepeatOffUnlessAutoplay();
1513 if (old_joystick_status != -1)
1514 joystick.status = old_joystick_status;
1518 void HandleClientMessageEvent(ClientMessageEvent *event)
1520 if (CheckCloseWindowEvent(event))
1524 void HandleWindowManagerEvent(Event *event)
1526 #if defined(TARGET_SDL)
1527 SDLHandleWindowManagerEvent(event);
1531 void HandleButton(int mx, int my, int button, int button_nr)
1533 static int old_mx = 0, old_my = 0;
1534 boolean button_hold = FALSE;
1535 boolean handle_gadgets = TRUE;
1541 button_nr = -button_nr;
1550 #if defined(PLATFORM_ANDROID)
1551 // when playing, only handle gadgets when using "follow finger" controls
1552 // or when using touch controls in combination with the MM game engine
1554 (game_status != GAME_MODE_PLAYING ||
1555 level.game_engine_type == GAME_ENGINE_TYPE_MM ||
1556 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1559 if (HandleGlobalAnimClicks(mx, my, button))
1561 /* do not handle this button event anymore */
1562 return; /* force mouse event not to be handled at all */
1565 if (handle_gadgets && HandleGadgets(mx, my, button))
1567 /* do not handle this button event anymore */
1568 mx = my = -32; /* force mouse event to be outside screen tiles */
1571 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1574 /* do not use scroll wheel button events for anything other than gadgets */
1575 if (IS_WHEEL_BUTTON(button_nr))
1578 switch (game_status)
1580 case GAME_MODE_TITLE:
1581 HandleTitleScreen(mx, my, 0, 0, button);
1584 case GAME_MODE_MAIN:
1585 HandleMainMenu(mx, my, 0, 0, button);
1588 case GAME_MODE_PSEUDO_TYPENAME:
1589 HandleTypeName(0, KSYM_Return);
1592 case GAME_MODE_LEVELS:
1593 HandleChooseLevelSet(mx, my, 0, 0, button);
1596 case GAME_MODE_LEVELNR:
1597 HandleChooseLevelNr(mx, my, 0, 0, button);
1600 case GAME_MODE_SCORES:
1601 HandleHallOfFame(0, 0, 0, 0, button);
1604 case GAME_MODE_EDITOR:
1605 HandleLevelEditorIdle();
1608 case GAME_MODE_INFO:
1609 HandleInfoScreen(mx, my, 0, 0, button);
1612 case GAME_MODE_SETUP:
1613 HandleSetupScreen(mx, my, 0, 0, button);
1616 case GAME_MODE_PLAYING:
1617 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_OFF))
1618 HandleButtonOrFinger(mx, my, button);
1620 SetPlayerMouseAction(mx, my, button);
1623 if (button == MB_PRESSED && !motion_status && !button_hold &&
1624 IN_GFX_FIELD_PLAY(mx, my) && GetKeyModState() & KMOD_Control)
1625 DumpTileFromScreen(mx, my);
1635 static boolean is_string_suffix(char *string, char *suffix)
1637 int string_len = strlen(string);
1638 int suffix_len = strlen(suffix);
1640 if (suffix_len > string_len)
1643 return (strEqual(&string[string_len - suffix_len], suffix));
1646 #define MAX_CHEAT_INPUT_LEN 32
1648 static void HandleKeysSpecial(Key key)
1650 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1651 char letter = getCharFromKey(key);
1652 int cheat_input_len = strlen(cheat_input);
1658 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1660 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1661 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1663 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1666 cheat_input[cheat_input_len++] = letter;
1667 cheat_input[cheat_input_len] = '\0';
1669 #if DEBUG_EVENTS_KEY
1670 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1673 if (game_status == GAME_MODE_MAIN)
1675 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1676 is_string_suffix(cheat_input, ":ist"))
1678 InsertSolutionTape();
1680 else if (is_string_suffix(cheat_input, ":play-solution-tape") ||
1681 is_string_suffix(cheat_input, ":pst"))
1685 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1686 is_string_suffix(cheat_input, ":rg"))
1688 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1691 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1692 is_string_suffix(cheat_input, ":rs"))
1694 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1697 else if (is_string_suffix(cheat_input, ":reload-music") ||
1698 is_string_suffix(cheat_input, ":rm"))
1700 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1703 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1704 is_string_suffix(cheat_input, ":ra"))
1706 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1707 1 << ARTWORK_TYPE_SOUNDS |
1708 1 << ARTWORK_TYPE_MUSIC);
1711 else if (is_string_suffix(cheat_input, ":dump-level") ||
1712 is_string_suffix(cheat_input, ":dl"))
1716 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1717 is_string_suffix(cheat_input, ":dt"))
1721 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1722 is_string_suffix(cheat_input, ":ft"))
1724 /* fix single-player tapes that contain player input for more than one
1725 player (due to a bug in 3.3.1.2 and earlier versions), which results
1726 in playing levels with more than one player in multi-player mode,
1727 even though the tape was originally recorded in single-player mode */
1729 /* remove player input actions for all players but the first one */
1730 for (i = 1; i < MAX_PLAYERS; i++)
1731 tape.player_participates[i] = FALSE;
1733 tape.changed = TRUE;
1735 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1736 is_string_suffix(cheat_input, ":snl"))
1738 SaveNativeLevel(&level);
1740 else if (is_string_suffix(cheat_input, ":frames-per-second") ||
1741 is_string_suffix(cheat_input, ":fps"))
1743 global.show_frames_per_second = !global.show_frames_per_second;
1746 else if (game_status == GAME_MODE_PLAYING)
1749 if (is_string_suffix(cheat_input, ".q"))
1750 DEBUG_SetMaximumDynamite();
1753 else if (game_status == GAME_MODE_EDITOR)
1755 if (is_string_suffix(cheat_input, ":dump-brush") ||
1756 is_string_suffix(cheat_input, ":DB"))
1760 else if (is_string_suffix(cheat_input, ":DDB"))
1766 /* special key shortcuts for all game modes */
1767 if (is_string_suffix(cheat_input, ":dump-event-actions") ||
1768 is_string_suffix(cheat_input, ":dea") ||
1769 is_string_suffix(cheat_input, ":DEA"))
1771 DumpGadgetIdentifiers();
1772 DumpScreenIdentifiers();
1776 void HandleKeysDebug(Key key)
1781 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1783 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
1785 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1787 if (key == setup.debug.frame_delay_key[i] &&
1788 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1790 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1791 setup.debug.frame_delay[i] : setup.game_frame_delay);
1793 if (!setup.debug.frame_delay_game_only)
1794 MenuFrameDelay = GameFrameDelay;
1796 SetVideoFrameDelay(GameFrameDelay);
1798 if (GameFrameDelay > ONE_SECOND_DELAY)
1799 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1800 else if (GameFrameDelay != 0)
1801 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1802 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1803 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1805 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1812 if (game_status == GAME_MODE_PLAYING)
1816 options.debug = !options.debug;
1818 Error(ERR_DEBUG, "debug mode %s",
1819 (options.debug ? "enabled" : "disabled"));
1821 else if (key == KSYM_v)
1823 Error(ERR_DEBUG, "currently using game engine version %d",
1824 game.engine_version);
1830 void HandleKey(Key key, int key_status)
1832 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1833 static boolean ignore_repeated_key = FALSE;
1834 static struct SetupKeyboardInfo ski;
1835 static struct SetupShortcutInfo ssi;
1844 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1845 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1846 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1847 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1848 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1849 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1854 #if defined(TARGET_SDL2)
1855 /* map special keys (media keys / remote control buttons) to default keys */
1856 if (key == KSYM_PlayPause)
1858 else if (key == KSYM_Select)
1862 HandleSpecialGameControllerKeys(key, key_status);
1864 if (game_status == GAME_MODE_PLAYING)
1866 /* only needed for single-step tape recording mode */
1867 static boolean has_snapped[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE };
1870 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1872 byte key_action = 0;
1874 if (setup.input[pnr].use_joystick)
1877 ski = setup.input[pnr].key;
1879 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1880 if (key == *key_info[i].key_custom)
1881 key_action |= key_info[i].action;
1883 /* use combined snap+direction keys for the first player only */
1886 ssi = setup.shortcut;
1888 for (i = 0; i < NUM_DIRECTIONS; i++)
1889 if (key == *key_info[i].key_snap)
1890 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1893 if (key_status == KEY_PRESSED)
1894 stored_player[pnr].action |= key_action;
1896 stored_player[pnr].action &= ~key_action;
1898 if (tape.single_step && tape.recording && tape.pausing && !tape.use_mouse)
1900 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1902 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1904 /* if snap key already pressed, keep pause mode when releasing */
1905 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1906 has_snapped[pnr] = TRUE;
1908 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1910 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1912 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1913 getRedDiskReleaseFlag_SP() == 0)
1915 /* add a single inactive frame before dropping starts */
1916 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1917 stored_player[pnr].force_dropping = TRUE;
1920 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON_SNAP)
1922 /* if snap key was pressed without direction, leave pause mode */
1923 if (!has_snapped[pnr])
1924 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1926 has_snapped[pnr] = FALSE;
1929 else if (tape.recording && tape.pausing && !tape.use_mouse)
1931 /* prevent key release events from un-pausing a paused game */
1932 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1933 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1936 // for MM style levels, handle in-game keyboard input in HandleJoystick()
1937 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1943 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1944 if (key == key_info[i].key_default)
1945 joy |= key_info[i].action;
1950 if (key_status == KEY_PRESSED)
1951 key_joystick_mapping |= joy;
1953 key_joystick_mapping &= ~joy;
1958 if (game_status != GAME_MODE_PLAYING)
1959 key_joystick_mapping = 0;
1961 if (key_status == KEY_RELEASED)
1963 // reset flag to ignore repeated "key pressed" events after key release
1964 ignore_repeated_key = FALSE;
1969 if ((key == KSYM_F11 ||
1970 ((key == KSYM_Return ||
1971 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1972 video.fullscreen_available &&
1973 !ignore_repeated_key)
1975 setup.fullscreen = !setup.fullscreen;
1977 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1979 if (game_status == GAME_MODE_SETUP)
1980 RedrawSetupScreenAfterFullscreenToggle();
1982 // set flag to ignore repeated "key pressed" events
1983 ignore_repeated_key = TRUE;
1988 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1989 key == KSYM_minus || key == KSYM_KP_Subtract ||
1990 key == KSYM_plus || key == KSYM_KP_Add ||
1991 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1992 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1993 video.window_scaling_available &&
1994 !video.fullscreen_enabled)
1996 if (key == KSYM_0 || key == KSYM_KP_0)
1997 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1998 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1999 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
2001 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
2003 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
2004 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
2005 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
2006 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
2008 ToggleFullscreenOrChangeWindowScalingIfNeeded();
2010 if (game_status == GAME_MODE_SETUP)
2011 RedrawSetupScreenAfterFullscreenToggle();
2016 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
2017 key == KSYM_Return ||
2018 key == KSYM_Escape)))
2020 /* do not handle this key event anymore */
2021 if (key != KSYM_Escape) /* always allow ESC key to be handled */
2025 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
2026 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
2033 if (game_status == GAME_MODE_MAIN &&
2034 (key == setup.shortcut.toggle_pause || key == KSYM_space))
2036 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
2041 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
2043 if (key == setup.shortcut.save_game)
2045 else if (key == setup.shortcut.load_game)
2047 else if (key == setup.shortcut.toggle_pause)
2048 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
2050 HandleTapeButtonKeys(key);
2051 HandleSoundButtonKeys(key);
2054 if (game_status == GAME_MODE_PLAYING && !network_playing)
2056 int centered_player_nr_next = -999;
2058 if (key == setup.shortcut.focus_player_all)
2059 centered_player_nr_next = -1;
2061 for (i = 0; i < MAX_PLAYERS; i++)
2062 if (key == setup.shortcut.focus_player[i])
2063 centered_player_nr_next = i;
2065 if (centered_player_nr_next != -999)
2067 game.centered_player_nr_next = centered_player_nr_next;
2068 game.set_centered_player = TRUE;
2072 tape.centered_player_nr_next = game.centered_player_nr_next;
2073 tape.set_centered_player = TRUE;
2078 HandleKeysSpecial(key);
2080 if (HandleGadgetsKeyInput(key))
2082 if (key != KSYM_Escape) /* always allow ESC key to be handled */
2083 key = KSYM_UNDEFINED;
2086 switch (game_status)
2088 case GAME_MODE_PSEUDO_TYPENAME:
2089 HandleTypeName(0, key);
2092 case GAME_MODE_TITLE:
2093 case GAME_MODE_MAIN:
2094 case GAME_MODE_LEVELS:
2095 case GAME_MODE_LEVELNR:
2096 case GAME_MODE_SETUP:
2097 case GAME_MODE_INFO:
2098 case GAME_MODE_SCORES:
2103 if (game_status == GAME_MODE_TITLE)
2104 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2105 else if (game_status == GAME_MODE_MAIN)
2106 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
2107 else if (game_status == GAME_MODE_LEVELS)
2108 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
2109 else if (game_status == GAME_MODE_LEVELNR)
2110 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
2111 else if (game_status == GAME_MODE_SETUP)
2112 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2113 else if (game_status == GAME_MODE_INFO)
2114 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2115 else if (game_status == GAME_MODE_SCORES)
2116 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
2120 if (game_status != GAME_MODE_MAIN)
2121 FadeSkipNextFadeIn();
2123 if (game_status == GAME_MODE_TITLE)
2124 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2125 else if (game_status == GAME_MODE_LEVELS)
2126 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
2127 else if (game_status == GAME_MODE_LEVELNR)
2128 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
2129 else if (game_status == GAME_MODE_SETUP)
2130 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2131 else if (game_status == GAME_MODE_INFO)
2132 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2133 else if (game_status == GAME_MODE_SCORES)
2134 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
2138 if (game_status == GAME_MODE_LEVELS)
2139 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2140 else if (game_status == GAME_MODE_LEVELNR)
2141 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2142 else if (game_status == GAME_MODE_SETUP)
2143 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2144 else if (game_status == GAME_MODE_INFO)
2145 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2146 else if (game_status == GAME_MODE_SCORES)
2147 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2150 case KSYM_Page_Down:
2151 if (game_status == GAME_MODE_LEVELS)
2152 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2153 else if (game_status == GAME_MODE_LEVELNR)
2154 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2155 else if (game_status == GAME_MODE_SETUP)
2156 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2157 else if (game_status == GAME_MODE_INFO)
2158 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2159 else if (game_status == GAME_MODE_SCORES)
2160 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2168 case GAME_MODE_EDITOR:
2169 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
2170 HandleLevelEditorKeyInput(key);
2173 case GAME_MODE_PLAYING:
2178 RequestQuitGame(setup.ask_on_escape);
2188 if (key == KSYM_Escape)
2190 SetGameStatus(GAME_MODE_MAIN);
2198 HandleKeysDebug(key);
2201 void HandleNoEvent()
2203 HandleMouseCursor();
2205 switch (game_status)
2207 case GAME_MODE_PLAYING:
2208 HandleButtonOrFinger(-1, -1, -1);
2213 void HandleEventActions()
2215 // if (button_status && game_status != GAME_MODE_PLAYING)
2216 if (button_status && (game_status != GAME_MODE_PLAYING ||
2218 level.game_engine_type == GAME_ENGINE_TYPE_MM))
2220 HandleButton(0, 0, button_status, -button_status);
2227 if (network.enabled)
2230 switch (game_status)
2232 case GAME_MODE_MAIN:
2233 DrawPreviewLevelAnimation();
2236 case GAME_MODE_EDITOR:
2237 HandleLevelEditorIdle();
2245 static void HandleTileCursor(int dx, int dy, int button)
2248 ClearPlayerMouseAction();
2255 SetPlayerMouseAction(tile_cursor.x, tile_cursor.y,
2256 (dx < 0 ? MB_LEFTBUTTON :
2257 dx > 0 ? MB_RIGHTBUTTON : MB_RELEASED));
2259 else if (!tile_cursor.moving)
2261 int old_xpos = tile_cursor.xpos;
2262 int old_ypos = tile_cursor.ypos;
2263 int new_xpos = old_xpos;
2264 int new_ypos = old_ypos;
2266 if (IN_LEV_FIELD(old_xpos + dx, old_ypos))
2267 new_xpos = old_xpos + dx;
2269 if (IN_LEV_FIELD(old_xpos, old_ypos + dy))
2270 new_ypos = old_ypos + dy;
2272 SetTileCursorTargetXY(new_xpos, new_ypos);
2276 static int HandleJoystickForAllPlayers()
2280 boolean no_joysticks_configured = TRUE;
2281 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
2282 static byte joy_action_last[MAX_PLAYERS];
2284 for (i = 0; i < MAX_PLAYERS; i++)
2285 if (setup.input[i].use_joystick)
2286 no_joysticks_configured = FALSE;
2288 /* if no joysticks configured, map connected joysticks to players */
2289 if (no_joysticks_configured)
2290 use_as_joystick_nr = TRUE;
2292 for (i = 0; i < MAX_PLAYERS; i++)
2294 byte joy_action = 0;
2296 joy_action = JoystickExt(i, use_as_joystick_nr);
2297 result |= joy_action;
2299 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
2300 joy_action != joy_action_last[i])
2301 stored_player[i].action = joy_action;
2303 joy_action_last[i] = joy_action;
2309 void HandleJoystick()
2311 static unsigned int joytest_delay = 0;
2312 static unsigned int joytest_delay_value = GADGET_FRAME_DELAY;
2313 static int joytest_last = 0;
2314 int delay_value_first = GADGET_FRAME_DELAY_FIRST;
2315 int delay_value = GADGET_FRAME_DELAY;
2316 int joystick = HandleJoystickForAllPlayers();
2317 int keyboard = key_joystick_mapping;
2318 int joy = (joystick | keyboard);
2319 int joytest = joystick;
2320 int left = joy & JOY_LEFT;
2321 int right = joy & JOY_RIGHT;
2322 int up = joy & JOY_UP;
2323 int down = joy & JOY_DOWN;
2324 int button = joy & JOY_BUTTON;
2325 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
2326 int dx = (left ? -1 : right ? 1 : 0);
2327 int dy = (up ? -1 : down ? 1 : 0);
2328 boolean use_delay_value_first = (joytest != joytest_last);
2330 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2332 /* do not handle this button event anymore */
2336 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2338 if (game_status == GAME_MODE_PLAYING)
2340 // when playing MM style levels, also use delay for keyboard events
2341 joytest |= keyboard;
2343 // only use first delay value for new events, but not for changed events
2344 use_delay_value_first = (!joytest != !joytest_last);
2346 // only use delay after the initial keyboard event
2350 // for any joystick or keyboard event, enable playfield tile cursor
2351 if (dx || dy || button)
2352 SetTileCursorEnabled(TRUE);
2355 if (joytest && !button && !DelayReached(&joytest_delay, joytest_delay_value))
2357 /* delay joystick/keyboard actions if axes/keys continually pressed */
2358 newbutton = dx = dy = 0;
2362 /* first start with longer delay, then continue with shorter delay */
2363 joytest_delay_value =
2364 (use_delay_value_first ? delay_value_first : delay_value);
2367 joytest_last = joytest;
2369 switch (game_status)
2371 case GAME_MODE_TITLE:
2372 case GAME_MODE_MAIN:
2373 case GAME_MODE_LEVELS:
2374 case GAME_MODE_LEVELNR:
2375 case GAME_MODE_SETUP:
2376 case GAME_MODE_INFO:
2377 case GAME_MODE_SCORES:
2379 if (game_status == GAME_MODE_TITLE)
2380 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2381 else if (game_status == GAME_MODE_MAIN)
2382 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2383 else if (game_status == GAME_MODE_LEVELS)
2384 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2385 else if (game_status == GAME_MODE_LEVELNR)
2386 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2387 else if (game_status == GAME_MODE_SETUP)
2388 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2389 else if (game_status == GAME_MODE_INFO)
2390 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2391 else if (game_status == GAME_MODE_SCORES)
2392 HandleHallOfFame(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2397 case GAME_MODE_PLAYING:
2399 // !!! causes immediate GameEnd() when solving MM level with keyboard !!!
2400 if (tape.playing || keyboard)
2401 newbutton = ((joy & JOY_BUTTON) != 0);
2404 if (newbutton && AllPlayersGone)
2411 if (tape.single_step && tape.recording && tape.pausing && !tape.use_mouse)
2413 if (joystick & JOY_ACTION)
2414 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
2416 else if (tape.recording && tape.pausing && !tape.use_mouse)
2418 if (joystick & JOY_ACTION)
2419 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2422 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2423 HandleTileCursor(dx, dy, button);
2432 void HandleSpecialGameControllerButtons(Event *event)
2434 #if defined(TARGET_SDL2)
2438 switch (event->type)
2440 case SDL_CONTROLLERBUTTONDOWN:
2441 key_status = KEY_PRESSED;
2444 case SDL_CONTROLLERBUTTONUP:
2445 key_status = KEY_RELEASED;
2452 switch (event->cbutton.button)
2454 case SDL_CONTROLLER_BUTTON_START:
2458 case SDL_CONTROLLER_BUTTON_BACK:
2466 HandleKey(key, key_status);
2470 void HandleSpecialGameControllerKeys(Key key, int key_status)
2472 #if defined(TARGET_SDL2)
2473 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2474 int button = SDL_CONTROLLER_BUTTON_INVALID;
2476 /* map keys to joystick buttons (special hack for Amazon Fire TV remote) */
2477 if (key == KSYM_Rewind)
2478 button = SDL_CONTROLLER_BUTTON_A;
2479 else if (key == KSYM_FastForward || key == KSYM_Menu)
2480 button = SDL_CONTROLLER_BUTTON_B;
2482 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2486 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2487 SDL_CONTROLLERBUTTONUP);
2489 event.cbutton.which = 0; /* first joystick (Amazon Fire TV remote) */
2490 event.cbutton.button = button;
2491 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2494 HandleJoystickEvent(&event);
2500 boolean DoKeysymAction(int keysym)
2504 Key key = (Key)(-keysym);
2506 HandleKey(key, KEY_PRESSED);
2507 HandleKey(key, KEY_RELEASED);