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:
362 HandleOtherEvents(&event);
368 void ClearPlayerAction()
372 /* simulate key release events for still pressed keys */
373 key_joystick_mapping = 0;
374 for (i = 0; i < MAX_PLAYERS; i++)
375 stored_player[i].action = 0;
378 void SleepWhileUnmapped()
380 boolean window_unmapped = TRUE;
382 KeyboardAutoRepeatOn();
384 while (window_unmapped)
392 case EVENT_BUTTONRELEASE:
393 button_status = MB_RELEASED;
396 case EVENT_KEYRELEASE:
397 key_joystick_mapping = 0;
400 #if defined(TARGET_SDL2)
401 case SDL_CONTROLLERBUTTONUP:
402 key_joystick_mapping = 0;
406 case EVENT_MAPNOTIFY:
407 window_unmapped = FALSE;
410 case EVENT_UNMAPNOTIFY:
411 /* this is only to surely prevent the 'should not happen' case
412 * of recursively looping between 'SleepWhileUnmapped()' and
413 * 'HandleOtherEvents()' which usually calls this funtion.
418 HandleOtherEvents(&event);
423 if (game_status == GAME_MODE_PLAYING)
424 KeyboardAutoRepeatOffUnlessAutoplay();
427 void HandleExposeEvent(ExposeEvent *event)
431 void HandleButtonEvent(ButtonEvent *event)
433 #if DEBUG_EVENTS_BUTTON
434 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
436 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
440 #if defined(HAS_SCREEN_KEYBOARD)
441 if (video.shifted_up)
442 event->y += video.shifted_up_pos;
445 motion_status = FALSE;
447 if (event->type == EVENT_BUTTONPRESS)
448 button_status = event->button;
450 button_status = MB_RELEASED;
452 HandleButton(event->x, event->y, button_status, event->button);
455 void HandleMotionEvent(MotionEvent *event)
457 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
460 motion_status = TRUE;
462 #if DEBUG_EVENTS_MOTION
463 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
464 button_status, event->x, event->y);
467 HandleButton(event->x, event->y, button_status, button_status);
470 #if defined(TARGET_SDL2)
472 void HandleWheelEvent(WheelEvent *event)
476 #if DEBUG_EVENTS_WHEEL
478 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
479 event->which, event->x, event->y);
481 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
482 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
483 event->which, event->x, event->y,
484 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
485 "SDL_MOUSEWHEEL_FLIPPED"));
489 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
490 event->x > 0 ? MB_WHEEL_RIGHT :
491 event->y < 0 ? MB_WHEEL_DOWN :
492 event->y > 0 ? MB_WHEEL_UP : 0);
494 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
495 // accelerated mouse wheel available on Mac and Windows
496 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
498 // no accelerated mouse wheel available on Unix/Linux
499 wheel_steps = DEFAULT_WHEEL_STEPS;
502 motion_status = FALSE;
504 button_status = button_nr;
505 HandleButton(0, 0, button_status, -button_nr);
507 button_status = MB_RELEASED;
508 HandleButton(0, 0, button_status, -button_nr);
511 void HandleWindowEvent(WindowEvent *event)
513 #if DEBUG_EVENTS_WINDOW
514 int subtype = event->event;
517 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
518 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
519 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
520 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
521 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
522 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
523 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
524 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
525 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
526 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
527 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
528 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
529 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
530 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
533 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
534 event_name, event->data1, event->data2);
538 // (not needed, as the screen gets redrawn every 20 ms anyway)
539 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
540 event->event == SDL_WINDOWEVENT_RESIZED ||
541 event->event == SDL_WINDOWEVENT_EXPOSED)
545 if (event->event == SDL_WINDOWEVENT_RESIZED)
547 if (!video.fullscreen_enabled)
549 int new_window_width = event->data1;
550 int new_window_height = event->data2;
552 // if window size has changed after resizing, calculate new scaling factor
553 if (new_window_width != video.window_width ||
554 new_window_height != video.window_height)
556 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
557 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
559 // (extreme window scaling allowed, but cannot be saved permanently)
560 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
561 setup.window_scaling_percent =
562 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
563 MAX_WINDOW_SCALING_PERCENT);
565 video.window_width = new_window_width;
566 video.window_height = new_window_height;
568 if (game_status == GAME_MODE_SETUP)
569 RedrawSetupScreenAfterFullscreenToggle();
574 #if defined(PLATFORM_ANDROID)
577 int new_display_width = event->data1;
578 int new_display_height = event->data2;
580 // if fullscreen display size has changed, device has been rotated
581 if (new_display_width != video.display_width ||
582 new_display_height != video.display_height)
584 video.display_width = new_display_width;
585 video.display_height = new_display_height;
587 SDLSetScreenProperties();
594 #define NUM_TOUCH_FINGERS 3
599 SDL_FingerID finger_id;
602 } touch_info[NUM_TOUCH_FINGERS];
604 void HandleFingerEvent(FingerEvent *event)
606 static Key motion_key_x = KSYM_UNDEFINED;
607 static Key motion_key_y = KSYM_UNDEFINED;
608 static Key button_key = KSYM_UNDEFINED;
609 static float motion_x1, motion_y1;
610 static float button_x1, button_y1;
611 static SDL_FingerID motion_id = -1;
612 static SDL_FingerID button_id = -1;
613 int move_trigger_distance_percent = setup.touch.move_distance;
614 int drop_trigger_distance_percent = setup.touch.drop_distance;
615 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
616 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
617 float event_x = event->x;
618 float event_y = event->y;
620 #if DEBUG_EVENTS_FINGER
621 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
622 event->type == EVENT_FINGERPRESS ? "pressed" :
623 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
627 event->dx, event->dy,
631 if (game_status != GAME_MODE_PLAYING)
634 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
637 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
639 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
641 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
643 event_y = (event_y - ypos) / (1 - ypos);
645 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
646 event_y > 2.0 / 3.0 && event_y < 1 ?
647 setup.input[0].key.snap :
648 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
649 event_y > 2.0 / 3.0 && event_y < 1 ?
650 setup.input[0].key.drop :
651 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
652 event_y > 0 && event_y < 1.0 / 3.0 ?
653 setup.input[0].key.up :
654 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
655 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
656 setup.input[0].key.left :
657 event_x > 8.0 / 9.0 && event_x < 1 &&
658 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
659 setup.input[0].key.right :
660 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
661 event_y > 2.0 / 3.0 && event_y < 1 ?
662 setup.input[0].key.down :
665 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
669 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
670 getKeyNameFromKey(key), key_status_name, event->fingerId);
672 // check if we already know this touch event's finger id
673 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
675 if (touch_info[i].touched &&
676 touch_info[i].finger_id == event->fingerId)
678 // Error(ERR_DEBUG, "MARK 1: %d", i);
684 if (i >= NUM_TOUCH_FINGERS)
686 if (key_status == KEY_PRESSED)
688 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
690 // unknown finger id -- get new, empty slot, if available
691 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
693 if (touch_info[i].counter < oldest_counter)
696 oldest_counter = touch_info[i].counter;
698 // Error(ERR_DEBUG, "MARK 2: %d", i);
701 if (!touch_info[i].touched)
703 // Error(ERR_DEBUG, "MARK 3: %d", i);
709 if (i >= NUM_TOUCH_FINGERS)
711 // all slots allocated -- use oldest slot
714 // Error(ERR_DEBUG, "MARK 4: %d", i);
719 // release of previously unknown key (should not happen)
721 if (key != KSYM_UNDEFINED)
723 HandleKey(key, KEY_RELEASED);
725 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
726 getKeyNameFromKey(key), "KEY_RELEASED", i);
731 if (i < NUM_TOUCH_FINGERS)
733 if (key_status == KEY_PRESSED)
735 if (touch_info[i].key != key)
737 if (touch_info[i].key != KSYM_UNDEFINED)
739 HandleKey(touch_info[i].key, KEY_RELEASED);
741 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
742 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
745 if (key != KSYM_UNDEFINED)
747 HandleKey(key, KEY_PRESSED);
749 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
750 getKeyNameFromKey(key), "KEY_PRESSED", i);
754 touch_info[i].touched = TRUE;
755 touch_info[i].finger_id = event->fingerId;
756 touch_info[i].counter = Counter();
757 touch_info[i].key = key;
761 if (touch_info[i].key != KSYM_UNDEFINED)
763 HandleKey(touch_info[i].key, KEY_RELEASED);
765 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
766 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
769 touch_info[i].touched = FALSE;
770 touch_info[i].finger_id = 0;
771 touch_info[i].counter = 0;
772 touch_info[i].key = 0;
779 // use touch direction control
781 if (event->type == EVENT_FINGERPRESS)
783 if (event_x > 1.0 / 3.0)
787 motion_id = event->fingerId;
792 motion_key_x = KSYM_UNDEFINED;
793 motion_key_y = KSYM_UNDEFINED;
795 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
801 button_id = event->fingerId;
806 button_key = setup.input[0].key.snap;
808 HandleKey(button_key, KEY_PRESSED);
810 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
813 else if (event->type == EVENT_FINGERRELEASE)
815 if (event->fingerId == motion_id)
819 if (motion_key_x != KSYM_UNDEFINED)
820 HandleKey(motion_key_x, KEY_RELEASED);
821 if (motion_key_y != KSYM_UNDEFINED)
822 HandleKey(motion_key_y, KEY_RELEASED);
824 motion_key_x = KSYM_UNDEFINED;
825 motion_key_y = KSYM_UNDEFINED;
827 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
829 else if (event->fingerId == button_id)
833 if (button_key != KSYM_UNDEFINED)
834 HandleKey(button_key, KEY_RELEASED);
836 button_key = KSYM_UNDEFINED;
838 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
841 else if (event->type == EVENT_FINGERMOTION)
843 if (event->fingerId == motion_id)
845 float distance_x = ABS(event_x - motion_x1);
846 float distance_y = ABS(event_y - motion_y1);
847 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
848 event_x > motion_x1 ? setup.input[0].key.right :
850 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
851 event_y > motion_y1 ? setup.input[0].key.down :
854 if (distance_x < move_trigger_distance / 2 ||
855 distance_x < distance_y)
856 new_motion_key_x = KSYM_UNDEFINED;
858 if (distance_y < move_trigger_distance / 2 ||
859 distance_y < distance_x)
860 new_motion_key_y = KSYM_UNDEFINED;
862 if (distance_x > move_trigger_distance ||
863 distance_y > move_trigger_distance)
865 if (new_motion_key_x != motion_key_x)
867 if (motion_key_x != KSYM_UNDEFINED)
868 HandleKey(motion_key_x, KEY_RELEASED);
869 if (new_motion_key_x != KSYM_UNDEFINED)
870 HandleKey(new_motion_key_x, KEY_PRESSED);
873 if (new_motion_key_y != motion_key_y)
875 if (motion_key_y != KSYM_UNDEFINED)
876 HandleKey(motion_key_y, KEY_RELEASED);
877 if (new_motion_key_y != KSYM_UNDEFINED)
878 HandleKey(new_motion_key_y, KEY_PRESSED);
884 motion_key_x = new_motion_key_x;
885 motion_key_y = new_motion_key_y;
887 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
890 else if (event->fingerId == button_id)
892 float distance_x = ABS(event_x - button_x1);
893 float distance_y = ABS(event_y - button_y1);
895 if (distance_x < drop_trigger_distance / 2 &&
896 distance_y > drop_trigger_distance)
898 if (button_key == setup.input[0].key.snap)
899 HandleKey(button_key, KEY_RELEASED);
904 button_key = setup.input[0].key.drop;
906 HandleKey(button_key, KEY_PRESSED);
908 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
914 static void HandleFollowFinger(int mx, int my, int button)
916 static int old_mx = 0, old_my = 0;
917 static Key motion_key_x = KSYM_UNDEFINED;
918 static Key motion_key_y = KSYM_UNDEFINED;
919 static boolean started_on_player = FALSE;
920 static boolean player_is_dropping = FALSE;
921 static int player_drop_count = 0;
922 static int last_player_x = -1;
923 static int last_player_y = -1;
925 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
928 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
930 touch_info[0].touched = TRUE;
931 touch_info[0].key = 0;
938 started_on_player = FALSE;
939 player_is_dropping = FALSE;
940 player_drop_count = 0;
944 motion_key_x = KSYM_UNDEFINED;
945 motion_key_y = KSYM_UNDEFINED;
947 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
950 else if (button == MB_RELEASED && touch_info[0].touched)
952 touch_info[0].touched = FALSE;
953 touch_info[0].key = 0;
958 if (motion_key_x != KSYM_UNDEFINED)
959 HandleKey(motion_key_x, KEY_RELEASED);
960 if (motion_key_y != KSYM_UNDEFINED)
961 HandleKey(motion_key_y, KEY_RELEASED);
963 if (started_on_player)
965 if (player_is_dropping)
967 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
969 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
973 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
975 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
979 motion_key_x = KSYM_UNDEFINED;
980 motion_key_y = KSYM_UNDEFINED;
982 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
985 if (touch_info[0].touched)
987 int src_x = local_player->jx;
988 int src_y = local_player->jy;
989 int dst_x = getLevelFromScreenX(old_mx);
990 int dst_y = getLevelFromScreenY(old_my);
991 int dx = dst_x - src_x;
992 int dy = dst_y - src_y;
993 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
994 dx > 0 ? setup.input[0].key.right :
996 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
997 dy > 0 ? setup.input[0].key.down :
1000 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1001 (last_player_x != local_player->jx ||
1002 last_player_y != local_player->jy))
1004 // in case of asymmetric diagonal movement, use "preferred" direction
1006 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1008 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1009 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1011 local_player->last_move_dir = last_move_dir;
1013 // (required to prevent accidentally forcing direction for next movement)
1014 last_player_x = local_player->jx;
1015 last_player_y = local_player->jy;
1018 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1020 started_on_player = TRUE;
1021 player_drop_count = getPlayerInventorySize(0);
1022 player_is_dropping = (player_drop_count > 0);
1024 if (player_is_dropping)
1026 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1028 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1032 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1034 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1037 else if (dx != 0 || dy != 0)
1039 if (player_is_dropping &&
1040 player_drop_count == getPlayerInventorySize(0))
1042 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1044 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1045 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1047 player_is_dropping = FALSE;
1051 if (new_motion_key_x != motion_key_x)
1053 Error(ERR_DEBUG, "---------- %s %s ----------",
1054 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1055 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1057 if (motion_key_x != KSYM_UNDEFINED)
1058 HandleKey(motion_key_x, KEY_RELEASED);
1059 if (new_motion_key_x != KSYM_UNDEFINED)
1060 HandleKey(new_motion_key_x, KEY_PRESSED);
1063 if (new_motion_key_y != motion_key_y)
1065 Error(ERR_DEBUG, "---------- %s %s ----------",
1066 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1067 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1069 if (motion_key_y != KSYM_UNDEFINED)
1070 HandleKey(motion_key_y, KEY_RELEASED);
1071 if (new_motion_key_y != KSYM_UNDEFINED)
1072 HandleKey(new_motion_key_y, KEY_PRESSED);
1075 motion_key_x = new_motion_key_x;
1076 motion_key_y = new_motion_key_y;
1080 static boolean checkTextInputKeyModState()
1082 // when playing, only handle raw key events and ignore text input
1083 if (game_status == GAME_MODE_PLAYING)
1086 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1089 void HandleTextEvent(TextEvent *event)
1091 char *text = event->text;
1092 Key key = getKeyFromKeyName(text);
1094 #if DEBUG_EVENTS_TEXT
1095 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1098 text[0], (int)(text[0]),
1100 getKeyNameFromKey(key),
1104 #if !defined(HAS_SCREEN_KEYBOARD)
1105 // non-mobile devices: only handle key input with modifier keys pressed here
1106 // (every other key input is handled directly as physical key input event)
1107 if (!checkTextInputKeyModState())
1111 // process text input as "classic" (with uppercase etc.) key input event
1112 HandleKey(key, KEY_PRESSED);
1113 HandleKey(key, KEY_RELEASED);
1116 void HandlePauseResumeEvent(PauseResumeEvent *event)
1118 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1122 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1130 void HandleKeyEvent(KeyEvent *event)
1132 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1133 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1134 Key key = GetEventKey(event, with_modifiers);
1135 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1137 #if DEBUG_EVENTS_KEY
1138 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1139 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1140 event->keysym.scancode,
1145 getKeyNameFromKey(key));
1148 #if defined(PLATFORM_ANDROID)
1149 // always map the "back" button to the "escape" key on Android devices
1150 if (key == KSYM_Back)
1154 HandleKeyModState(keymod, key_status);
1156 #if defined(TARGET_SDL2)
1157 // only handle raw key input without text modifier keys pressed
1158 if (!checkTextInputKeyModState())
1159 HandleKey(key, key_status);
1161 HandleKey(key, key_status);
1165 void HandleFocusEvent(FocusChangeEvent *event)
1167 static int old_joystick_status = -1;
1169 if (event->type == EVENT_FOCUSOUT)
1171 KeyboardAutoRepeatOn();
1172 old_joystick_status = joystick.status;
1173 joystick.status = JOYSTICK_NOT_AVAILABLE;
1175 ClearPlayerAction();
1177 else if (event->type == EVENT_FOCUSIN)
1179 /* When there are two Rocks'n'Diamonds windows which overlap and
1180 the player moves the pointer from one game window to the other,
1181 a 'FocusOut' event is generated for the window the pointer is
1182 leaving and a 'FocusIn' event is generated for the window the
1183 pointer is entering. In some cases, it can happen that the
1184 'FocusIn' event is handled by the one game process before the
1185 'FocusOut' event by the other game process. In this case the
1186 X11 environment would end up with activated keyboard auto repeat,
1187 because unfortunately this is a global setting and not (which
1188 would be far better) set for each X11 window individually.
1189 The effect would be keyboard auto repeat while playing the game
1190 (game_status == GAME_MODE_PLAYING), which is not desired.
1191 To avoid this special case, we just wait 1/10 second before
1192 processing the 'FocusIn' event.
1195 if (game_status == GAME_MODE_PLAYING)
1198 KeyboardAutoRepeatOffUnlessAutoplay();
1201 if (old_joystick_status != -1)
1202 joystick.status = old_joystick_status;
1206 void HandleClientMessageEvent(ClientMessageEvent *event)
1208 if (CheckCloseWindowEvent(event))
1212 void HandleWindowManagerEvent(Event *event)
1214 #if defined(TARGET_SDL)
1215 SDLHandleWindowManagerEvent(event);
1219 void HandleButton(int mx, int my, int button, int button_nr)
1221 static int old_mx = 0, old_my = 0;
1222 boolean button_hold = FALSE;
1228 button_nr = -button_nr;
1237 #if defined(PLATFORM_ANDROID)
1238 // when playing, only handle gadgets when using "follow finger" controls
1239 boolean handle_gadgets =
1240 (game_status != GAME_MODE_PLAYING ||
1241 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1243 if (handle_gadgets &&
1244 HandleGadgets(mx, my, button))
1246 /* do not handle this button event anymore */
1247 mx = my = -32; /* force mouse event to be outside screen tiles */
1250 if (HandleGadgets(mx, my, button))
1252 /* do not handle this button event anymore */
1253 mx = my = -32; /* force mouse event to be outside screen tiles */
1257 if (HandleGlobalAnimClicks(mx, my, button))
1259 /* do not handle this button event anymore */
1260 mx = my = -32; /* force mouse event to be outside screen tiles */
1263 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1266 /* do not use scroll wheel button events for anything other than gadgets */
1267 if (IS_WHEEL_BUTTON(button_nr))
1270 switch (game_status)
1272 case GAME_MODE_TITLE:
1273 HandleTitleScreen(mx, my, 0, 0, button);
1276 case GAME_MODE_MAIN:
1277 HandleMainMenu(mx, my, 0, 0, button);
1280 case GAME_MODE_PSEUDO_TYPENAME:
1281 HandleTypeName(0, KSYM_Return);
1284 case GAME_MODE_LEVELS:
1285 HandleChooseLevelSet(mx, my, 0, 0, button);
1288 case GAME_MODE_LEVELNR:
1289 HandleChooseLevelNr(mx, my, 0, 0, button);
1292 case GAME_MODE_SCORES:
1293 HandleHallOfFame(0, 0, 0, 0, button);
1296 case GAME_MODE_EDITOR:
1297 HandleLevelEditorIdle();
1300 case GAME_MODE_INFO:
1301 HandleInfoScreen(mx, my, 0, 0, button);
1304 case GAME_MODE_SETUP:
1305 HandleSetupScreen(mx, my, 0, 0, button);
1308 #if defined(TARGET_SDL2)
1309 case GAME_MODE_PLAYING:
1310 HandleFollowFinger(mx, my, button);
1314 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1315 GetKeyModState() & KMOD_Control)
1316 DumpTileFromScreen(mx, my);
1326 static boolean is_string_suffix(char *string, char *suffix)
1328 int string_len = strlen(string);
1329 int suffix_len = strlen(suffix);
1331 if (suffix_len > string_len)
1334 return (strEqual(&string[string_len - suffix_len], suffix));
1337 #define MAX_CHEAT_INPUT_LEN 32
1339 static void HandleKeysSpecial(Key key)
1341 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1342 char letter = getCharFromKey(key);
1343 int cheat_input_len = strlen(cheat_input);
1349 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1351 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1352 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1354 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1357 cheat_input[cheat_input_len++] = letter;
1358 cheat_input[cheat_input_len] = '\0';
1360 #if DEBUG_EVENTS_KEY
1361 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1364 if (game_status == GAME_MODE_MAIN)
1366 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1367 is_string_suffix(cheat_input, ":ist"))
1369 InsertSolutionTape();
1371 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1372 is_string_suffix(cheat_input, ":rg"))
1374 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1377 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1378 is_string_suffix(cheat_input, ":rs"))
1380 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1383 else if (is_string_suffix(cheat_input, ":reload-music") ||
1384 is_string_suffix(cheat_input, ":rm"))
1386 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1389 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1390 is_string_suffix(cheat_input, ":ra"))
1392 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1393 1 << ARTWORK_TYPE_SOUNDS |
1394 1 << ARTWORK_TYPE_MUSIC);
1397 else if (is_string_suffix(cheat_input, ":dump-level") ||
1398 is_string_suffix(cheat_input, ":dl"))
1402 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1403 is_string_suffix(cheat_input, ":dt"))
1407 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1408 is_string_suffix(cheat_input, ":ft"))
1410 /* fix single-player tapes that contain player input for more than one
1411 player (due to a bug in 3.3.1.2 and earlier versions), which results
1412 in playing levels with more than one player in multi-player mode,
1413 even though the tape was originally recorded in single-player mode */
1415 /* remove player input actions for all players but the first one */
1416 for (i = 1; i < MAX_PLAYERS; i++)
1417 tape.player_participates[i] = FALSE;
1419 tape.changed = TRUE;
1421 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1422 is_string_suffix(cheat_input, ":snl"))
1424 SaveNativeLevel(&level);
1427 else if (game_status == GAME_MODE_PLAYING)
1430 if (is_string_suffix(cheat_input, ".q"))
1431 DEBUG_SetMaximumDynamite();
1434 else if (game_status == GAME_MODE_EDITOR)
1436 if (is_string_suffix(cheat_input, ":dump-brush") ||
1437 is_string_suffix(cheat_input, ":DB"))
1441 else if (is_string_suffix(cheat_input, ":DDB"))
1448 void HandleKeysDebug(Key key)
1453 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1455 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1457 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1459 if (key == setup.debug.frame_delay_key[i] &&
1460 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1462 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1463 setup.debug.frame_delay[i] : setup.game_frame_delay);
1465 if (!setup.debug.frame_delay_game_only)
1466 MenuFrameDelay = GameFrameDelay;
1468 SetVideoFrameDelay(GameFrameDelay);
1470 if (GameFrameDelay > ONE_SECOND_DELAY)
1471 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1472 else if (GameFrameDelay != 0)
1473 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1474 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1475 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1477 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1484 if (game_status == GAME_MODE_PLAYING)
1488 options.debug = !options.debug;
1490 Error(ERR_DEBUG, "debug mode %s",
1491 (options.debug ? "enabled" : "disabled"));
1493 else if (key == KSYM_v)
1495 Error(ERR_DEBUG, "currently using game engine version %d",
1496 game.engine_version);
1502 void HandleKey(Key key, int key_status)
1504 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1505 static boolean ignore_repeated_key = FALSE;
1506 static struct SetupKeyboardInfo ski;
1507 static struct SetupShortcutInfo ssi;
1516 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1517 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1518 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1519 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1520 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1521 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1526 if (game_status == GAME_MODE_PLAYING)
1528 /* only needed for single-step tape recording mode */
1529 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1530 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1531 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1532 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1535 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1537 byte key_action = 0;
1539 if (setup.input[pnr].use_joystick)
1542 ski = setup.input[pnr].key;
1544 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1545 if (key == *key_info[i].key_custom)
1546 key_action |= key_info[i].action;
1548 /* use combined snap+direction keys for the first player only */
1551 ssi = setup.shortcut;
1553 for (i = 0; i < NUM_DIRECTIONS; i++)
1554 if (key == *key_info[i].key_snap)
1555 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1558 /* clear delayed snap and drop actions in single step mode (see below) */
1559 if (tape.single_step)
1561 if (clear_snap_button[pnr])
1563 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1564 clear_snap_button[pnr] = FALSE;
1567 if (clear_drop_button[pnr])
1569 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1570 clear_drop_button[pnr] = FALSE;
1574 if (key_status == KEY_PRESSED)
1575 stored_player[pnr].action |= key_action;
1577 stored_player[pnr].action &= ~key_action;
1579 if (tape.single_step && tape.recording && tape.pausing)
1581 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1583 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1585 /* if snap key already pressed, don't snap when releasing (below) */
1586 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1587 element_snapped[pnr] = TRUE;
1589 /* if drop key already pressed, don't drop when releasing (below) */
1590 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1591 element_dropped[pnr] = TRUE;
1593 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1595 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1596 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1599 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1600 getRedDiskReleaseFlag_SP() == 0)
1601 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1603 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1606 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1608 if (key_action & KEY_BUTTON_SNAP)
1610 /* if snap key was released without moving (see above), snap now */
1611 if (!element_snapped[pnr])
1613 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1615 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1617 /* clear delayed snap button on next event */
1618 clear_snap_button[pnr] = TRUE;
1621 element_snapped[pnr] = FALSE;
1624 if (key_action & KEY_BUTTON_DROP &&
1625 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1627 /* if drop key was released without moving (see above), drop now */
1628 if (!element_dropped[pnr])
1630 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1632 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1633 getRedDiskReleaseFlag_SP() != 0)
1634 stored_player[pnr].action |= KEY_BUTTON_DROP;
1636 /* clear delayed drop button on next event */
1637 clear_drop_button[pnr] = TRUE;
1640 element_dropped[pnr] = FALSE;
1644 else if (tape.recording && tape.pausing)
1646 /* prevent key release events from un-pausing a paused game */
1647 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1648 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1654 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1655 if (key == key_info[i].key_default)
1656 joy |= key_info[i].action;
1661 if (key_status == KEY_PRESSED)
1662 key_joystick_mapping |= joy;
1664 key_joystick_mapping &= ~joy;
1669 if (game_status != GAME_MODE_PLAYING)
1670 key_joystick_mapping = 0;
1672 if (key_status == KEY_RELEASED)
1674 // reset flag to ignore repeated "key pressed" events after key release
1675 ignore_repeated_key = FALSE;
1680 if ((key == KSYM_F11 ||
1681 ((key == KSYM_Return ||
1682 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1683 video.fullscreen_available &&
1684 !ignore_repeated_key)
1686 setup.fullscreen = !setup.fullscreen;
1688 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1690 if (game_status == GAME_MODE_SETUP)
1691 RedrawSetupScreenAfterFullscreenToggle();
1693 // set flag to ignore repeated "key pressed" events
1694 ignore_repeated_key = TRUE;
1699 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1700 key == KSYM_minus || key == KSYM_KP_Subtract ||
1701 key == KSYM_plus || key == KSYM_KP_Add ||
1702 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1703 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1704 video.window_scaling_available &&
1705 !video.fullscreen_enabled)
1707 if (key == KSYM_0 || key == KSYM_KP_0)
1708 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1709 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1710 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1712 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1714 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1715 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1716 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1717 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1719 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1721 if (game_status == GAME_MODE_SETUP)
1722 RedrawSetupScreenAfterFullscreenToggle();
1727 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1728 key == KSYM_Return ||
1729 key == KSYM_Escape)))
1731 /* do not handle this key event anymore */
1732 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1736 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1737 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1744 if (game_status == GAME_MODE_MAIN &&
1745 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1747 StartGameActions(options.network, setup.autorecord, level.random_seed);
1752 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1754 if (key == setup.shortcut.save_game)
1756 else if (key == setup.shortcut.load_game)
1758 else if (key == setup.shortcut.toggle_pause)
1759 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1761 HandleTapeButtonKeys(key);
1762 HandleSoundButtonKeys(key);
1765 if (game_status == GAME_MODE_PLAYING && !network_playing)
1767 int centered_player_nr_next = -999;
1769 if (key == setup.shortcut.focus_player_all)
1770 centered_player_nr_next = -1;
1772 for (i = 0; i < MAX_PLAYERS; i++)
1773 if (key == setup.shortcut.focus_player[i])
1774 centered_player_nr_next = i;
1776 if (centered_player_nr_next != -999)
1778 game.centered_player_nr_next = centered_player_nr_next;
1779 game.set_centered_player = TRUE;
1783 tape.centered_player_nr_next = game.centered_player_nr_next;
1784 tape.set_centered_player = TRUE;
1789 HandleKeysSpecial(key);
1791 if (HandleGadgetsKeyInput(key))
1793 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1794 key = KSYM_UNDEFINED;
1797 switch (game_status)
1799 case GAME_MODE_PSEUDO_TYPENAME:
1800 HandleTypeName(0, key);
1803 case GAME_MODE_TITLE:
1804 case GAME_MODE_MAIN:
1805 case GAME_MODE_LEVELS:
1806 case GAME_MODE_LEVELNR:
1807 case GAME_MODE_SETUP:
1808 case GAME_MODE_INFO:
1809 case GAME_MODE_SCORES:
1814 if (game_status == GAME_MODE_TITLE)
1815 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1816 else if (game_status == GAME_MODE_MAIN)
1817 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1818 else if (game_status == GAME_MODE_LEVELS)
1819 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1820 else if (game_status == GAME_MODE_LEVELNR)
1821 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1822 else if (game_status == GAME_MODE_SETUP)
1823 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1824 else if (game_status == GAME_MODE_INFO)
1825 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1826 else if (game_status == GAME_MODE_SCORES)
1827 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1831 if (game_status != GAME_MODE_MAIN)
1832 FadeSkipNextFadeIn();
1834 if (game_status == GAME_MODE_TITLE)
1835 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1836 else if (game_status == GAME_MODE_LEVELS)
1837 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1838 else if (game_status == GAME_MODE_LEVELNR)
1839 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1840 else if (game_status == GAME_MODE_SETUP)
1841 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1842 else if (game_status == GAME_MODE_INFO)
1843 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1844 else if (game_status == GAME_MODE_SCORES)
1845 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1849 if (game_status == GAME_MODE_LEVELS)
1850 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1851 else if (game_status == GAME_MODE_LEVELNR)
1852 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1853 else if (game_status == GAME_MODE_SETUP)
1854 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1855 else if (game_status == GAME_MODE_INFO)
1856 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1857 else if (game_status == GAME_MODE_SCORES)
1858 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1861 case KSYM_Page_Down:
1862 if (game_status == GAME_MODE_LEVELS)
1863 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1864 else if (game_status == GAME_MODE_LEVELNR)
1865 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1866 else if (game_status == GAME_MODE_SETUP)
1867 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1868 else if (game_status == GAME_MODE_INFO)
1869 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1870 else if (game_status == GAME_MODE_SCORES)
1871 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1879 case GAME_MODE_EDITOR:
1880 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1881 HandleLevelEditorKeyInput(key);
1884 case GAME_MODE_PLAYING:
1889 RequestQuitGame(setup.ask_on_escape);
1899 if (key == KSYM_Escape)
1901 SetGameStatus(GAME_MODE_MAIN);
1909 HandleKeysDebug(key);
1912 void HandleNoEvent()
1914 // if (button_status && game_status != GAME_MODE_PLAYING)
1915 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1917 HandleButton(0, 0, button_status, -button_status);
1924 #if defined(NETWORK_AVALIABLE)
1925 if (options.network)
1929 switch (game_status)
1931 case GAME_MODE_MAIN:
1932 DrawPreviewLevelAnimation();
1935 case GAME_MODE_EDITOR:
1936 HandleLevelEditorIdle();
1939 #if defined(TARGET_SDL2)
1940 case GAME_MODE_PLAYING:
1941 HandleFollowFinger(-1, -1, -1);
1950 static int HandleJoystickForAllPlayers()
1954 boolean no_joysticks_configured = TRUE;
1955 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1956 static byte joy_action_last[MAX_PLAYERS];
1958 for (i = 0; i < MAX_PLAYERS; i++)
1959 if (setup.input[i].use_joystick)
1960 no_joysticks_configured = FALSE;
1962 /* if no joysticks configured, map connected joysticks to players */
1963 if (no_joysticks_configured)
1964 use_as_joystick_nr = TRUE;
1966 for (i = 0; i < MAX_PLAYERS; i++)
1968 byte joy_action = 0;
1970 joy_action = JoystickExt(i, use_as_joystick_nr);
1971 result |= joy_action;
1973 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1974 joy_action != joy_action_last[i])
1975 stored_player[i].action = joy_action;
1977 joy_action_last[i] = joy_action;
1983 void HandleJoystick()
1985 int joystick = HandleJoystickForAllPlayers();
1986 int keyboard = key_joystick_mapping;
1987 int joy = (joystick | keyboard);
1988 int left = joy & JOY_LEFT;
1989 int right = joy & JOY_RIGHT;
1990 int up = joy & JOY_UP;
1991 int down = joy & JOY_DOWN;
1992 int button = joy & JOY_BUTTON;
1993 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1994 int dx = (left ? -1 : right ? 1 : 0);
1995 int dy = (up ? -1 : down ? 1 : 0);
1997 if (HandleGlobalAnimClicks(-1, -1, newbutton))
1999 /* do not handle this button event anymore */
2003 switch (game_status)
2005 case GAME_MODE_TITLE:
2006 case GAME_MODE_MAIN:
2007 case GAME_MODE_LEVELS:
2008 case GAME_MODE_LEVELNR:
2009 case GAME_MODE_SETUP:
2010 case GAME_MODE_INFO:
2012 static unsigned int joystickmove_delay = 0;
2013 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
2014 static int joystick_last = 0;
2016 if (joystick && !button &&
2017 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2019 /* delay joystick actions if buttons/axes continually pressed */
2020 newbutton = dx = dy = 0;
2024 /* start with longer delay, then continue with shorter delay */
2025 if (joystick != joystick_last)
2026 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2028 joystickmove_delay_value = GADGET_FRAME_DELAY;
2031 if (game_status == GAME_MODE_TITLE)
2032 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2033 else if (game_status == GAME_MODE_MAIN)
2034 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2035 else if (game_status == GAME_MODE_LEVELS)
2036 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2037 else if (game_status == GAME_MODE_LEVELNR)
2038 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2039 else if (game_status == GAME_MODE_SETUP)
2040 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2041 else if (game_status == GAME_MODE_INFO)
2042 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2044 joystick_last = joystick;
2049 case GAME_MODE_SCORES:
2050 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2053 case GAME_MODE_PLAYING:
2054 if (tape.playing || keyboard)
2055 newbutton = ((joy & JOY_BUTTON) != 0);
2057 if (newbutton && AllPlayersGone)
2064 if (tape.recording && tape.pausing)
2066 if (joystick & JOY_ACTION)
2067 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2077 void HandleSpecialGameControllerButtons(Event *event)
2079 #if defined(TARGET_SDL2)
2080 switch (event->type)
2082 case SDL_CONTROLLERBUTTONDOWN:
2083 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2084 HandleKey(KSYM_space, KEY_PRESSED);
2085 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2086 HandleKey(KSYM_Escape, KEY_PRESSED);
2090 case SDL_CONTROLLERBUTTONUP:
2091 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2092 HandleKey(KSYM_space, KEY_RELEASED);
2093 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2094 HandleKey(KSYM_Escape, KEY_RELEASED);