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 HandleSpecialGameControllerButtons(event);
255 case SDL_CONTROLLERDEVICEADDED:
256 case SDL_CONTROLLERDEVICEREMOVED:
257 case SDL_CONTROLLERAXISMOTION:
259 case SDL_JOYAXISMOTION:
260 case SDL_JOYBUTTONDOWN:
261 case SDL_JOYBUTTONUP:
262 HandleJoystickEvent(event);
266 HandleWindowManagerEvent(event);
275 void HandleMouseCursor()
277 if (game_status == GAME_MODE_TITLE)
279 /* when showing title screens, hide mouse pointer (if not moved) */
281 if (gfx.cursor_mode != CURSOR_NONE &&
282 DelayReached(&special_cursor_delay, special_cursor_delay_value))
284 SetMouseCursor(CURSOR_NONE);
287 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
290 /* when playing, display a special mouse pointer inside the playfield */
292 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
293 cursor_inside_playfield &&
294 DelayReached(&special_cursor_delay, special_cursor_delay_value))
296 SetMouseCursor(CURSOR_PLAYFIELD);
299 else if (gfx.cursor_mode != CURSOR_DEFAULT)
301 SetMouseCursor(CURSOR_DEFAULT);
304 /* this is set after all pending events have been processed */
305 cursor_mode_last = gfx.cursor_mode;
317 /* also execute after pending events have been processed before */
320 /* don't use all CPU time when idle; the main loop while playing
321 has its own synchronization and is CPU friendly, too */
323 if (game_status == GAME_MODE_PLAYING)
326 /* always copy backbuffer to visible screen for every video frame */
329 /* reset video frame delay to default (may change again while playing) */
330 SetVideoFrameDelay(MenuFrameDelay);
332 if (game_status == GAME_MODE_QUIT)
337 void ClearEventQueue()
339 while (PendingEvent())
347 case EVENT_BUTTONRELEASE:
348 button_status = MB_RELEASED;
351 case EVENT_KEYRELEASE:
355 #if defined(TARGET_SDL2)
356 case SDL_CONTROLLERBUTTONUP:
357 HandleJoystickEvent(&event);
363 HandleOtherEvents(&event);
369 void ClearPlayerAction()
373 /* simulate key release events for still pressed keys */
374 key_joystick_mapping = 0;
375 for (i = 0; i < MAX_PLAYERS; i++)
376 stored_player[i].action = 0;
379 void SleepWhileUnmapped()
381 boolean window_unmapped = TRUE;
383 KeyboardAutoRepeatOn();
385 while (window_unmapped)
393 case EVENT_BUTTONRELEASE:
394 button_status = MB_RELEASED;
397 case EVENT_KEYRELEASE:
398 key_joystick_mapping = 0;
401 #if defined(TARGET_SDL2)
402 case SDL_CONTROLLERBUTTONUP:
403 HandleJoystickEvent(&event);
404 key_joystick_mapping = 0;
408 case EVENT_MAPNOTIFY:
409 window_unmapped = FALSE;
412 case EVENT_UNMAPNOTIFY:
413 /* this is only to surely prevent the 'should not happen' case
414 * of recursively looping between 'SleepWhileUnmapped()' and
415 * 'HandleOtherEvents()' which usually calls this funtion.
420 HandleOtherEvents(&event);
425 if (game_status == GAME_MODE_PLAYING)
426 KeyboardAutoRepeatOffUnlessAutoplay();
429 void HandleExposeEvent(ExposeEvent *event)
433 void HandleButtonEvent(ButtonEvent *event)
435 #if DEBUG_EVENTS_BUTTON
436 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
438 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
442 #if defined(HAS_SCREEN_KEYBOARD)
443 if (video.shifted_up)
444 event->y += video.shifted_up_pos;
447 motion_status = FALSE;
449 if (event->type == EVENT_BUTTONPRESS)
450 button_status = event->button;
452 button_status = MB_RELEASED;
454 HandleButton(event->x, event->y, button_status, event->button);
457 void HandleMotionEvent(MotionEvent *event)
459 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
462 motion_status = TRUE;
464 #if DEBUG_EVENTS_MOTION
465 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
466 button_status, event->x, event->y);
469 HandleButton(event->x, event->y, button_status, button_status);
472 #if defined(TARGET_SDL2)
474 void HandleWheelEvent(WheelEvent *event)
478 #if DEBUG_EVENTS_WHEEL
480 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
481 event->which, event->x, event->y);
483 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
484 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
485 event->which, event->x, event->y,
486 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
487 "SDL_MOUSEWHEEL_FLIPPED"));
491 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
492 event->x > 0 ? MB_WHEEL_RIGHT :
493 event->y < 0 ? MB_WHEEL_DOWN :
494 event->y > 0 ? MB_WHEEL_UP : 0);
496 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
497 // accelerated mouse wheel available on Mac and Windows
498 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
500 // no accelerated mouse wheel available on Unix/Linux
501 wheel_steps = DEFAULT_WHEEL_STEPS;
504 motion_status = FALSE;
506 button_status = button_nr;
507 HandleButton(0, 0, button_status, -button_nr);
509 button_status = MB_RELEASED;
510 HandleButton(0, 0, button_status, -button_nr);
513 void HandleWindowEvent(WindowEvent *event)
515 #if DEBUG_EVENTS_WINDOW
516 int subtype = event->event;
519 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
520 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
521 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
522 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
523 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
524 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
525 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
526 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
527 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
528 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
529 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
530 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
531 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
532 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
535 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
536 event_name, event->data1, event->data2);
540 // (not needed, as the screen gets redrawn every 20 ms anyway)
541 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
542 event->event == SDL_WINDOWEVENT_RESIZED ||
543 event->event == SDL_WINDOWEVENT_EXPOSED)
547 if (event->event == SDL_WINDOWEVENT_RESIZED)
549 if (!video.fullscreen_enabled)
551 int new_window_width = event->data1;
552 int new_window_height = event->data2;
554 // if window size has changed after resizing, calculate new scaling factor
555 if (new_window_width != video.window_width ||
556 new_window_height != video.window_height)
558 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
559 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
561 // (extreme window scaling allowed, but cannot be saved permanently)
562 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
563 setup.window_scaling_percent =
564 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
565 MAX_WINDOW_SCALING_PERCENT);
567 video.window_width = new_window_width;
568 video.window_height = new_window_height;
570 if (game_status == GAME_MODE_SETUP)
571 RedrawSetupScreenAfterFullscreenToggle();
576 #if defined(PLATFORM_ANDROID)
579 int new_display_width = event->data1;
580 int new_display_height = event->data2;
582 // if fullscreen display size has changed, device has been rotated
583 if (new_display_width != video.display_width ||
584 new_display_height != video.display_height)
586 video.display_width = new_display_width;
587 video.display_height = new_display_height;
589 SDLSetScreenProperties();
596 #define NUM_TOUCH_FINGERS 3
601 SDL_FingerID finger_id;
604 } touch_info[NUM_TOUCH_FINGERS];
606 void HandleFingerEvent(FingerEvent *event)
608 static Key motion_key_x = KSYM_UNDEFINED;
609 static Key motion_key_y = KSYM_UNDEFINED;
610 static Key button_key = KSYM_UNDEFINED;
611 static float motion_x1, motion_y1;
612 static float button_x1, button_y1;
613 static SDL_FingerID motion_id = -1;
614 static SDL_FingerID button_id = -1;
615 int move_trigger_distance_percent = setup.touch.move_distance;
616 int drop_trigger_distance_percent = setup.touch.drop_distance;
617 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
618 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
619 float event_x = event->x;
620 float event_y = event->y;
622 #if DEBUG_EVENTS_FINGER
623 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
624 event->type == EVENT_FINGERPRESS ? "pressed" :
625 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
629 event->dx, event->dy,
633 if (game_status != GAME_MODE_PLAYING)
636 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
639 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
641 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
643 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
645 event_y = (event_y - ypos) / (1 - ypos);
647 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
648 event_y > 2.0 / 3.0 && event_y < 1 ?
649 setup.input[0].key.snap :
650 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
651 event_y > 2.0 / 3.0 && event_y < 1 ?
652 setup.input[0].key.drop :
653 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
654 event_y > 0 && event_y < 1.0 / 3.0 ?
655 setup.input[0].key.up :
656 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
657 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
658 setup.input[0].key.left :
659 event_x > 8.0 / 9.0 && event_x < 1 &&
660 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
661 setup.input[0].key.right :
662 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
663 event_y > 2.0 / 3.0 && event_y < 1 ?
664 setup.input[0].key.down :
667 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
671 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
672 getKeyNameFromKey(key), key_status_name, event->fingerId);
674 // check if we already know this touch event's finger id
675 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
677 if (touch_info[i].touched &&
678 touch_info[i].finger_id == event->fingerId)
680 // Error(ERR_DEBUG, "MARK 1: %d", i);
686 if (i >= NUM_TOUCH_FINGERS)
688 if (key_status == KEY_PRESSED)
690 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
692 // unknown finger id -- get new, empty slot, if available
693 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
695 if (touch_info[i].counter < oldest_counter)
698 oldest_counter = touch_info[i].counter;
700 // Error(ERR_DEBUG, "MARK 2: %d", i);
703 if (!touch_info[i].touched)
705 // Error(ERR_DEBUG, "MARK 3: %d", i);
711 if (i >= NUM_TOUCH_FINGERS)
713 // all slots allocated -- use oldest slot
716 // Error(ERR_DEBUG, "MARK 4: %d", i);
721 // release of previously unknown key (should not happen)
723 if (key != KSYM_UNDEFINED)
725 HandleKey(key, KEY_RELEASED);
727 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
728 getKeyNameFromKey(key), "KEY_RELEASED", i);
733 if (i < NUM_TOUCH_FINGERS)
735 if (key_status == KEY_PRESSED)
737 if (touch_info[i].key != key)
739 if (touch_info[i].key != KSYM_UNDEFINED)
741 HandleKey(touch_info[i].key, KEY_RELEASED);
743 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
744 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
747 if (key != KSYM_UNDEFINED)
749 HandleKey(key, KEY_PRESSED);
751 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
752 getKeyNameFromKey(key), "KEY_PRESSED", i);
756 touch_info[i].touched = TRUE;
757 touch_info[i].finger_id = event->fingerId;
758 touch_info[i].counter = Counter();
759 touch_info[i].key = key;
763 if (touch_info[i].key != KSYM_UNDEFINED)
765 HandleKey(touch_info[i].key, KEY_RELEASED);
767 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
768 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
771 touch_info[i].touched = FALSE;
772 touch_info[i].finger_id = 0;
773 touch_info[i].counter = 0;
774 touch_info[i].key = 0;
781 // use touch direction control
783 if (event->type == EVENT_FINGERPRESS)
785 if (event_x > 1.0 / 3.0)
789 motion_id = event->fingerId;
794 motion_key_x = KSYM_UNDEFINED;
795 motion_key_y = KSYM_UNDEFINED;
797 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
803 button_id = event->fingerId;
808 button_key = setup.input[0].key.snap;
810 HandleKey(button_key, KEY_PRESSED);
812 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
815 else if (event->type == EVENT_FINGERRELEASE)
817 if (event->fingerId == motion_id)
821 if (motion_key_x != KSYM_UNDEFINED)
822 HandleKey(motion_key_x, KEY_RELEASED);
823 if (motion_key_y != KSYM_UNDEFINED)
824 HandleKey(motion_key_y, KEY_RELEASED);
826 motion_key_x = KSYM_UNDEFINED;
827 motion_key_y = KSYM_UNDEFINED;
829 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
831 else if (event->fingerId == button_id)
835 if (button_key != KSYM_UNDEFINED)
836 HandleKey(button_key, KEY_RELEASED);
838 button_key = KSYM_UNDEFINED;
840 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
843 else if (event->type == EVENT_FINGERMOTION)
845 if (event->fingerId == motion_id)
847 float distance_x = ABS(event_x - motion_x1);
848 float distance_y = ABS(event_y - motion_y1);
849 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
850 event_x > motion_x1 ? setup.input[0].key.right :
852 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
853 event_y > motion_y1 ? setup.input[0].key.down :
856 if (distance_x < move_trigger_distance / 2 ||
857 distance_x < distance_y)
858 new_motion_key_x = KSYM_UNDEFINED;
860 if (distance_y < move_trigger_distance / 2 ||
861 distance_y < distance_x)
862 new_motion_key_y = KSYM_UNDEFINED;
864 if (distance_x > move_trigger_distance ||
865 distance_y > move_trigger_distance)
867 if (new_motion_key_x != motion_key_x)
869 if (motion_key_x != KSYM_UNDEFINED)
870 HandleKey(motion_key_x, KEY_RELEASED);
871 if (new_motion_key_x != KSYM_UNDEFINED)
872 HandleKey(new_motion_key_x, KEY_PRESSED);
875 if (new_motion_key_y != motion_key_y)
877 if (motion_key_y != KSYM_UNDEFINED)
878 HandleKey(motion_key_y, KEY_RELEASED);
879 if (new_motion_key_y != KSYM_UNDEFINED)
880 HandleKey(new_motion_key_y, KEY_PRESSED);
886 motion_key_x = new_motion_key_x;
887 motion_key_y = new_motion_key_y;
889 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
892 else if (event->fingerId == button_id)
894 float distance_x = ABS(event_x - button_x1);
895 float distance_y = ABS(event_y - button_y1);
897 if (distance_x < drop_trigger_distance / 2 &&
898 distance_y > drop_trigger_distance)
900 if (button_key == setup.input[0].key.snap)
901 HandleKey(button_key, KEY_RELEASED);
906 button_key = setup.input[0].key.drop;
908 HandleKey(button_key, KEY_PRESSED);
910 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
916 static void HandleFollowFinger(int mx, int my, int button)
918 static int old_mx = 0, old_my = 0;
919 static Key motion_key_x = KSYM_UNDEFINED;
920 static Key motion_key_y = KSYM_UNDEFINED;
921 static boolean started_on_player = FALSE;
922 static boolean player_is_dropping = FALSE;
923 static int player_drop_count = 0;
924 static int last_player_x = -1;
925 static int last_player_y = -1;
927 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
930 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
932 touch_info[0].touched = TRUE;
933 touch_info[0].key = 0;
940 started_on_player = FALSE;
941 player_is_dropping = FALSE;
942 player_drop_count = 0;
946 motion_key_x = KSYM_UNDEFINED;
947 motion_key_y = KSYM_UNDEFINED;
949 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
952 else if (button == MB_RELEASED && touch_info[0].touched)
954 touch_info[0].touched = FALSE;
955 touch_info[0].key = 0;
960 if (motion_key_x != KSYM_UNDEFINED)
961 HandleKey(motion_key_x, KEY_RELEASED);
962 if (motion_key_y != KSYM_UNDEFINED)
963 HandleKey(motion_key_y, KEY_RELEASED);
965 if (started_on_player)
967 if (player_is_dropping)
969 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
971 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
975 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
977 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
981 motion_key_x = KSYM_UNDEFINED;
982 motion_key_y = KSYM_UNDEFINED;
984 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
987 if (touch_info[0].touched)
989 int src_x = local_player->jx;
990 int src_y = local_player->jy;
991 int dst_x = getLevelFromScreenX(old_mx);
992 int dst_y = getLevelFromScreenY(old_my);
993 int dx = dst_x - src_x;
994 int dy = dst_y - src_y;
995 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
996 dx > 0 ? setup.input[0].key.right :
998 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
999 dy > 0 ? setup.input[0].key.down :
1002 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1003 (last_player_x != local_player->jx ||
1004 last_player_y != local_player->jy))
1006 // in case of asymmetric diagonal movement, use "preferred" direction
1008 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1010 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1011 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1013 local_player->last_move_dir = last_move_dir;
1015 // (required to prevent accidentally forcing direction for next movement)
1016 last_player_x = local_player->jx;
1017 last_player_y = local_player->jy;
1020 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1022 started_on_player = TRUE;
1023 player_drop_count = getPlayerInventorySize(0);
1024 player_is_dropping = (player_drop_count > 0);
1026 if (player_is_dropping)
1028 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1030 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1034 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1036 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1039 else if (dx != 0 || dy != 0)
1041 if (player_is_dropping &&
1042 player_drop_count == getPlayerInventorySize(0))
1044 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1046 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1047 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1049 player_is_dropping = FALSE;
1053 if (new_motion_key_x != motion_key_x)
1055 Error(ERR_DEBUG, "---------- %s %s ----------",
1056 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1057 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1059 if (motion_key_x != KSYM_UNDEFINED)
1060 HandleKey(motion_key_x, KEY_RELEASED);
1061 if (new_motion_key_x != KSYM_UNDEFINED)
1062 HandleKey(new_motion_key_x, KEY_PRESSED);
1065 if (new_motion_key_y != motion_key_y)
1067 Error(ERR_DEBUG, "---------- %s %s ----------",
1068 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1069 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1071 if (motion_key_y != KSYM_UNDEFINED)
1072 HandleKey(motion_key_y, KEY_RELEASED);
1073 if (new_motion_key_y != KSYM_UNDEFINED)
1074 HandleKey(new_motion_key_y, KEY_PRESSED);
1077 motion_key_x = new_motion_key_x;
1078 motion_key_y = new_motion_key_y;
1082 static boolean checkTextInputKeyModState()
1084 // when playing, only handle raw key events and ignore text input
1085 if (game_status == GAME_MODE_PLAYING)
1088 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1091 void HandleTextEvent(TextEvent *event)
1093 char *text = event->text;
1094 Key key = getKeyFromKeyName(text);
1096 #if DEBUG_EVENTS_TEXT
1097 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1100 text[0], (int)(text[0]),
1102 getKeyNameFromKey(key),
1106 #if !defined(HAS_SCREEN_KEYBOARD)
1107 // non-mobile devices: only handle key input with modifier keys pressed here
1108 // (every other key input is handled directly as physical key input event)
1109 if (!checkTextInputKeyModState())
1113 // process text input as "classic" (with uppercase etc.) key input event
1114 HandleKey(key, KEY_PRESSED);
1115 HandleKey(key, KEY_RELEASED);
1118 void HandlePauseResumeEvent(PauseResumeEvent *event)
1120 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1124 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1132 void HandleKeyEvent(KeyEvent *event)
1134 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1135 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1136 Key key = GetEventKey(event, with_modifiers);
1137 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1139 #if DEBUG_EVENTS_KEY
1140 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1141 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1142 event->keysym.scancode,
1147 getKeyNameFromKey(key));
1150 #if defined(PLATFORM_ANDROID)
1151 // always map the "back" button to the "escape" key on Android devices
1152 if (key == KSYM_Back)
1156 HandleKeyModState(keymod, key_status);
1158 #if defined(TARGET_SDL2)
1159 // only handle raw key input without text modifier keys pressed
1160 if (!checkTextInputKeyModState())
1161 HandleKey(key, key_status);
1163 HandleKey(key, key_status);
1167 void HandleFocusEvent(FocusChangeEvent *event)
1169 static int old_joystick_status = -1;
1171 if (event->type == EVENT_FOCUSOUT)
1173 KeyboardAutoRepeatOn();
1174 old_joystick_status = joystick.status;
1175 joystick.status = JOYSTICK_NOT_AVAILABLE;
1177 ClearPlayerAction();
1179 else if (event->type == EVENT_FOCUSIN)
1181 /* When there are two Rocks'n'Diamonds windows which overlap and
1182 the player moves the pointer from one game window to the other,
1183 a 'FocusOut' event is generated for the window the pointer is
1184 leaving and a 'FocusIn' event is generated for the window the
1185 pointer is entering. In some cases, it can happen that the
1186 'FocusIn' event is handled by the one game process before the
1187 'FocusOut' event by the other game process. In this case the
1188 X11 environment would end up with activated keyboard auto repeat,
1189 because unfortunately this is a global setting and not (which
1190 would be far better) set for each X11 window individually.
1191 The effect would be keyboard auto repeat while playing the game
1192 (game_status == GAME_MODE_PLAYING), which is not desired.
1193 To avoid this special case, we just wait 1/10 second before
1194 processing the 'FocusIn' event.
1197 if (game_status == GAME_MODE_PLAYING)
1200 KeyboardAutoRepeatOffUnlessAutoplay();
1203 if (old_joystick_status != -1)
1204 joystick.status = old_joystick_status;
1208 void HandleClientMessageEvent(ClientMessageEvent *event)
1210 if (CheckCloseWindowEvent(event))
1214 void HandleWindowManagerEvent(Event *event)
1216 #if defined(TARGET_SDL)
1217 SDLHandleWindowManagerEvent(event);
1221 void HandleButton(int mx, int my, int button, int button_nr)
1223 static int old_mx = 0, old_my = 0;
1224 boolean button_hold = FALSE;
1230 button_nr = -button_nr;
1239 #if defined(PLATFORM_ANDROID)
1240 // when playing, only handle gadgets when using "follow finger" controls
1241 boolean handle_gadgets =
1242 (game_status != GAME_MODE_PLAYING ||
1243 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1245 if (handle_gadgets &&
1246 HandleGadgets(mx, my, button))
1248 /* do not handle this button event anymore */
1249 mx = my = -32; /* force mouse event to be outside screen tiles */
1252 if (HandleGadgets(mx, my, button))
1254 /* do not handle this button event anymore */
1255 mx = my = -32; /* force mouse event to be outside screen tiles */
1259 if (HandleGlobalAnimClicks(mx, my, button))
1261 /* do not handle this button event anymore */
1262 mx = my = -32; /* force mouse event to be outside screen tiles */
1265 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1268 /* do not use scroll wheel button events for anything other than gadgets */
1269 if (IS_WHEEL_BUTTON(button_nr))
1272 switch (game_status)
1274 case GAME_MODE_TITLE:
1275 HandleTitleScreen(mx, my, 0, 0, button);
1278 case GAME_MODE_MAIN:
1279 HandleMainMenu(mx, my, 0, 0, button);
1282 case GAME_MODE_PSEUDO_TYPENAME:
1283 HandleTypeName(0, KSYM_Return);
1286 case GAME_MODE_LEVELS:
1287 HandleChooseLevelSet(mx, my, 0, 0, button);
1290 case GAME_MODE_LEVELNR:
1291 HandleChooseLevelNr(mx, my, 0, 0, button);
1294 case GAME_MODE_SCORES:
1295 HandleHallOfFame(0, 0, 0, 0, button);
1298 case GAME_MODE_EDITOR:
1299 HandleLevelEditorIdle();
1302 case GAME_MODE_INFO:
1303 HandleInfoScreen(mx, my, 0, 0, button);
1306 case GAME_MODE_SETUP:
1307 HandleSetupScreen(mx, my, 0, 0, button);
1310 #if defined(TARGET_SDL2)
1311 case GAME_MODE_PLAYING:
1312 HandleFollowFinger(mx, my, button);
1316 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1317 GetKeyModState() & KMOD_Control)
1318 DumpTileFromScreen(mx, my);
1328 static boolean is_string_suffix(char *string, char *suffix)
1330 int string_len = strlen(string);
1331 int suffix_len = strlen(suffix);
1333 if (suffix_len > string_len)
1336 return (strEqual(&string[string_len - suffix_len], suffix));
1339 #define MAX_CHEAT_INPUT_LEN 32
1341 static void HandleKeysSpecial(Key key)
1343 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1344 char letter = getCharFromKey(key);
1345 int cheat_input_len = strlen(cheat_input);
1351 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1353 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1354 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1356 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1359 cheat_input[cheat_input_len++] = letter;
1360 cheat_input[cheat_input_len] = '\0';
1362 #if DEBUG_EVENTS_KEY
1363 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1366 if (game_status == GAME_MODE_MAIN)
1368 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1369 is_string_suffix(cheat_input, ":ist"))
1371 InsertSolutionTape();
1373 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1374 is_string_suffix(cheat_input, ":rg"))
1376 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1379 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1380 is_string_suffix(cheat_input, ":rs"))
1382 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1385 else if (is_string_suffix(cheat_input, ":reload-music") ||
1386 is_string_suffix(cheat_input, ":rm"))
1388 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1391 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1392 is_string_suffix(cheat_input, ":ra"))
1394 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1395 1 << ARTWORK_TYPE_SOUNDS |
1396 1 << ARTWORK_TYPE_MUSIC);
1399 else if (is_string_suffix(cheat_input, ":dump-level") ||
1400 is_string_suffix(cheat_input, ":dl"))
1404 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1405 is_string_suffix(cheat_input, ":dt"))
1409 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1410 is_string_suffix(cheat_input, ":ft"))
1412 /* fix single-player tapes that contain player input for more than one
1413 player (due to a bug in 3.3.1.2 and earlier versions), which results
1414 in playing levels with more than one player in multi-player mode,
1415 even though the tape was originally recorded in single-player mode */
1417 /* remove player input actions for all players but the first one */
1418 for (i = 1; i < MAX_PLAYERS; i++)
1419 tape.player_participates[i] = FALSE;
1421 tape.changed = TRUE;
1423 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1424 is_string_suffix(cheat_input, ":snl"))
1426 SaveNativeLevel(&level);
1429 else if (game_status == GAME_MODE_PLAYING)
1432 if (is_string_suffix(cheat_input, ".q"))
1433 DEBUG_SetMaximumDynamite();
1436 else if (game_status == GAME_MODE_EDITOR)
1438 if (is_string_suffix(cheat_input, ":dump-brush") ||
1439 is_string_suffix(cheat_input, ":DB"))
1443 else if (is_string_suffix(cheat_input, ":DDB"))
1450 void HandleKeysDebug(Key key)
1455 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1457 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1459 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1461 if (key == setup.debug.frame_delay_key[i] &&
1462 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1464 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1465 setup.debug.frame_delay[i] : setup.game_frame_delay);
1467 if (!setup.debug.frame_delay_game_only)
1468 MenuFrameDelay = GameFrameDelay;
1470 SetVideoFrameDelay(GameFrameDelay);
1472 if (GameFrameDelay > ONE_SECOND_DELAY)
1473 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1474 else if (GameFrameDelay != 0)
1475 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1476 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1477 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1479 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1486 if (game_status == GAME_MODE_PLAYING)
1490 options.debug = !options.debug;
1492 Error(ERR_DEBUG, "debug mode %s",
1493 (options.debug ? "enabled" : "disabled"));
1495 else if (key == KSYM_v)
1497 Error(ERR_DEBUG, "currently using game engine version %d",
1498 game.engine_version);
1504 void HandleKey(Key key, int key_status)
1506 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1507 static boolean ignore_repeated_key = FALSE;
1508 static struct SetupKeyboardInfo ski;
1509 static struct SetupShortcutInfo ssi;
1518 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1519 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1520 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1521 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1522 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1523 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1528 #if defined(TARGET_SDL2)
1529 /* map special "play/pause" media key to default key for play/pause actions */
1530 if (key == KSYM_PlayPause)
1534 HandleSpecialGameControllerKeys(key, key_status);
1536 if (game_status == GAME_MODE_PLAYING)
1538 /* only needed for single-step tape recording mode */
1539 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1540 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1541 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1542 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1545 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1547 byte key_action = 0;
1549 if (setup.input[pnr].use_joystick)
1552 ski = setup.input[pnr].key;
1554 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1555 if (key == *key_info[i].key_custom)
1556 key_action |= key_info[i].action;
1558 /* use combined snap+direction keys for the first player only */
1561 ssi = setup.shortcut;
1563 for (i = 0; i < NUM_DIRECTIONS; i++)
1564 if (key == *key_info[i].key_snap)
1565 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1568 /* clear delayed snap and drop actions in single step mode (see below) */
1569 if (tape.single_step)
1571 if (clear_snap_button[pnr])
1573 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1574 clear_snap_button[pnr] = FALSE;
1577 if (clear_drop_button[pnr])
1579 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1580 clear_drop_button[pnr] = FALSE;
1584 if (key_status == KEY_PRESSED)
1585 stored_player[pnr].action |= key_action;
1587 stored_player[pnr].action &= ~key_action;
1589 if (tape.single_step && tape.recording && tape.pausing)
1591 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1593 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1595 /* if snap key already pressed, don't snap when releasing (below) */
1596 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1597 element_snapped[pnr] = TRUE;
1599 /* if drop key already pressed, don't drop when releasing (below) */
1600 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1601 element_dropped[pnr] = TRUE;
1603 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1605 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1606 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1609 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1610 getRedDiskReleaseFlag_SP() == 0)
1611 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1613 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1616 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1618 if (key_action & KEY_BUTTON_SNAP)
1620 /* if snap key was released without moving (see above), snap now */
1621 if (!element_snapped[pnr])
1623 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1625 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1627 /* clear delayed snap button on next event */
1628 clear_snap_button[pnr] = TRUE;
1631 element_snapped[pnr] = FALSE;
1634 if (key_action & KEY_BUTTON_DROP &&
1635 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1637 /* if drop key was released without moving (see above), drop now */
1638 if (!element_dropped[pnr])
1640 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1642 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1643 getRedDiskReleaseFlag_SP() != 0)
1644 stored_player[pnr].action |= KEY_BUTTON_DROP;
1646 /* clear delayed drop button on next event */
1647 clear_drop_button[pnr] = TRUE;
1650 element_dropped[pnr] = FALSE;
1654 else if (tape.recording && tape.pausing)
1656 /* prevent key release events from un-pausing a paused game */
1657 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1658 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1664 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1665 if (key == key_info[i].key_default)
1666 joy |= key_info[i].action;
1671 if (key_status == KEY_PRESSED)
1672 key_joystick_mapping |= joy;
1674 key_joystick_mapping &= ~joy;
1679 if (game_status != GAME_MODE_PLAYING)
1680 key_joystick_mapping = 0;
1682 if (key_status == KEY_RELEASED)
1684 // reset flag to ignore repeated "key pressed" events after key release
1685 ignore_repeated_key = FALSE;
1690 if ((key == KSYM_F11 ||
1691 ((key == KSYM_Return ||
1692 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1693 video.fullscreen_available &&
1694 !ignore_repeated_key)
1696 setup.fullscreen = !setup.fullscreen;
1698 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1700 if (game_status == GAME_MODE_SETUP)
1701 RedrawSetupScreenAfterFullscreenToggle();
1703 // set flag to ignore repeated "key pressed" events
1704 ignore_repeated_key = TRUE;
1709 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1710 key == KSYM_minus || key == KSYM_KP_Subtract ||
1711 key == KSYM_plus || key == KSYM_KP_Add ||
1712 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1713 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1714 video.window_scaling_available &&
1715 !video.fullscreen_enabled)
1717 if (key == KSYM_0 || key == KSYM_KP_0)
1718 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1719 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1720 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1722 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1724 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1725 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1726 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1727 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1729 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1731 if (game_status == GAME_MODE_SETUP)
1732 RedrawSetupScreenAfterFullscreenToggle();
1737 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1738 key == KSYM_Return ||
1739 key == KSYM_Escape)))
1741 /* do not handle this key event anymore */
1742 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1746 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1747 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1754 if (game_status == GAME_MODE_MAIN &&
1755 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1757 StartGameActions(options.network, setup.autorecord, level.random_seed);
1762 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1764 if (key == setup.shortcut.save_game)
1766 else if (key == setup.shortcut.load_game)
1768 else if (key == setup.shortcut.toggle_pause)
1769 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1771 HandleTapeButtonKeys(key);
1772 HandleSoundButtonKeys(key);
1775 if (game_status == GAME_MODE_PLAYING && !network_playing)
1777 int centered_player_nr_next = -999;
1779 if (key == setup.shortcut.focus_player_all)
1780 centered_player_nr_next = -1;
1782 for (i = 0; i < MAX_PLAYERS; i++)
1783 if (key == setup.shortcut.focus_player[i])
1784 centered_player_nr_next = i;
1786 if (centered_player_nr_next != -999)
1788 game.centered_player_nr_next = centered_player_nr_next;
1789 game.set_centered_player = TRUE;
1793 tape.centered_player_nr_next = game.centered_player_nr_next;
1794 tape.set_centered_player = TRUE;
1799 HandleKeysSpecial(key);
1801 if (HandleGadgetsKeyInput(key))
1803 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1804 key = KSYM_UNDEFINED;
1807 switch (game_status)
1809 case GAME_MODE_PSEUDO_TYPENAME:
1810 HandleTypeName(0, key);
1813 case GAME_MODE_TITLE:
1814 case GAME_MODE_MAIN:
1815 case GAME_MODE_LEVELS:
1816 case GAME_MODE_LEVELNR:
1817 case GAME_MODE_SETUP:
1818 case GAME_MODE_INFO:
1819 case GAME_MODE_SCORES:
1824 if (game_status == GAME_MODE_TITLE)
1825 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1826 else if (game_status == GAME_MODE_MAIN)
1827 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1828 else if (game_status == GAME_MODE_LEVELS)
1829 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1830 else if (game_status == GAME_MODE_LEVELNR)
1831 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1832 else if (game_status == GAME_MODE_SETUP)
1833 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1834 else if (game_status == GAME_MODE_INFO)
1835 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1836 else if (game_status == GAME_MODE_SCORES)
1837 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1841 if (game_status != GAME_MODE_MAIN)
1842 FadeSkipNextFadeIn();
1844 if (game_status == GAME_MODE_TITLE)
1845 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1846 else if (game_status == GAME_MODE_LEVELS)
1847 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1848 else if (game_status == GAME_MODE_LEVELNR)
1849 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1850 else if (game_status == GAME_MODE_SETUP)
1851 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1852 else if (game_status == GAME_MODE_INFO)
1853 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1854 else if (game_status == GAME_MODE_SCORES)
1855 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1859 if (game_status == GAME_MODE_LEVELS)
1860 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1861 else if (game_status == GAME_MODE_LEVELNR)
1862 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1863 else if (game_status == GAME_MODE_SETUP)
1864 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1865 else if (game_status == GAME_MODE_INFO)
1866 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1867 else if (game_status == GAME_MODE_SCORES)
1868 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1871 case KSYM_Page_Down:
1872 if (game_status == GAME_MODE_LEVELS)
1873 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1874 else if (game_status == GAME_MODE_LEVELNR)
1875 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1876 else if (game_status == GAME_MODE_SETUP)
1877 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1878 else if (game_status == GAME_MODE_INFO)
1879 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1880 else if (game_status == GAME_MODE_SCORES)
1881 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1889 case GAME_MODE_EDITOR:
1890 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1891 HandleLevelEditorKeyInput(key);
1894 case GAME_MODE_PLAYING:
1899 RequestQuitGame(setup.ask_on_escape);
1909 if (key == KSYM_Escape)
1911 SetGameStatus(GAME_MODE_MAIN);
1919 HandleKeysDebug(key);
1922 void HandleNoEvent()
1924 // if (button_status && game_status != GAME_MODE_PLAYING)
1925 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1927 HandleButton(0, 0, button_status, -button_status);
1934 #if defined(NETWORK_AVALIABLE)
1935 if (options.network)
1939 switch (game_status)
1941 case GAME_MODE_MAIN:
1942 DrawPreviewLevelAnimation();
1945 case GAME_MODE_EDITOR:
1946 HandleLevelEditorIdle();
1949 #if defined(TARGET_SDL2)
1950 case GAME_MODE_PLAYING:
1951 HandleFollowFinger(-1, -1, -1);
1960 static int HandleJoystickForAllPlayers()
1964 boolean no_joysticks_configured = TRUE;
1965 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1966 static byte joy_action_last[MAX_PLAYERS];
1968 for (i = 0; i < MAX_PLAYERS; i++)
1969 if (setup.input[i].use_joystick)
1970 no_joysticks_configured = FALSE;
1972 /* if no joysticks configured, map connected joysticks to players */
1973 if (no_joysticks_configured)
1974 use_as_joystick_nr = TRUE;
1976 for (i = 0; i < MAX_PLAYERS; i++)
1978 byte joy_action = 0;
1980 joy_action = JoystickExt(i, use_as_joystick_nr);
1981 result |= joy_action;
1983 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1984 joy_action != joy_action_last[i])
1985 stored_player[i].action = joy_action;
1987 joy_action_last[i] = joy_action;
1993 void HandleJoystick()
1995 int joystick = HandleJoystickForAllPlayers();
1996 int keyboard = key_joystick_mapping;
1997 int joy = (joystick | keyboard);
1998 int left = joy & JOY_LEFT;
1999 int right = joy & JOY_RIGHT;
2000 int up = joy & JOY_UP;
2001 int down = joy & JOY_DOWN;
2002 int button = joy & JOY_BUTTON;
2003 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
2004 int dx = (left ? -1 : right ? 1 : 0);
2005 int dy = (up ? -1 : down ? 1 : 0);
2007 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2009 /* do not handle this button event anymore */
2013 switch (game_status)
2015 case GAME_MODE_TITLE:
2016 case GAME_MODE_MAIN:
2017 case GAME_MODE_LEVELS:
2018 case GAME_MODE_LEVELNR:
2019 case GAME_MODE_SETUP:
2020 case GAME_MODE_INFO:
2022 static unsigned int joystickmove_delay = 0;
2023 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
2024 static int joystick_last = 0;
2026 if (joystick && !button &&
2027 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2029 /* delay joystick actions if buttons/axes continually pressed */
2030 newbutton = dx = dy = 0;
2034 /* start with longer delay, then continue with shorter delay */
2035 if (joystick != joystick_last)
2036 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2038 joystickmove_delay_value = GADGET_FRAME_DELAY;
2041 if (game_status == GAME_MODE_TITLE)
2042 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2043 else if (game_status == GAME_MODE_MAIN)
2044 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2045 else if (game_status == GAME_MODE_LEVELS)
2046 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2047 else if (game_status == GAME_MODE_LEVELNR)
2048 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2049 else if (game_status == GAME_MODE_SETUP)
2050 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2051 else if (game_status == GAME_MODE_INFO)
2052 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2054 joystick_last = joystick;
2059 case GAME_MODE_SCORES:
2060 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2063 case GAME_MODE_PLAYING:
2064 if (tape.playing || keyboard)
2065 newbutton = ((joy & JOY_BUTTON) != 0);
2067 if (newbutton && AllPlayersGone)
2074 if (tape.recording && tape.pausing)
2076 if (joystick & JOY_ACTION)
2077 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2087 void HandleSpecialGameControllerButtons(Event *event)
2089 #if defined(TARGET_SDL2)
2090 switch (event->type)
2092 case SDL_CONTROLLERBUTTONDOWN:
2093 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2094 HandleKey(KSYM_space, KEY_PRESSED);
2095 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2096 HandleKey(KSYM_Escape, KEY_PRESSED);
2100 case SDL_CONTROLLERBUTTONUP:
2101 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2102 HandleKey(KSYM_space, KEY_RELEASED);
2103 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2104 HandleKey(KSYM_Escape, KEY_RELEASED);
2111 void HandleSpecialGameControllerKeys(Key key, int key_status)
2113 #if defined(TARGET_SDL2)
2114 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2115 int button = SDL_CONTROLLER_BUTTON_INVALID;
2117 /* map keys to joystick buttons (special hack for Amazon Fire TV remote) */
2118 if (key == KSYM_Rewind)
2119 button = SDL_CONTROLLER_BUTTON_A;
2120 else if (key == KSYM_FastForward || key == KSYM_Menu)
2121 button = SDL_CONTROLLER_BUTTON_B;
2123 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2127 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2128 SDL_CONTROLLERBUTTONUP);
2130 event.cbutton.which = 0; /* first joystick (Amazon Fire TV remote) */
2131 event.cbutton.button = button;
2132 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2135 HandleJoystickEvent(&event);