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 keys (media keys / remote control buttons) to default keys */
1544 if (key == KSYM_PlayPause)
1546 else if (key == KSYM_Select)
1550 HandleSpecialGameControllerKeys(key, key_status);
1552 if (game_status == GAME_MODE_PLAYING)
1554 /* only needed for single-step tape recording mode */
1555 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1556 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1557 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1558 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1561 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1563 byte key_action = 0;
1565 if (setup.input[pnr].use_joystick)
1568 ski = setup.input[pnr].key;
1570 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1571 if (key == *key_info[i].key_custom)
1572 key_action |= key_info[i].action;
1574 /* use combined snap+direction keys for the first player only */
1577 ssi = setup.shortcut;
1579 for (i = 0; i < NUM_DIRECTIONS; i++)
1580 if (key == *key_info[i].key_snap)
1581 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1584 /* clear delayed snap and drop actions in single step mode (see below) */
1585 if (tape.single_step)
1587 if (clear_snap_button[pnr])
1589 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1590 clear_snap_button[pnr] = FALSE;
1593 if (clear_drop_button[pnr])
1595 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1596 clear_drop_button[pnr] = FALSE;
1600 if (key_status == KEY_PRESSED)
1601 stored_player[pnr].action |= key_action;
1603 stored_player[pnr].action &= ~key_action;
1605 if (tape.single_step && tape.recording && tape.pausing)
1607 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1609 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1611 /* if snap key already pressed, don't snap when releasing (below) */
1612 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1613 element_snapped[pnr] = TRUE;
1615 /* if drop key already pressed, don't drop when releasing (below) */
1616 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1617 element_dropped[pnr] = TRUE;
1619 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1621 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1622 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1625 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1626 getRedDiskReleaseFlag_SP() == 0)
1627 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1629 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1632 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1634 if (key_action & KEY_BUTTON_SNAP)
1636 /* if snap key was released without moving (see above), snap now */
1637 if (!element_snapped[pnr])
1639 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1641 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1643 /* clear delayed snap button on next event */
1644 clear_snap_button[pnr] = TRUE;
1647 element_snapped[pnr] = FALSE;
1650 if (key_action & KEY_BUTTON_DROP &&
1651 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1653 /* if drop key was released without moving (see above), drop now */
1654 if (!element_dropped[pnr])
1656 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1658 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1659 getRedDiskReleaseFlag_SP() != 0)
1660 stored_player[pnr].action |= KEY_BUTTON_DROP;
1662 /* clear delayed drop button on next event */
1663 clear_drop_button[pnr] = TRUE;
1666 element_dropped[pnr] = FALSE;
1670 else if (tape.recording && tape.pausing)
1672 /* prevent key release events from un-pausing a paused game */
1673 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1674 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1680 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1681 if (key == key_info[i].key_default)
1682 joy |= key_info[i].action;
1687 if (key_status == KEY_PRESSED)
1688 key_joystick_mapping |= joy;
1690 key_joystick_mapping &= ~joy;
1695 if (game_status != GAME_MODE_PLAYING)
1696 key_joystick_mapping = 0;
1698 if (key_status == KEY_RELEASED)
1700 // reset flag to ignore repeated "key pressed" events after key release
1701 ignore_repeated_key = FALSE;
1706 if ((key == KSYM_F11 ||
1707 ((key == KSYM_Return ||
1708 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1709 video.fullscreen_available &&
1710 !ignore_repeated_key)
1712 setup.fullscreen = !setup.fullscreen;
1714 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1716 if (game_status == GAME_MODE_SETUP)
1717 RedrawSetupScreenAfterFullscreenToggle();
1719 // set flag to ignore repeated "key pressed" events
1720 ignore_repeated_key = TRUE;
1725 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1726 key == KSYM_minus || key == KSYM_KP_Subtract ||
1727 key == KSYM_plus || key == KSYM_KP_Add ||
1728 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1729 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1730 video.window_scaling_available &&
1731 !video.fullscreen_enabled)
1733 if (key == KSYM_0 || key == KSYM_KP_0)
1734 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1735 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1736 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1738 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1740 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1741 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1742 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1743 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1745 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1747 if (game_status == GAME_MODE_SETUP)
1748 RedrawSetupScreenAfterFullscreenToggle();
1753 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1754 key == KSYM_Return ||
1755 key == KSYM_Escape)))
1757 /* do not handle this key event anymore */
1758 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1762 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1763 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1770 if (game_status == GAME_MODE_MAIN &&
1771 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1773 StartGameActions(options.network, setup.autorecord, level.random_seed);
1778 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1780 if (key == setup.shortcut.save_game)
1782 else if (key == setup.shortcut.load_game)
1784 else if (key == setup.shortcut.toggle_pause)
1785 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1787 HandleTapeButtonKeys(key);
1788 HandleSoundButtonKeys(key);
1791 if (game_status == GAME_MODE_PLAYING && !network_playing)
1793 int centered_player_nr_next = -999;
1795 if (key == setup.shortcut.focus_player_all)
1796 centered_player_nr_next = -1;
1798 for (i = 0; i < MAX_PLAYERS; i++)
1799 if (key == setup.shortcut.focus_player[i])
1800 centered_player_nr_next = i;
1802 if (centered_player_nr_next != -999)
1804 game.centered_player_nr_next = centered_player_nr_next;
1805 game.set_centered_player = TRUE;
1809 tape.centered_player_nr_next = game.centered_player_nr_next;
1810 tape.set_centered_player = TRUE;
1815 HandleKeysSpecial(key);
1817 if (HandleGadgetsKeyInput(key))
1819 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1820 key = KSYM_UNDEFINED;
1823 switch (game_status)
1825 case GAME_MODE_PSEUDO_TYPENAME:
1826 HandleTypeName(0, key);
1829 case GAME_MODE_TITLE:
1830 case GAME_MODE_MAIN:
1831 case GAME_MODE_LEVELS:
1832 case GAME_MODE_LEVELNR:
1833 case GAME_MODE_SETUP:
1834 case GAME_MODE_INFO:
1835 case GAME_MODE_SCORES:
1840 if (game_status == GAME_MODE_TITLE)
1841 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1842 else if (game_status == GAME_MODE_MAIN)
1843 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1844 else if (game_status == GAME_MODE_LEVELS)
1845 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1846 else if (game_status == GAME_MODE_LEVELNR)
1847 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1848 else if (game_status == GAME_MODE_SETUP)
1849 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1850 else if (game_status == GAME_MODE_INFO)
1851 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1852 else if (game_status == GAME_MODE_SCORES)
1853 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1857 if (game_status != GAME_MODE_MAIN)
1858 FadeSkipNextFadeIn();
1860 if (game_status == GAME_MODE_TITLE)
1861 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1862 else if (game_status == GAME_MODE_LEVELS)
1863 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1864 else if (game_status == GAME_MODE_LEVELNR)
1865 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1866 else if (game_status == GAME_MODE_SETUP)
1867 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1868 else if (game_status == GAME_MODE_INFO)
1869 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1870 else if (game_status == GAME_MODE_SCORES)
1871 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1875 if (game_status == GAME_MODE_LEVELS)
1876 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1877 else if (game_status == GAME_MODE_LEVELNR)
1878 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1879 else if (game_status == GAME_MODE_SETUP)
1880 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1881 else if (game_status == GAME_MODE_INFO)
1882 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1883 else if (game_status == GAME_MODE_SCORES)
1884 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1887 case KSYM_Page_Down:
1888 if (game_status == GAME_MODE_LEVELS)
1889 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1890 else if (game_status == GAME_MODE_LEVELNR)
1891 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1892 else if (game_status == GAME_MODE_SETUP)
1893 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1894 else if (game_status == GAME_MODE_INFO)
1895 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1896 else if (game_status == GAME_MODE_SCORES)
1897 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1905 case GAME_MODE_EDITOR:
1906 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1907 HandleLevelEditorKeyInput(key);
1910 case GAME_MODE_PLAYING:
1915 RequestQuitGame(setup.ask_on_escape);
1925 if (key == KSYM_Escape)
1927 SetGameStatus(GAME_MODE_MAIN);
1935 HandleKeysDebug(key);
1938 void HandleNoEvent()
1940 // if (button_status && game_status != GAME_MODE_PLAYING)
1941 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1943 HandleButton(0, 0, button_status, -button_status);
1950 #if defined(NETWORK_AVALIABLE)
1951 if (options.network)
1955 switch (game_status)
1957 case GAME_MODE_MAIN:
1958 DrawPreviewLevelAnimation();
1961 case GAME_MODE_EDITOR:
1962 HandleLevelEditorIdle();
1965 #if defined(TARGET_SDL2)
1966 case GAME_MODE_PLAYING:
1967 HandleFollowFinger(-1, -1, -1);
1976 static int HandleJoystickForAllPlayers()
1980 boolean no_joysticks_configured = TRUE;
1981 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1982 static byte joy_action_last[MAX_PLAYERS];
1984 for (i = 0; i < MAX_PLAYERS; i++)
1985 if (setup.input[i].use_joystick)
1986 no_joysticks_configured = FALSE;
1988 /* if no joysticks configured, map connected joysticks to players */
1989 if (no_joysticks_configured)
1990 use_as_joystick_nr = TRUE;
1992 for (i = 0; i < MAX_PLAYERS; i++)
1994 byte joy_action = 0;
1996 joy_action = JoystickExt(i, use_as_joystick_nr);
1997 result |= joy_action;
1999 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
2000 joy_action != joy_action_last[i])
2001 stored_player[i].action = joy_action;
2003 joy_action_last[i] = joy_action;
2009 void HandleJoystick()
2011 int joystick = HandleJoystickForAllPlayers();
2012 int keyboard = key_joystick_mapping;
2013 int joy = (joystick | keyboard);
2014 int left = joy & JOY_LEFT;
2015 int right = joy & JOY_RIGHT;
2016 int up = joy & JOY_UP;
2017 int down = joy & JOY_DOWN;
2018 int button = joy & JOY_BUTTON;
2019 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
2020 int dx = (left ? -1 : right ? 1 : 0);
2021 int dy = (up ? -1 : down ? 1 : 0);
2023 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2025 /* do not handle this button event anymore */
2029 switch (game_status)
2031 case GAME_MODE_TITLE:
2032 case GAME_MODE_MAIN:
2033 case GAME_MODE_LEVELS:
2034 case GAME_MODE_LEVELNR:
2035 case GAME_MODE_SETUP:
2036 case GAME_MODE_INFO:
2038 static unsigned int joystickmove_delay = 0;
2039 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
2040 static int joystick_last = 0;
2042 if (joystick && !button &&
2043 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2045 /* delay joystick actions if buttons/axes continually pressed */
2046 newbutton = dx = dy = 0;
2050 /* start with longer delay, then continue with shorter delay */
2051 if (joystick != joystick_last)
2052 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2054 joystickmove_delay_value = GADGET_FRAME_DELAY;
2057 if (game_status == GAME_MODE_TITLE)
2058 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2059 else if (game_status == GAME_MODE_MAIN)
2060 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2061 else if (game_status == GAME_MODE_LEVELS)
2062 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2063 else if (game_status == GAME_MODE_LEVELNR)
2064 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2065 else if (game_status == GAME_MODE_SETUP)
2066 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2067 else if (game_status == GAME_MODE_INFO)
2068 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2070 joystick_last = joystick;
2075 case GAME_MODE_SCORES:
2076 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2079 case GAME_MODE_PLAYING:
2080 if (tape.playing || keyboard)
2081 newbutton = ((joy & JOY_BUTTON) != 0);
2083 if (newbutton && AllPlayersGone)
2090 if (tape.recording && tape.pausing)
2092 if (joystick & JOY_ACTION)
2093 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2103 void HandleSpecialGameControllerButtons(Event *event)
2105 #if defined(TARGET_SDL2)
2106 switch (event->type)
2108 case SDL_CONTROLLERBUTTONDOWN:
2109 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2110 HandleKey(KSYM_space, KEY_PRESSED);
2111 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2112 HandleKey(KSYM_Escape, KEY_PRESSED);
2116 case SDL_CONTROLLERBUTTONUP:
2117 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2118 HandleKey(KSYM_space, KEY_RELEASED);
2119 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2120 HandleKey(KSYM_Escape, KEY_RELEASED);
2127 void HandleSpecialGameControllerKeys(Key key, int key_status)
2129 #if defined(TARGET_SDL2)
2130 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2131 int button = SDL_CONTROLLER_BUTTON_INVALID;
2133 /* map keys to joystick buttons (special hack for Amazon Fire TV remote) */
2134 if (key == KSYM_Rewind)
2135 button = SDL_CONTROLLER_BUTTON_A;
2136 else if (key == KSYM_FastForward || key == KSYM_Menu)
2137 button = SDL_CONTROLLER_BUTTON_B;
2139 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2143 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2144 SDL_CONTROLLERBUTTONUP);
2146 event.cbutton.which = 0; /* first joystick (Amazon Fire TV remote) */
2147 event.cbutton.button = button;
2148 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2151 HandleJoystickEvent(&event);