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;
42 /* event filter especially needed for SDL event filtering due to
43 delay problems with lots of mouse motion events when mouse button
44 not pressed (X11 can handle this with 'PointerMotionHintMask') */
46 /* event filter addition for SDL2: as SDL2 does not have a function to enable
47 or disable keyboard auto-repeat, filter repeated keyboard events instead */
49 static int FilterEvents(const Event *event)
53 #if defined(TARGET_SDL2)
54 /* skip repeated key press events if keyboard auto-repeat is disabled */
55 if (event->type == EVENT_KEYPRESS &&
61 if (event->type == EVENT_BUTTONPRESS ||
62 event->type == EVENT_BUTTONRELEASE)
64 ((ButtonEvent *)event)->x -= video.screen_xoffset;
65 ((ButtonEvent *)event)->y -= video.screen_yoffset;
67 else if (event->type == EVENT_MOTIONNOTIFY)
69 ((MotionEvent *)event)->x -= video.screen_xoffset;
70 ((MotionEvent *)event)->y -= video.screen_yoffset;
73 /* non-motion events are directly passed to event handler functions */
74 if (event->type != EVENT_MOTIONNOTIFY)
77 motion = (MotionEvent *)event;
78 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
79 motion->y >= SY && motion->y < SY + SYSIZE);
81 /* do no reset mouse cursor before all pending events have been processed */
82 if (gfx.cursor_mode == cursor_mode_last &&
83 ((game_status == GAME_MODE_TITLE &&
84 gfx.cursor_mode == CURSOR_NONE) ||
85 (game_status == GAME_MODE_PLAYING &&
86 gfx.cursor_mode == CURSOR_PLAYFIELD)))
88 SetMouseCursor(CURSOR_DEFAULT);
90 DelayReached(&special_cursor_delay, 0);
92 cursor_mode_last = CURSOR_DEFAULT;
95 /* skip mouse motion events without pressed button outside level editor */
96 if (button_status == MB_RELEASED &&
97 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
103 /* to prevent delay problems, skip mouse motion events if the very next
104 event is also a mouse motion event (and therefore effectively only
105 handling the last of a row of mouse motion events in the event queue) */
107 static boolean SkipPressedMouseMotionEvent(const Event *event)
109 /* nothing to do if the current event is not a mouse motion event */
110 if (event->type != EVENT_MOTIONNOTIFY)
113 /* only skip motion events with pressed button outside the game */
114 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
121 PeekEvent(&next_event);
123 /* if next event is also a mouse motion event, skip the current one */
124 if (next_event.type == EVENT_MOTIONNOTIFY)
131 /* this is especially needed for event modifications for the Android target:
132 if mouse coordinates should be modified in the event filter function,
133 using a properly installed SDL event filter does not work, because in
134 the event filter, mouse coordinates in the event structure are still
135 physical pixel positions, not logical (scaled) screen positions, so this
136 has to be handled at a later stage in the event processing functions
137 (when device pixel positions are already converted to screen positions) */
139 boolean NextValidEvent(Event *event)
141 while (PendingEvent())
143 boolean handle_this_event = FALSE;
147 if (FilterEvents(event))
148 handle_this_event = TRUE;
150 if (SkipPressedMouseMotionEvent(event))
151 handle_this_event = FALSE;
153 if (handle_this_event)
163 unsigned int event_frame_delay = 0;
164 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
166 ResetDelayCounter(&event_frame_delay);
168 while (NextValidEvent(&event))
172 case EVENT_BUTTONPRESS:
173 case EVENT_BUTTONRELEASE:
174 HandleButtonEvent((ButtonEvent *) &event);
177 case EVENT_MOTIONNOTIFY:
178 HandleMotionEvent((MotionEvent *) &event);
181 #if defined(TARGET_SDL2)
182 case EVENT_WHEELMOTION:
183 HandleWheelEvent((WheelEvent *) &event);
186 case SDL_WINDOWEVENT:
187 HandleWindowEvent((WindowEvent *) &event);
190 case EVENT_FINGERPRESS:
191 case EVENT_FINGERRELEASE:
192 case EVENT_FINGERMOTION:
193 HandleFingerEvent((FingerEvent *) &event);
196 case EVENT_TEXTINPUT:
197 HandleTextEvent((TextEvent *) &event);
200 case SDL_APP_WILLENTERBACKGROUND:
201 case SDL_APP_DIDENTERBACKGROUND:
202 case SDL_APP_WILLENTERFOREGROUND:
203 case SDL_APP_DIDENTERFOREGROUND:
204 HandlePauseResumeEvent((PauseResumeEvent *) &event);
209 case EVENT_KEYRELEASE:
210 HandleKeyEvent((KeyEvent *) &event);
214 HandleOtherEvents(&event);
218 // do not handle events for longer than standard frame delay period
219 if (DelayReached(&event_frame_delay, event_frame_delay_value))
224 void HandleOtherEvents(Event *event)
229 HandleExposeEvent((ExposeEvent *) event);
232 case EVENT_UNMAPNOTIFY:
234 /* This causes the game to stop not only when iconified, but also
235 when on another virtual desktop, which might be not desired. */
236 SleepWhileUnmapped();
242 HandleFocusEvent((FocusChangeEvent *) event);
245 case EVENT_CLIENTMESSAGE:
246 HandleClientMessageEvent((ClientMessageEvent *) event);
249 #if defined(TARGET_SDL)
250 #if defined(TARGET_SDL2)
251 case SDL_CONTROLLERBUTTONDOWN:
252 case SDL_CONTROLLERBUTTONUP:
253 // for any game controller button event, disable overlay buttons
254 SetOverlayEnabled(FALSE);
256 HandleSpecialGameControllerButtons(event);
259 case SDL_CONTROLLERDEVICEADDED:
260 case SDL_CONTROLLERDEVICEREMOVED:
261 case SDL_CONTROLLERAXISMOTION:
263 case SDL_JOYAXISMOTION:
264 case SDL_JOYBUTTONDOWN:
265 case SDL_JOYBUTTONUP:
266 HandleJoystickEvent(event);
270 HandleWindowManagerEvent(event);
279 void HandleMouseCursor()
281 if (game_status == GAME_MODE_TITLE)
283 /* when showing title screens, hide mouse pointer (if not moved) */
285 if (gfx.cursor_mode != CURSOR_NONE &&
286 DelayReached(&special_cursor_delay, special_cursor_delay_value))
288 SetMouseCursor(CURSOR_NONE);
291 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
294 /* when playing, display a special mouse pointer inside the playfield */
296 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
297 cursor_inside_playfield &&
298 DelayReached(&special_cursor_delay, special_cursor_delay_value))
300 SetMouseCursor(CURSOR_PLAYFIELD);
303 else if (gfx.cursor_mode != CURSOR_DEFAULT)
305 SetMouseCursor(CURSOR_DEFAULT);
308 /* this is set after all pending events have been processed */
309 cursor_mode_last = gfx.cursor_mode;
321 /* also execute after pending events have been processed before */
324 /* don't use all CPU time when idle; the main loop while playing
325 has its own synchronization and is CPU friendly, too */
327 if (game_status == GAME_MODE_PLAYING)
330 /* always copy backbuffer to visible screen for every video frame */
333 /* reset video frame delay to default (may change again while playing) */
334 SetVideoFrameDelay(MenuFrameDelay);
336 if (game_status == GAME_MODE_QUIT)
341 void ClearEventQueue()
343 while (PendingEvent())
351 case EVENT_BUTTONRELEASE:
352 button_status = MB_RELEASED;
355 case EVENT_KEYRELEASE:
359 #if defined(TARGET_SDL2)
360 case SDL_CONTROLLERBUTTONUP:
361 HandleJoystickEvent(&event);
367 HandleOtherEvents(&event);
373 void ClearPlayerAction()
377 /* simulate key release events for still pressed keys */
378 key_joystick_mapping = 0;
379 for (i = 0; i < MAX_PLAYERS; i++)
380 stored_player[i].action = 0;
383 void SleepWhileUnmapped()
385 boolean window_unmapped = TRUE;
387 KeyboardAutoRepeatOn();
389 while (window_unmapped)
397 case EVENT_BUTTONRELEASE:
398 button_status = MB_RELEASED;
401 case EVENT_KEYRELEASE:
402 key_joystick_mapping = 0;
405 #if defined(TARGET_SDL2)
406 case SDL_CONTROLLERBUTTONUP:
407 HandleJoystickEvent(&event);
408 key_joystick_mapping = 0;
412 case EVENT_MAPNOTIFY:
413 window_unmapped = FALSE;
416 case EVENT_UNMAPNOTIFY:
417 /* this is only to surely prevent the 'should not happen' case
418 * of recursively looping between 'SleepWhileUnmapped()' and
419 * 'HandleOtherEvents()' which usually calls this funtion.
424 HandleOtherEvents(&event);
429 if (game_status == GAME_MODE_PLAYING)
430 KeyboardAutoRepeatOffUnlessAutoplay();
433 void HandleExposeEvent(ExposeEvent *event)
437 void HandleButtonEvent(ButtonEvent *event)
439 #if DEBUG_EVENTS_BUTTON
440 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
442 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
446 #if defined(HAS_SCREEN_KEYBOARD)
447 if (video.shifted_up)
448 event->y += video.shifted_up_pos;
451 motion_status = FALSE;
453 if (event->type == EVENT_BUTTONPRESS)
454 button_status = event->button;
456 button_status = MB_RELEASED;
458 HandleButton(event->x, event->y, button_status, event->button);
461 void HandleMotionEvent(MotionEvent *event)
463 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
466 motion_status = TRUE;
468 #if DEBUG_EVENTS_MOTION
469 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
470 button_status, event->x, event->y);
473 HandleButton(event->x, event->y, button_status, button_status);
476 #if defined(TARGET_SDL2)
478 void HandleWheelEvent(WheelEvent *event)
482 #if DEBUG_EVENTS_WHEEL
484 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
485 event->which, event->x, event->y);
487 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
488 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
489 event->which, event->x, event->y,
490 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
491 "SDL_MOUSEWHEEL_FLIPPED"));
495 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
496 event->x > 0 ? MB_WHEEL_RIGHT :
497 event->y < 0 ? MB_WHEEL_DOWN :
498 event->y > 0 ? MB_WHEEL_UP : 0);
500 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
501 // accelerated mouse wheel available on Mac and Windows
502 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
504 // no accelerated mouse wheel available on Unix/Linux
505 wheel_steps = DEFAULT_WHEEL_STEPS;
508 motion_status = FALSE;
510 button_status = button_nr;
511 HandleButton(0, 0, button_status, -button_nr);
513 button_status = MB_RELEASED;
514 HandleButton(0, 0, button_status, -button_nr);
517 void HandleWindowEvent(WindowEvent *event)
519 #if DEBUG_EVENTS_WINDOW
520 int subtype = event->event;
523 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
524 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
525 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
526 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
527 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
528 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
529 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
530 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
531 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
532 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
533 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
534 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
535 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
536 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
539 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
540 event_name, event->data1, event->data2);
544 // (not needed, as the screen gets redrawn every 20 ms anyway)
545 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
546 event->event == SDL_WINDOWEVENT_RESIZED ||
547 event->event == SDL_WINDOWEVENT_EXPOSED)
551 if (event->event == SDL_WINDOWEVENT_RESIZED)
553 if (!video.fullscreen_enabled)
555 int new_window_width = event->data1;
556 int new_window_height = event->data2;
558 // if window size has changed after resizing, calculate new scaling factor
559 if (new_window_width != video.window_width ||
560 new_window_height != video.window_height)
562 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
563 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
565 // (extreme window scaling allowed, but cannot be saved permanently)
566 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
567 setup.window_scaling_percent =
568 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
569 MAX_WINDOW_SCALING_PERCENT);
571 video.window_width = new_window_width;
572 video.window_height = new_window_height;
574 if (game_status == GAME_MODE_SETUP)
575 RedrawSetupScreenAfterFullscreenToggle();
580 #if defined(PLATFORM_ANDROID)
583 int new_display_width = event->data1;
584 int new_display_height = event->data2;
586 // if fullscreen display size has changed, device has been rotated
587 if (new_display_width != video.display_width ||
588 new_display_height != video.display_height)
590 video.display_width = new_display_width;
591 video.display_height = new_display_height;
593 SDLSetScreenProperties();
600 #define NUM_TOUCH_FINGERS 3
605 SDL_FingerID finger_id;
608 } touch_info[NUM_TOUCH_FINGERS];
610 void HandleFingerEvent(FingerEvent *event)
612 static Key motion_key_x = KSYM_UNDEFINED;
613 static Key motion_key_y = KSYM_UNDEFINED;
614 static Key button_key = KSYM_UNDEFINED;
615 static float motion_x1, motion_y1;
616 static float button_x1, button_y1;
617 static SDL_FingerID motion_id = -1;
618 static SDL_FingerID button_id = -1;
619 int move_trigger_distance_percent = setup.touch.move_distance;
620 int drop_trigger_distance_percent = setup.touch.drop_distance;
621 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
622 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
623 float event_x = event->x;
624 float event_y = event->y;
626 #if DEBUG_EVENTS_FINGER
627 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
628 event->type == EVENT_FINGERPRESS ? "pressed" :
629 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
633 event->dx, event->dy,
637 if (game_status != GAME_MODE_PLAYING)
640 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
643 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
645 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
647 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
649 event_y = (event_y - ypos) / (1 - ypos);
651 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
652 event_y > 2.0 / 3.0 && event_y < 1 ?
653 setup.input[0].key.snap :
654 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
655 event_y > 2.0 / 3.0 && event_y < 1 ?
656 setup.input[0].key.drop :
657 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
658 event_y > 0 && event_y < 1.0 / 3.0 ?
659 setup.input[0].key.up :
660 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
661 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
662 setup.input[0].key.left :
663 event_x > 8.0 / 9.0 && event_x < 1 &&
664 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
665 setup.input[0].key.right :
666 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
667 event_y > 2.0 / 3.0 && event_y < 1 ?
668 setup.input[0].key.down :
671 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
675 // for any touch input event, enable overlay buttons (if activated)
676 SetOverlayEnabled(TRUE);
678 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
679 getKeyNameFromKey(key), key_status_name, event->fingerId);
681 // check if we already know this touch event's finger id
682 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
684 if (touch_info[i].touched &&
685 touch_info[i].finger_id == event->fingerId)
687 // Error(ERR_DEBUG, "MARK 1: %d", i);
693 if (i >= NUM_TOUCH_FINGERS)
695 if (key_status == KEY_PRESSED)
697 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
699 // unknown finger id -- get new, empty slot, if available
700 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
702 if (touch_info[i].counter < oldest_counter)
705 oldest_counter = touch_info[i].counter;
707 // Error(ERR_DEBUG, "MARK 2: %d", i);
710 if (!touch_info[i].touched)
712 // Error(ERR_DEBUG, "MARK 3: %d", i);
718 if (i >= NUM_TOUCH_FINGERS)
720 // all slots allocated -- use oldest slot
723 // Error(ERR_DEBUG, "MARK 4: %d", i);
728 // release of previously unknown key (should not happen)
730 if (key != KSYM_UNDEFINED)
732 HandleKey(key, KEY_RELEASED);
734 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
735 getKeyNameFromKey(key), "KEY_RELEASED", i);
740 if (i < NUM_TOUCH_FINGERS)
742 if (key_status == KEY_PRESSED)
744 if (touch_info[i].key != key)
746 if (touch_info[i].key != KSYM_UNDEFINED)
748 HandleKey(touch_info[i].key, KEY_RELEASED);
750 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
751 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
754 if (key != KSYM_UNDEFINED)
756 HandleKey(key, KEY_PRESSED);
758 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
759 getKeyNameFromKey(key), "KEY_PRESSED", i);
763 touch_info[i].touched = TRUE;
764 touch_info[i].finger_id = event->fingerId;
765 touch_info[i].counter = Counter();
766 touch_info[i].key = key;
770 if (touch_info[i].key != KSYM_UNDEFINED)
772 HandleKey(touch_info[i].key, KEY_RELEASED);
774 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
775 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
778 touch_info[i].touched = FALSE;
779 touch_info[i].finger_id = 0;
780 touch_info[i].counter = 0;
781 touch_info[i].key = 0;
788 // use touch direction control
790 if (event->type == EVENT_FINGERPRESS)
792 if (event_x > 1.0 / 3.0)
796 motion_id = event->fingerId;
801 motion_key_x = KSYM_UNDEFINED;
802 motion_key_y = KSYM_UNDEFINED;
804 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
810 button_id = event->fingerId;
815 button_key = setup.input[0].key.snap;
817 HandleKey(button_key, KEY_PRESSED);
819 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
822 else if (event->type == EVENT_FINGERRELEASE)
824 if (event->fingerId == motion_id)
828 if (motion_key_x != KSYM_UNDEFINED)
829 HandleKey(motion_key_x, KEY_RELEASED);
830 if (motion_key_y != KSYM_UNDEFINED)
831 HandleKey(motion_key_y, KEY_RELEASED);
833 motion_key_x = KSYM_UNDEFINED;
834 motion_key_y = KSYM_UNDEFINED;
836 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
838 else if (event->fingerId == button_id)
842 if (button_key != KSYM_UNDEFINED)
843 HandleKey(button_key, KEY_RELEASED);
845 button_key = KSYM_UNDEFINED;
847 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
850 else if (event->type == EVENT_FINGERMOTION)
852 if (event->fingerId == motion_id)
854 float distance_x = ABS(event_x - motion_x1);
855 float distance_y = ABS(event_y - motion_y1);
856 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
857 event_x > motion_x1 ? setup.input[0].key.right :
859 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
860 event_y > motion_y1 ? setup.input[0].key.down :
863 if (distance_x < move_trigger_distance / 2 ||
864 distance_x < distance_y)
865 new_motion_key_x = KSYM_UNDEFINED;
867 if (distance_y < move_trigger_distance / 2 ||
868 distance_y < distance_x)
869 new_motion_key_y = KSYM_UNDEFINED;
871 if (distance_x > move_trigger_distance ||
872 distance_y > move_trigger_distance)
874 if (new_motion_key_x != motion_key_x)
876 if (motion_key_x != KSYM_UNDEFINED)
877 HandleKey(motion_key_x, KEY_RELEASED);
878 if (new_motion_key_x != KSYM_UNDEFINED)
879 HandleKey(new_motion_key_x, KEY_PRESSED);
882 if (new_motion_key_y != motion_key_y)
884 if (motion_key_y != KSYM_UNDEFINED)
885 HandleKey(motion_key_y, KEY_RELEASED);
886 if (new_motion_key_y != KSYM_UNDEFINED)
887 HandleKey(new_motion_key_y, KEY_PRESSED);
893 motion_key_x = new_motion_key_x;
894 motion_key_y = new_motion_key_y;
896 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
899 else if (event->fingerId == button_id)
901 float distance_x = ABS(event_x - button_x1);
902 float distance_y = ABS(event_y - button_y1);
904 if (distance_x < drop_trigger_distance / 2 &&
905 distance_y > drop_trigger_distance)
907 if (button_key == setup.input[0].key.snap)
908 HandleKey(button_key, KEY_RELEASED);
913 button_key = setup.input[0].key.drop;
915 HandleKey(button_key, KEY_PRESSED);
917 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
923 static void HandleFollowFinger(int mx, int my, int button)
925 static int old_mx = 0, old_my = 0;
926 static Key motion_key_x = KSYM_UNDEFINED;
927 static Key motion_key_y = KSYM_UNDEFINED;
928 static boolean started_on_player = FALSE;
929 static boolean player_is_dropping = FALSE;
930 static int player_drop_count = 0;
931 static int last_player_x = -1;
932 static int last_player_y = -1;
934 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
937 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
939 touch_info[0].touched = TRUE;
940 touch_info[0].key = 0;
947 started_on_player = FALSE;
948 player_is_dropping = FALSE;
949 player_drop_count = 0;
953 motion_key_x = KSYM_UNDEFINED;
954 motion_key_y = KSYM_UNDEFINED;
956 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
959 else if (button == MB_RELEASED && touch_info[0].touched)
961 touch_info[0].touched = FALSE;
962 touch_info[0].key = 0;
967 if (motion_key_x != KSYM_UNDEFINED)
968 HandleKey(motion_key_x, KEY_RELEASED);
969 if (motion_key_y != KSYM_UNDEFINED)
970 HandleKey(motion_key_y, KEY_RELEASED);
972 if (started_on_player)
974 if (player_is_dropping)
976 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
978 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
982 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
984 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
988 motion_key_x = KSYM_UNDEFINED;
989 motion_key_y = KSYM_UNDEFINED;
991 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
994 if (touch_info[0].touched)
996 int src_x = local_player->jx;
997 int src_y = local_player->jy;
998 int dst_x = getLevelFromScreenX(old_mx);
999 int dst_y = getLevelFromScreenY(old_my);
1000 int dx = dst_x - src_x;
1001 int dy = dst_y - src_y;
1002 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
1003 dx > 0 ? setup.input[0].key.right :
1005 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
1006 dy > 0 ? setup.input[0].key.down :
1009 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1010 (last_player_x != local_player->jx ||
1011 last_player_y != local_player->jy))
1013 // in case of asymmetric diagonal movement, use "preferred" direction
1015 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1017 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1018 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1020 local_player->last_move_dir = last_move_dir;
1022 // (required to prevent accidentally forcing direction for next movement)
1023 last_player_x = local_player->jx;
1024 last_player_y = local_player->jy;
1027 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1029 started_on_player = TRUE;
1030 player_drop_count = getPlayerInventorySize(0);
1031 player_is_dropping = (player_drop_count > 0);
1033 if (player_is_dropping)
1035 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1037 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1041 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1043 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1046 else if (dx != 0 || dy != 0)
1048 if (player_is_dropping &&
1049 player_drop_count == getPlayerInventorySize(0))
1051 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1053 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1054 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1056 player_is_dropping = FALSE;
1060 if (new_motion_key_x != motion_key_x)
1062 Error(ERR_DEBUG, "---------- %s %s ----------",
1063 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1064 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1066 if (motion_key_x != KSYM_UNDEFINED)
1067 HandleKey(motion_key_x, KEY_RELEASED);
1068 if (new_motion_key_x != KSYM_UNDEFINED)
1069 HandleKey(new_motion_key_x, KEY_PRESSED);
1072 if (new_motion_key_y != motion_key_y)
1074 Error(ERR_DEBUG, "---------- %s %s ----------",
1075 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1076 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1078 if (motion_key_y != KSYM_UNDEFINED)
1079 HandleKey(motion_key_y, KEY_RELEASED);
1080 if (new_motion_key_y != KSYM_UNDEFINED)
1081 HandleKey(new_motion_key_y, KEY_PRESSED);
1084 motion_key_x = new_motion_key_x;
1085 motion_key_y = new_motion_key_y;
1089 static boolean checkTextInputKeyModState()
1091 // when playing, only handle raw key events and ignore text input
1092 if (game_status == GAME_MODE_PLAYING)
1095 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1098 void HandleTextEvent(TextEvent *event)
1100 char *text = event->text;
1101 Key key = getKeyFromKeyName(text);
1103 #if DEBUG_EVENTS_TEXT
1104 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1107 text[0], (int)(text[0]),
1109 getKeyNameFromKey(key),
1113 #if !defined(HAS_SCREEN_KEYBOARD)
1114 // non-mobile devices: only handle key input with modifier keys pressed here
1115 // (every other key input is handled directly as physical key input event)
1116 if (!checkTextInputKeyModState())
1120 // process text input as "classic" (with uppercase etc.) key input event
1121 HandleKey(key, KEY_PRESSED);
1122 HandleKey(key, KEY_RELEASED);
1125 void HandlePauseResumeEvent(PauseResumeEvent *event)
1127 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1131 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1139 void HandleKeyEvent(KeyEvent *event)
1141 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1142 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1143 Key key = GetEventKey(event, with_modifiers);
1144 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1146 #if DEBUG_EVENTS_KEY
1147 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1148 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1149 event->keysym.scancode,
1154 getKeyNameFromKey(key));
1157 #if defined(PLATFORM_ANDROID)
1158 if (key == KSYM_Back)
1160 // always map the "back" button to the "escape" key on Android devices
1165 // for any key event other than "back" button, disable overlay buttons
1166 SetOverlayEnabled(FALSE);
1170 HandleKeyModState(keymod, key_status);
1172 #if defined(TARGET_SDL2)
1173 // only handle raw key input without text modifier keys pressed
1174 if (!checkTextInputKeyModState())
1175 HandleKey(key, key_status);
1177 HandleKey(key, key_status);
1181 void HandleFocusEvent(FocusChangeEvent *event)
1183 static int old_joystick_status = -1;
1185 if (event->type == EVENT_FOCUSOUT)
1187 KeyboardAutoRepeatOn();
1188 old_joystick_status = joystick.status;
1189 joystick.status = JOYSTICK_NOT_AVAILABLE;
1191 ClearPlayerAction();
1193 else if (event->type == EVENT_FOCUSIN)
1195 /* When there are two Rocks'n'Diamonds windows which overlap and
1196 the player moves the pointer from one game window to the other,
1197 a 'FocusOut' event is generated for the window the pointer is
1198 leaving and a 'FocusIn' event is generated for the window the
1199 pointer is entering. In some cases, it can happen that the
1200 'FocusIn' event is handled by the one game process before the
1201 'FocusOut' event by the other game process. In this case the
1202 X11 environment would end up with activated keyboard auto repeat,
1203 because unfortunately this is a global setting and not (which
1204 would be far better) set for each X11 window individually.
1205 The effect would be keyboard auto repeat while playing the game
1206 (game_status == GAME_MODE_PLAYING), which is not desired.
1207 To avoid this special case, we just wait 1/10 second before
1208 processing the 'FocusIn' event.
1211 if (game_status == GAME_MODE_PLAYING)
1214 KeyboardAutoRepeatOffUnlessAutoplay();
1217 if (old_joystick_status != -1)
1218 joystick.status = old_joystick_status;
1222 void HandleClientMessageEvent(ClientMessageEvent *event)
1224 if (CheckCloseWindowEvent(event))
1228 void HandleWindowManagerEvent(Event *event)
1230 #if defined(TARGET_SDL)
1231 SDLHandleWindowManagerEvent(event);
1235 void HandleButton(int mx, int my, int button, int button_nr)
1237 static int old_mx = 0, old_my = 0;
1238 boolean button_hold = FALSE;
1244 button_nr = -button_nr;
1253 #if defined(PLATFORM_ANDROID)
1254 // when playing, only handle gadgets when using "follow finger" controls
1255 boolean handle_gadgets =
1256 (game_status != GAME_MODE_PLAYING ||
1257 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1259 if (handle_gadgets &&
1260 HandleGadgets(mx, my, button))
1262 /* do not handle this button event anymore */
1263 mx = my = -32; /* force mouse event to be outside screen tiles */
1266 if (HandleGadgets(mx, my, button))
1268 /* do not handle this button event anymore */
1269 mx = my = -32; /* force mouse event to be outside screen tiles */
1273 if (HandleGlobalAnimClicks(mx, my, button))
1275 /* do not handle this button event anymore */
1276 mx = my = -32; /* force mouse event to be outside screen tiles */
1279 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1282 /* do not use scroll wheel button events for anything other than gadgets */
1283 if (IS_WHEEL_BUTTON(button_nr))
1286 switch (game_status)
1288 case GAME_MODE_TITLE:
1289 HandleTitleScreen(mx, my, 0, 0, button);
1292 case GAME_MODE_MAIN:
1293 HandleMainMenu(mx, my, 0, 0, button);
1296 case GAME_MODE_PSEUDO_TYPENAME:
1297 HandleTypeName(0, KSYM_Return);
1300 case GAME_MODE_LEVELS:
1301 HandleChooseLevelSet(mx, my, 0, 0, button);
1304 case GAME_MODE_LEVELNR:
1305 HandleChooseLevelNr(mx, my, 0, 0, button);
1308 case GAME_MODE_SCORES:
1309 HandleHallOfFame(0, 0, 0, 0, button);
1312 case GAME_MODE_EDITOR:
1313 HandleLevelEditorIdle();
1316 case GAME_MODE_INFO:
1317 HandleInfoScreen(mx, my, 0, 0, button);
1320 case GAME_MODE_SETUP:
1321 HandleSetupScreen(mx, my, 0, 0, button);
1324 #if defined(TARGET_SDL2)
1325 case GAME_MODE_PLAYING:
1326 HandleFollowFinger(mx, my, button);
1330 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1331 GetKeyModState() & KMOD_Control)
1332 DumpTileFromScreen(mx, my);
1342 static boolean is_string_suffix(char *string, char *suffix)
1344 int string_len = strlen(string);
1345 int suffix_len = strlen(suffix);
1347 if (suffix_len > string_len)
1350 return (strEqual(&string[string_len - suffix_len], suffix));
1353 #define MAX_CHEAT_INPUT_LEN 32
1355 static void HandleKeysSpecial(Key key)
1357 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1358 char letter = getCharFromKey(key);
1359 int cheat_input_len = strlen(cheat_input);
1365 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1367 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1368 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1370 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1373 cheat_input[cheat_input_len++] = letter;
1374 cheat_input[cheat_input_len] = '\0';
1376 #if DEBUG_EVENTS_KEY
1377 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1380 if (game_status == GAME_MODE_MAIN)
1382 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1383 is_string_suffix(cheat_input, ":ist"))
1385 InsertSolutionTape();
1387 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1388 is_string_suffix(cheat_input, ":rg"))
1390 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1393 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1394 is_string_suffix(cheat_input, ":rs"))
1396 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1399 else if (is_string_suffix(cheat_input, ":reload-music") ||
1400 is_string_suffix(cheat_input, ":rm"))
1402 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1405 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1406 is_string_suffix(cheat_input, ":ra"))
1408 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1409 1 << ARTWORK_TYPE_SOUNDS |
1410 1 << ARTWORK_TYPE_MUSIC);
1413 else if (is_string_suffix(cheat_input, ":dump-level") ||
1414 is_string_suffix(cheat_input, ":dl"))
1418 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1419 is_string_suffix(cheat_input, ":dt"))
1423 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1424 is_string_suffix(cheat_input, ":ft"))
1426 /* fix single-player tapes that contain player input for more than one
1427 player (due to a bug in 3.3.1.2 and earlier versions), which results
1428 in playing levels with more than one player in multi-player mode,
1429 even though the tape was originally recorded in single-player mode */
1431 /* remove player input actions for all players but the first one */
1432 for (i = 1; i < MAX_PLAYERS; i++)
1433 tape.player_participates[i] = FALSE;
1435 tape.changed = TRUE;
1437 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1438 is_string_suffix(cheat_input, ":snl"))
1440 SaveNativeLevel(&level);
1443 else if (game_status == GAME_MODE_PLAYING)
1446 if (is_string_suffix(cheat_input, ".q"))
1447 DEBUG_SetMaximumDynamite();
1450 else if (game_status == GAME_MODE_EDITOR)
1452 if (is_string_suffix(cheat_input, ":dump-brush") ||
1453 is_string_suffix(cheat_input, ":DB"))
1457 else if (is_string_suffix(cheat_input, ":DDB"))
1464 void HandleKeysDebug(Key key)
1469 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1471 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1473 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1475 if (key == setup.debug.frame_delay_key[i] &&
1476 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1478 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1479 setup.debug.frame_delay[i] : setup.game_frame_delay);
1481 if (!setup.debug.frame_delay_game_only)
1482 MenuFrameDelay = GameFrameDelay;
1484 SetVideoFrameDelay(GameFrameDelay);
1486 if (GameFrameDelay > ONE_SECOND_DELAY)
1487 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1488 else if (GameFrameDelay != 0)
1489 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1490 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1491 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1493 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1500 if (game_status == GAME_MODE_PLAYING)
1504 options.debug = !options.debug;
1506 Error(ERR_DEBUG, "debug mode %s",
1507 (options.debug ? "enabled" : "disabled"));
1509 else if (key == KSYM_v)
1511 Error(ERR_DEBUG, "currently using game engine version %d",
1512 game.engine_version);
1518 void HandleKey(Key key, int key_status)
1520 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1521 static boolean ignore_repeated_key = FALSE;
1522 static struct SetupKeyboardInfo ski;
1523 static struct SetupShortcutInfo ssi;
1532 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1533 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1534 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1535 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1536 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1537 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1542 #if defined(TARGET_SDL2)
1543 /* map special "play/pause" media key to default key for play/pause actions */
1544 if (key == KSYM_PlayPause)
1548 HandleSpecialGameControllerKeys(key, key_status);
1550 if (game_status == GAME_MODE_PLAYING)
1552 /* only needed for single-step tape recording mode */
1553 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1554 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1555 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1556 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1559 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1561 byte key_action = 0;
1563 if (setup.input[pnr].use_joystick)
1566 ski = setup.input[pnr].key;
1568 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1569 if (key == *key_info[i].key_custom)
1570 key_action |= key_info[i].action;
1572 /* use combined snap+direction keys for the first player only */
1575 ssi = setup.shortcut;
1577 for (i = 0; i < NUM_DIRECTIONS; i++)
1578 if (key == *key_info[i].key_snap)
1579 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1582 /* clear delayed snap and drop actions in single step mode (see below) */
1583 if (tape.single_step)
1585 if (clear_snap_button[pnr])
1587 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1588 clear_snap_button[pnr] = FALSE;
1591 if (clear_drop_button[pnr])
1593 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1594 clear_drop_button[pnr] = FALSE;
1598 if (key_status == KEY_PRESSED)
1599 stored_player[pnr].action |= key_action;
1601 stored_player[pnr].action &= ~key_action;
1603 if (tape.single_step && tape.recording && tape.pausing)
1605 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1607 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1609 /* if snap key already pressed, don't snap when releasing (below) */
1610 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1611 element_snapped[pnr] = TRUE;
1613 /* if drop key already pressed, don't drop when releasing (below) */
1614 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1615 element_dropped[pnr] = TRUE;
1617 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1619 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1620 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1623 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1624 getRedDiskReleaseFlag_SP() == 0)
1625 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1627 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1630 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1632 if (key_action & KEY_BUTTON_SNAP)
1634 /* if snap key was released without moving (see above), snap now */
1635 if (!element_snapped[pnr])
1637 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1639 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1641 /* clear delayed snap button on next event */
1642 clear_snap_button[pnr] = TRUE;
1645 element_snapped[pnr] = FALSE;
1648 if (key_action & KEY_BUTTON_DROP &&
1649 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1651 /* if drop key was released without moving (see above), drop now */
1652 if (!element_dropped[pnr])
1654 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1656 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1657 getRedDiskReleaseFlag_SP() != 0)
1658 stored_player[pnr].action |= KEY_BUTTON_DROP;
1660 /* clear delayed drop button on next event */
1661 clear_drop_button[pnr] = TRUE;
1664 element_dropped[pnr] = FALSE;
1668 else if (tape.recording && tape.pausing)
1670 /* prevent key release events from un-pausing a paused game */
1671 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1672 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1678 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1679 if (key == key_info[i].key_default)
1680 joy |= key_info[i].action;
1685 if (key_status == KEY_PRESSED)
1686 key_joystick_mapping |= joy;
1688 key_joystick_mapping &= ~joy;
1693 if (game_status != GAME_MODE_PLAYING)
1694 key_joystick_mapping = 0;
1696 if (key_status == KEY_RELEASED)
1698 // reset flag to ignore repeated "key pressed" events after key release
1699 ignore_repeated_key = FALSE;
1704 if ((key == KSYM_F11 ||
1705 ((key == KSYM_Return ||
1706 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1707 video.fullscreen_available &&
1708 !ignore_repeated_key)
1710 setup.fullscreen = !setup.fullscreen;
1712 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1714 if (game_status == GAME_MODE_SETUP)
1715 RedrawSetupScreenAfterFullscreenToggle();
1717 // set flag to ignore repeated "key pressed" events
1718 ignore_repeated_key = TRUE;
1723 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1724 key == KSYM_minus || key == KSYM_KP_Subtract ||
1725 key == KSYM_plus || key == KSYM_KP_Add ||
1726 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1727 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1728 video.window_scaling_available &&
1729 !video.fullscreen_enabled)
1731 if (key == KSYM_0 || key == KSYM_KP_0)
1732 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1733 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1734 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1736 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1738 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1739 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1740 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1741 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1743 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1745 if (game_status == GAME_MODE_SETUP)
1746 RedrawSetupScreenAfterFullscreenToggle();
1751 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1752 key == KSYM_Return ||
1753 key == KSYM_Escape)))
1755 /* do not handle this key event anymore */
1756 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1760 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1761 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1768 if (game_status == GAME_MODE_MAIN &&
1769 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1771 StartGameActions(options.network, setup.autorecord, level.random_seed);
1776 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1778 if (key == setup.shortcut.save_game)
1780 else if (key == setup.shortcut.load_game)
1782 else if (key == setup.shortcut.toggle_pause)
1783 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1785 HandleTapeButtonKeys(key);
1786 HandleSoundButtonKeys(key);
1789 if (game_status == GAME_MODE_PLAYING && !network_playing)
1791 int centered_player_nr_next = -999;
1793 if (key == setup.shortcut.focus_player_all)
1794 centered_player_nr_next = -1;
1796 for (i = 0; i < MAX_PLAYERS; i++)
1797 if (key == setup.shortcut.focus_player[i])
1798 centered_player_nr_next = i;
1800 if (centered_player_nr_next != -999)
1802 game.centered_player_nr_next = centered_player_nr_next;
1803 game.set_centered_player = TRUE;
1807 tape.centered_player_nr_next = game.centered_player_nr_next;
1808 tape.set_centered_player = TRUE;
1813 HandleKeysSpecial(key);
1815 if (HandleGadgetsKeyInput(key))
1817 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1818 key = KSYM_UNDEFINED;
1821 switch (game_status)
1823 case GAME_MODE_PSEUDO_TYPENAME:
1824 HandleTypeName(0, key);
1827 case GAME_MODE_TITLE:
1828 case GAME_MODE_MAIN:
1829 case GAME_MODE_LEVELS:
1830 case GAME_MODE_LEVELNR:
1831 case GAME_MODE_SETUP:
1832 case GAME_MODE_INFO:
1833 case GAME_MODE_SCORES:
1838 if (game_status == GAME_MODE_TITLE)
1839 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1840 else if (game_status == GAME_MODE_MAIN)
1841 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1842 else if (game_status == GAME_MODE_LEVELS)
1843 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1844 else if (game_status == GAME_MODE_LEVELNR)
1845 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1846 else if (game_status == GAME_MODE_SETUP)
1847 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1848 else if (game_status == GAME_MODE_INFO)
1849 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1850 else if (game_status == GAME_MODE_SCORES)
1851 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1855 if (game_status != GAME_MODE_MAIN)
1856 FadeSkipNextFadeIn();
1858 if (game_status == GAME_MODE_TITLE)
1859 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1860 else if (game_status == GAME_MODE_LEVELS)
1861 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1862 else if (game_status == GAME_MODE_LEVELNR)
1863 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1864 else if (game_status == GAME_MODE_SETUP)
1865 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1866 else if (game_status == GAME_MODE_INFO)
1867 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1868 else if (game_status == GAME_MODE_SCORES)
1869 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1873 if (game_status == GAME_MODE_LEVELS)
1874 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1875 else if (game_status == GAME_MODE_LEVELNR)
1876 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1877 else if (game_status == GAME_MODE_SETUP)
1878 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1879 else if (game_status == GAME_MODE_INFO)
1880 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1881 else if (game_status == GAME_MODE_SCORES)
1882 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1885 case KSYM_Page_Down:
1886 if (game_status == GAME_MODE_LEVELS)
1887 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1888 else if (game_status == GAME_MODE_LEVELNR)
1889 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1890 else if (game_status == GAME_MODE_SETUP)
1891 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1892 else if (game_status == GAME_MODE_INFO)
1893 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1894 else if (game_status == GAME_MODE_SCORES)
1895 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1903 case GAME_MODE_EDITOR:
1904 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1905 HandleLevelEditorKeyInput(key);
1908 case GAME_MODE_PLAYING:
1913 RequestQuitGame(setup.ask_on_escape);
1923 if (key == KSYM_Escape)
1925 SetGameStatus(GAME_MODE_MAIN);
1933 HandleKeysDebug(key);
1936 void HandleNoEvent()
1938 // if (button_status && game_status != GAME_MODE_PLAYING)
1939 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1941 HandleButton(0, 0, button_status, -button_status);
1948 #if defined(NETWORK_AVALIABLE)
1949 if (options.network)
1953 switch (game_status)
1955 case GAME_MODE_MAIN:
1956 DrawPreviewLevelAnimation();
1959 case GAME_MODE_EDITOR:
1960 HandleLevelEditorIdle();
1963 #if defined(TARGET_SDL2)
1964 case GAME_MODE_PLAYING:
1965 HandleFollowFinger(-1, -1, -1);
1974 static int HandleJoystickForAllPlayers()
1978 boolean no_joysticks_configured = TRUE;
1979 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1980 static byte joy_action_last[MAX_PLAYERS];
1982 for (i = 0; i < MAX_PLAYERS; i++)
1983 if (setup.input[i].use_joystick)
1984 no_joysticks_configured = FALSE;
1986 /* if no joysticks configured, map connected joysticks to players */
1987 if (no_joysticks_configured)
1988 use_as_joystick_nr = TRUE;
1990 for (i = 0; i < MAX_PLAYERS; i++)
1992 byte joy_action = 0;
1994 joy_action = JoystickExt(i, use_as_joystick_nr);
1995 result |= joy_action;
1997 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1998 joy_action != joy_action_last[i])
1999 stored_player[i].action = joy_action;
2001 joy_action_last[i] = joy_action;
2007 void HandleJoystick()
2009 int joystick = HandleJoystickForAllPlayers();
2010 int keyboard = key_joystick_mapping;
2011 int joy = (joystick | keyboard);
2012 int left = joy & JOY_LEFT;
2013 int right = joy & JOY_RIGHT;
2014 int up = joy & JOY_UP;
2015 int down = joy & JOY_DOWN;
2016 int button = joy & JOY_BUTTON;
2017 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
2018 int dx = (left ? -1 : right ? 1 : 0);
2019 int dy = (up ? -1 : down ? 1 : 0);
2021 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2023 /* do not handle this button event anymore */
2027 switch (game_status)
2029 case GAME_MODE_TITLE:
2030 case GAME_MODE_MAIN:
2031 case GAME_MODE_LEVELS:
2032 case GAME_MODE_LEVELNR:
2033 case GAME_MODE_SETUP:
2034 case GAME_MODE_INFO:
2036 static unsigned int joystickmove_delay = 0;
2037 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
2038 static int joystick_last = 0;
2040 if (joystick && !button &&
2041 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2043 /* delay joystick actions if buttons/axes continually pressed */
2044 newbutton = dx = dy = 0;
2048 /* start with longer delay, then continue with shorter delay */
2049 if (joystick != joystick_last)
2050 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2052 joystickmove_delay_value = GADGET_FRAME_DELAY;
2055 if (game_status == GAME_MODE_TITLE)
2056 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2057 else if (game_status == GAME_MODE_MAIN)
2058 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2059 else if (game_status == GAME_MODE_LEVELS)
2060 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2061 else if (game_status == GAME_MODE_LEVELNR)
2062 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2063 else if (game_status == GAME_MODE_SETUP)
2064 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2065 else if (game_status == GAME_MODE_INFO)
2066 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2068 joystick_last = joystick;
2073 case GAME_MODE_SCORES:
2074 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2077 case GAME_MODE_PLAYING:
2078 if (tape.playing || keyboard)
2079 newbutton = ((joy & JOY_BUTTON) != 0);
2081 if (newbutton && AllPlayersGone)
2088 if (tape.recording && tape.pausing)
2090 if (joystick & JOY_ACTION)
2091 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2101 void HandleSpecialGameControllerButtons(Event *event)
2103 #if defined(TARGET_SDL2)
2104 switch (event->type)
2106 case SDL_CONTROLLERBUTTONDOWN:
2107 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2108 HandleKey(KSYM_space, KEY_PRESSED);
2109 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2110 HandleKey(KSYM_Escape, KEY_PRESSED);
2114 case SDL_CONTROLLERBUTTONUP:
2115 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2116 HandleKey(KSYM_space, KEY_RELEASED);
2117 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2118 HandleKey(KSYM_Escape, KEY_RELEASED);
2125 void HandleSpecialGameControllerKeys(Key key, int key_status)
2127 #if defined(TARGET_SDL2)
2128 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2129 int button = SDL_CONTROLLER_BUTTON_INVALID;
2131 /* map keys to joystick buttons (special hack for Amazon Fire TV remote) */
2132 if (key == KSYM_Rewind)
2133 button = SDL_CONTROLLER_BUTTON_A;
2134 else if (key == KSYM_FastForward || key == KSYM_Menu)
2135 button = SDL_CONTROLLER_BUTTON_B;
2137 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2141 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2142 SDL_CONTROLLERBUTTONUP);
2144 event.cbutton.which = 0; /* first joystick (Amazon Fire TV remote) */
2145 event.cbutton.button = button;
2146 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2149 HandleJoystickEvent(&event);