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 /* non-motion events are directly passed to event handler functions */
62 if (event->type != EVENT_MOTIONNOTIFY)
65 motion = (MotionEvent *)event;
66 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
67 motion->y >= SY && motion->y < SY + SYSIZE);
69 /* do no reset mouse cursor before all pending events have been processed */
70 if (gfx.cursor_mode == cursor_mode_last &&
71 ((game_status == GAME_MODE_TITLE &&
72 gfx.cursor_mode == CURSOR_NONE) ||
73 (game_status == GAME_MODE_PLAYING &&
74 gfx.cursor_mode == CURSOR_PLAYFIELD)))
76 SetMouseCursor(CURSOR_DEFAULT);
78 DelayReached(&special_cursor_delay, 0);
80 cursor_mode_last = CURSOR_DEFAULT;
83 /* skip mouse motion events without pressed button outside level editor */
84 if (button_status == MB_RELEASED &&
85 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
91 /* to prevent delay problems, skip mouse motion events if the very next
92 event is also a mouse motion event (and therefore effectively only
93 handling the last of a row of mouse motion events in the event queue) */
95 static boolean SkipPressedMouseMotionEvent(const Event *event)
97 /* nothing to do if the current event is not a mouse motion event */
98 if (event->type != EVENT_MOTIONNOTIFY)
101 /* only skip motion events with pressed button outside the game */
102 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
109 PeekEvent(&next_event);
111 /* if next event is also a mouse motion event, skip the current one */
112 if (next_event.type == EVENT_MOTIONNOTIFY)
119 /* this is especially needed for event modifications for the Android target:
120 if mouse coordinates should be modified in the event filter function,
121 using a properly installed SDL event filter does not work, because in
122 the event filter, mouse coordinates in the event structure are still
123 physical pixel positions, not logical (scaled) screen positions, so this
124 has to be handled at a later stage in the event processing functions
125 (when device pixel positions are already converted to screen positions) */
127 boolean NextValidEvent(Event *event)
129 while (PendingEvent())
131 boolean handle_this_event = FALSE;
135 if (FilterEvents(event))
136 handle_this_event = TRUE;
138 if (SkipPressedMouseMotionEvent(event))
139 handle_this_event = FALSE;
141 if (handle_this_event)
151 unsigned int event_frame_delay = 0;
152 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
154 ResetDelayCounter(&event_frame_delay);
156 while (NextValidEvent(&event))
160 case EVENT_BUTTONPRESS:
161 case EVENT_BUTTONRELEASE:
162 HandleButtonEvent((ButtonEvent *) &event);
165 case EVENT_MOTIONNOTIFY:
166 HandleMotionEvent((MotionEvent *) &event);
169 #if defined(TARGET_SDL2)
170 case EVENT_WHEELMOTION:
171 HandleWheelEvent((WheelEvent *) &event);
174 case SDL_WINDOWEVENT:
175 HandleWindowEvent((WindowEvent *) &event);
178 case EVENT_FINGERPRESS:
179 case EVENT_FINGERRELEASE:
180 case EVENT_FINGERMOTION:
181 HandleFingerEvent((FingerEvent *) &event);
184 case EVENT_TEXTINPUT:
185 HandleTextEvent((TextEvent *) &event);
188 case SDL_APP_WILLENTERBACKGROUND:
189 case SDL_APP_DIDENTERBACKGROUND:
190 case SDL_APP_WILLENTERFOREGROUND:
191 case SDL_APP_DIDENTERFOREGROUND:
192 HandlePauseResumeEvent((PauseResumeEvent *) &event);
197 case EVENT_KEYRELEASE:
198 HandleKeyEvent((KeyEvent *) &event);
202 HandleOtherEvents(&event);
206 // do not handle events for longer than standard frame delay period
207 if (DelayReached(&event_frame_delay, event_frame_delay_value))
212 void HandleOtherEvents(Event *event)
217 HandleExposeEvent((ExposeEvent *) event);
220 case EVENT_UNMAPNOTIFY:
222 /* This causes the game to stop not only when iconified, but also
223 when on another virtual desktop, which might be not desired. */
224 SleepWhileUnmapped();
230 HandleFocusEvent((FocusChangeEvent *) event);
233 case EVENT_CLIENTMESSAGE:
234 HandleClientMessageEvent((ClientMessageEvent *) event);
237 #if defined(TARGET_SDL)
238 case SDL_JOYAXISMOTION:
239 case SDL_JOYBUTTONDOWN:
240 case SDL_JOYBUTTONUP:
241 HandleJoystickEvent(event);
245 HandleWindowManagerEvent(event);
254 void HandleMouseCursor()
256 if (game_status == GAME_MODE_TITLE)
258 /* when showing title screens, hide mouse pointer (if not moved) */
260 if (gfx.cursor_mode != CURSOR_NONE &&
261 DelayReached(&special_cursor_delay, special_cursor_delay_value))
263 SetMouseCursor(CURSOR_NONE);
266 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
269 /* when playing, display a special mouse pointer inside the playfield */
271 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
272 cursor_inside_playfield &&
273 DelayReached(&special_cursor_delay, special_cursor_delay_value))
275 SetMouseCursor(CURSOR_PLAYFIELD);
278 else if (gfx.cursor_mode != CURSOR_DEFAULT)
280 SetMouseCursor(CURSOR_DEFAULT);
283 /* this is set after all pending events have been processed */
284 cursor_mode_last = gfx.cursor_mode;
296 /* also execute after pending events have been processed before */
299 /* don't use all CPU time when idle; the main loop while playing
300 has its own synchronization and is CPU friendly, too */
302 if (game_status == GAME_MODE_PLAYING)
305 /* always copy backbuffer to visible screen for every video frame */
308 /* reset video frame delay to default (may change again while playing) */
309 SetVideoFrameDelay(MenuFrameDelay);
311 if (game_status == GAME_MODE_QUIT)
316 void ClearEventQueue()
318 while (PendingEvent())
326 case EVENT_BUTTONRELEASE:
327 button_status = MB_RELEASED;
330 case EVENT_KEYRELEASE:
335 HandleOtherEvents(&event);
341 void ClearPlayerAction()
345 /* simulate key release events for still pressed keys */
346 key_joystick_mapping = 0;
347 for (i = 0; i < MAX_PLAYERS; i++)
348 stored_player[i].action = 0;
351 void SleepWhileUnmapped()
353 boolean window_unmapped = TRUE;
355 KeyboardAutoRepeatOn();
357 while (window_unmapped)
365 case EVENT_BUTTONRELEASE:
366 button_status = MB_RELEASED;
369 case EVENT_KEYRELEASE:
370 key_joystick_mapping = 0;
373 case EVENT_MAPNOTIFY:
374 window_unmapped = FALSE;
377 case EVENT_UNMAPNOTIFY:
378 /* this is only to surely prevent the 'should not happen' case
379 * of recursively looping between 'SleepWhileUnmapped()' and
380 * 'HandleOtherEvents()' which usually calls this funtion.
385 HandleOtherEvents(&event);
390 if (game_status == GAME_MODE_PLAYING)
391 KeyboardAutoRepeatOffUnlessAutoplay();
394 void HandleExposeEvent(ExposeEvent *event)
398 void HandleButtonEvent(ButtonEvent *event)
400 #if DEBUG_EVENTS_BUTTON
401 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
403 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
407 #if defined(HAS_SCREEN_KEYBOARD)
408 if (video.shifted_up)
409 event->y += video.shifted_up_pos;
412 motion_status = FALSE;
414 if (event->type == EVENT_BUTTONPRESS)
415 button_status = event->button;
417 button_status = MB_RELEASED;
419 HandleButton(event->x, event->y, button_status, event->button);
422 void HandleMotionEvent(MotionEvent *event)
424 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
427 motion_status = TRUE;
429 #if DEBUG_EVENTS_MOTION
430 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
431 button_status, event->x, event->y);
434 HandleButton(event->x, event->y, button_status, button_status);
437 #if defined(TARGET_SDL2)
439 void HandleWheelEvent(WheelEvent *event)
443 #if DEBUG_EVENTS_WHEEL
445 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
446 event->which, event->x, event->y);
448 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
449 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
450 event->which, event->x, event->y,
451 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
452 "SDL_MOUSEWHEEL_FLIPPED"));
456 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
457 event->x > 0 ? MB_WHEEL_RIGHT :
458 event->y < 0 ? MB_WHEEL_DOWN :
459 event->y > 0 ? MB_WHEEL_UP : 0);
461 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
462 // accelerated mouse wheel available on Mac and Windows
463 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
465 // no accelerated mouse wheel available on Unix/Linux
466 wheel_steps = DEFAULT_WHEEL_STEPS;
469 motion_status = FALSE;
471 button_status = button_nr;
472 HandleButton(0, 0, button_status, -button_nr);
474 button_status = MB_RELEASED;
475 HandleButton(0, 0, button_status, -button_nr);
478 void HandleWindowEvent(WindowEvent *event)
480 #if DEBUG_EVENTS_WINDOW
481 int subtype = event->event;
484 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
485 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
486 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
487 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
488 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
489 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
490 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
491 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
492 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
493 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
494 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
495 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
496 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
497 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
500 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
501 event_name, event->data1, event->data2);
505 // (not needed, as the screen gets redrawn every 20 ms anyway)
506 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
507 event->event == SDL_WINDOWEVENT_RESIZED ||
508 event->event == SDL_WINDOWEVENT_EXPOSED)
512 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
514 int new_window_width = event->data1;
515 int new_window_height = event->data2;
517 // if window size has changed after resizing, calculate new scaling factor
518 if (new_window_width != video.window_width ||
519 new_window_height != video.window_height)
521 int new_xpercent = (100 * new_window_width / video.width);
522 int new_ypercent = (100 * new_window_height / video.height);
524 // (extreme window scaling allowed, but cannot be saved permanently)
525 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
526 setup.window_scaling_percent =
527 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
528 MAX_WINDOW_SCALING_PERCENT);
530 video.window_width = new_window_width;
531 video.window_height = new_window_height;
533 if (game_status == GAME_MODE_SETUP)
534 RedrawSetupScreenAfterFullscreenToggle();
541 #define NUM_TOUCH_FINGERS 3
546 SDL_FingerID finger_id;
549 } touch_info[NUM_TOUCH_FINGERS];
551 void HandleFingerEvent(FingerEvent *event)
553 static Key motion_key_x = KSYM_UNDEFINED;
554 static Key motion_key_y = KSYM_UNDEFINED;
555 static Key button_key = KSYM_UNDEFINED;
556 static float motion_x1, motion_y1;
557 static float button_x1, button_y1;
558 static SDL_FingerID motion_id = -1;
559 static SDL_FingerID button_id = -1;
560 int move_trigger_distance_percent = setup.touch.move_distance;
561 int drop_trigger_distance_percent = setup.touch.drop_distance;
562 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
563 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
564 float event_x = event->x;
565 float event_y = event->y;
567 #if DEBUG_EVENTS_FINGER
568 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
569 event->type == EVENT_FINGERPRESS ? "pressed" :
570 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
574 event->dx, event->dy,
578 if (game_status != GAME_MODE_PLAYING)
581 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
584 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
586 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
588 Key key = (event->x < 1.0 / 3.0 ?
589 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
590 setup.input[0].key.drop) :
591 event->x > 2.0 / 3.0 ?
592 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
593 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
594 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
595 setup.input[0].key.right) :
597 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
601 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
602 getKeyNameFromKey(key), key_status_name, event->fingerId);
604 // check if we already know this touch event's finger id
605 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
607 if (touch_info[i].touched &&
608 touch_info[i].finger_id == event->fingerId)
610 // Error(ERR_DEBUG, "MARK 1: %d", i);
616 if (i >= NUM_TOUCH_FINGERS)
618 if (key_status == KEY_PRESSED)
620 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
622 // unknown finger id -- get new, empty slot, if available
623 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
625 if (touch_info[i].counter < oldest_counter)
628 oldest_counter = touch_info[i].counter;
630 // Error(ERR_DEBUG, "MARK 2: %d", i);
633 if (!touch_info[i].touched)
635 // Error(ERR_DEBUG, "MARK 3: %d", i);
641 if (i >= NUM_TOUCH_FINGERS)
643 // all slots allocated -- use oldest slot
646 // Error(ERR_DEBUG, "MARK 4: %d", i);
651 // release of previously unknown key (should not happen)
653 if (key != KSYM_UNDEFINED)
655 HandleKey(key, KEY_RELEASED);
657 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
658 getKeyNameFromKey(key), "KEY_RELEASED", i);
663 if (i < NUM_TOUCH_FINGERS)
665 if (key_status == KEY_PRESSED)
667 if (touch_info[i].key != key)
669 if (touch_info[i].key != KSYM_UNDEFINED)
671 HandleKey(touch_info[i].key, KEY_RELEASED);
673 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
674 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
677 if (key != KSYM_UNDEFINED)
679 HandleKey(key, KEY_PRESSED);
681 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
682 getKeyNameFromKey(key), "KEY_PRESSED", i);
686 touch_info[i].touched = TRUE;
687 touch_info[i].finger_id = event->fingerId;
688 touch_info[i].counter = Counter();
689 touch_info[i].key = key;
693 if (touch_info[i].key != KSYM_UNDEFINED)
695 HandleKey(touch_info[i].key, KEY_RELEASED);
697 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
698 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
701 touch_info[i].touched = FALSE;
702 touch_info[i].finger_id = 0;
703 touch_info[i].counter = 0;
704 touch_info[i].key = 0;
711 // use touch direction control
713 if (event->type == EVENT_FINGERPRESS)
715 if (event_x > 1.0 / 3.0)
719 motion_id = event->fingerId;
724 motion_key_x = KSYM_UNDEFINED;
725 motion_key_y = KSYM_UNDEFINED;
727 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
733 button_id = event->fingerId;
738 button_key = setup.input[0].key.snap;
740 HandleKey(button_key, KEY_PRESSED);
742 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
745 else if (event->type == EVENT_FINGERRELEASE)
747 if (event->fingerId == motion_id)
751 if (motion_key_x != KSYM_UNDEFINED)
752 HandleKey(motion_key_x, KEY_RELEASED);
753 if (motion_key_y != KSYM_UNDEFINED)
754 HandleKey(motion_key_y, KEY_RELEASED);
756 motion_key_x = KSYM_UNDEFINED;
757 motion_key_y = KSYM_UNDEFINED;
759 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
761 else if (event->fingerId == button_id)
765 if (button_key != KSYM_UNDEFINED)
766 HandleKey(button_key, KEY_RELEASED);
768 button_key = KSYM_UNDEFINED;
770 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
773 else if (event->type == EVENT_FINGERMOTION)
775 if (event->fingerId == motion_id)
777 float distance_x = ABS(event_x - motion_x1);
778 float distance_y = ABS(event_y - motion_y1);
779 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
780 event_x > motion_x1 ? setup.input[0].key.right :
782 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
783 event_y > motion_y1 ? setup.input[0].key.down :
786 if (distance_x < move_trigger_distance / 2 ||
787 distance_x < distance_y)
788 new_motion_key_x = KSYM_UNDEFINED;
790 if (distance_y < move_trigger_distance / 2 ||
791 distance_y < distance_x)
792 new_motion_key_y = KSYM_UNDEFINED;
794 if (distance_x > move_trigger_distance ||
795 distance_y > move_trigger_distance)
797 if (new_motion_key_x != motion_key_x)
799 if (motion_key_x != KSYM_UNDEFINED)
800 HandleKey(motion_key_x, KEY_RELEASED);
801 if (new_motion_key_x != KSYM_UNDEFINED)
802 HandleKey(new_motion_key_x, KEY_PRESSED);
805 if (new_motion_key_y != motion_key_y)
807 if (motion_key_y != KSYM_UNDEFINED)
808 HandleKey(motion_key_y, KEY_RELEASED);
809 if (new_motion_key_y != KSYM_UNDEFINED)
810 HandleKey(new_motion_key_y, KEY_PRESSED);
816 motion_key_x = new_motion_key_x;
817 motion_key_y = new_motion_key_y;
819 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
822 else if (event->fingerId == button_id)
824 float distance_x = ABS(event_x - button_x1);
825 float distance_y = ABS(event_y - button_y1);
827 if (distance_x < drop_trigger_distance / 2 &&
828 distance_y > drop_trigger_distance)
830 if (button_key == setup.input[0].key.snap)
831 HandleKey(button_key, KEY_RELEASED);
836 button_key = setup.input[0].key.drop;
838 HandleKey(button_key, KEY_PRESSED);
840 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
846 static void HandleFollowFinger(int mx, int my, int button)
848 static int old_mx = 0, old_my = 0;
849 static Key motion_key_x = KSYM_UNDEFINED;
850 static Key motion_key_y = KSYM_UNDEFINED;
851 static boolean started_on_player = FALSE;
852 static boolean player_is_dropping = FALSE;
853 static int player_drop_count = 0;
854 static int last_player_x = -1;
855 static int last_player_y = -1;
857 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
860 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
862 touch_info[0].touched = TRUE;
863 touch_info[0].key = 0;
870 started_on_player = FALSE;
871 player_is_dropping = FALSE;
872 player_drop_count = 0;
876 motion_key_x = KSYM_UNDEFINED;
877 motion_key_y = KSYM_UNDEFINED;
879 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
882 else if (button == MB_RELEASED && touch_info[0].touched)
884 touch_info[0].touched = FALSE;
885 touch_info[0].key = 0;
890 if (motion_key_x != KSYM_UNDEFINED)
891 HandleKey(motion_key_x, KEY_RELEASED);
892 if (motion_key_y != KSYM_UNDEFINED)
893 HandleKey(motion_key_y, KEY_RELEASED);
895 if (started_on_player)
897 if (player_is_dropping)
899 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
901 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
905 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
907 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
911 motion_key_x = KSYM_UNDEFINED;
912 motion_key_y = KSYM_UNDEFINED;
914 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
917 if (touch_info[0].touched)
919 int src_x = local_player->jx;
920 int src_y = local_player->jy;
921 int dst_x = getLevelFromScreenX(old_mx);
922 int dst_y = getLevelFromScreenY(old_my);
923 int dx = dst_x - src_x;
924 int dy = dst_y - src_y;
925 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
926 dx > 0 ? setup.input[0].key.right :
928 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
929 dy > 0 ? setup.input[0].key.down :
932 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
933 (last_player_x != local_player->jx ||
934 last_player_y != local_player->jy))
936 // in case of asymmetric diagonal movement, use "preferred" direction
938 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
940 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
941 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
943 local_player->last_move_dir = last_move_dir;
945 // (required to prevent accidentally forcing direction for next movement)
946 last_player_x = local_player->jx;
947 last_player_y = local_player->jy;
950 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
952 started_on_player = TRUE;
953 player_drop_count = getPlayerInventorySize(0);
954 player_is_dropping = (player_drop_count > 0);
956 if (player_is_dropping)
958 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
960 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
964 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
966 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
969 else if (dx != 0 || dy != 0)
971 if (player_is_dropping &&
972 player_drop_count == getPlayerInventorySize(0))
974 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
976 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
977 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
979 player_is_dropping = FALSE;
983 if (new_motion_key_x != motion_key_x)
985 Error(ERR_DEBUG, "---------- %s %s ----------",
986 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
987 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
989 if (motion_key_x != KSYM_UNDEFINED)
990 HandleKey(motion_key_x, KEY_RELEASED);
991 if (new_motion_key_x != KSYM_UNDEFINED)
992 HandleKey(new_motion_key_x, KEY_PRESSED);
995 if (new_motion_key_y != motion_key_y)
997 Error(ERR_DEBUG, "---------- %s %s ----------",
998 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
999 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1001 if (motion_key_y != KSYM_UNDEFINED)
1002 HandleKey(motion_key_y, KEY_RELEASED);
1003 if (new_motion_key_y != KSYM_UNDEFINED)
1004 HandleKey(new_motion_key_y, KEY_PRESSED);
1007 motion_key_x = new_motion_key_x;
1008 motion_key_y = new_motion_key_y;
1012 static boolean checkTextInputKeyModState()
1014 // when playing, only handle raw key events and ignore text input
1015 if (game_status == GAME_MODE_PLAYING)
1018 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1021 void HandleTextEvent(TextEvent *event)
1023 char *text = event->text;
1024 Key key = getKeyFromKeyName(text);
1026 #if DEBUG_EVENTS_TEXT
1027 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1030 text[0], (int)(text[0]),
1032 getKeyNameFromKey(key),
1036 #if !defined(HAS_SCREEN_KEYBOARD)
1037 // non-mobile devices: only handle key input with modifier keys pressed here
1038 // (every other key input is handled directly as physical key input event)
1039 if (!checkTextInputKeyModState())
1043 // process text input as "classic" (with uppercase etc.) key input event
1044 HandleKey(key, KEY_PRESSED);
1045 HandleKey(key, KEY_RELEASED);
1048 void HandlePauseResumeEvent(PauseResumeEvent *event)
1050 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1054 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1062 void HandleKeyEvent(KeyEvent *event)
1064 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1065 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1066 Key key = GetEventKey(event, with_modifiers);
1067 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1069 #if DEBUG_EVENTS_KEY
1070 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1071 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1072 event->keysym.scancode,
1077 getKeyNameFromKey(key));
1080 #if defined(PLATFORM_ANDROID)
1081 // always map the "back" button to the "escape" key on Android devices
1082 if (key == KSYM_Back)
1086 HandleKeyModState(keymod, key_status);
1088 #if defined(TARGET_SDL2)
1089 // only handle raw key input without text modifier keys pressed
1090 if (!checkTextInputKeyModState())
1091 HandleKey(key, key_status);
1093 HandleKey(key, key_status);
1097 void HandleFocusEvent(FocusChangeEvent *event)
1099 static int old_joystick_status = -1;
1101 if (event->type == EVENT_FOCUSOUT)
1103 KeyboardAutoRepeatOn();
1104 old_joystick_status = joystick.status;
1105 joystick.status = JOYSTICK_NOT_AVAILABLE;
1107 ClearPlayerAction();
1109 else if (event->type == EVENT_FOCUSIN)
1111 /* When there are two Rocks'n'Diamonds windows which overlap and
1112 the player moves the pointer from one game window to the other,
1113 a 'FocusOut' event is generated for the window the pointer is
1114 leaving and a 'FocusIn' event is generated for the window the
1115 pointer is entering. In some cases, it can happen that the
1116 'FocusIn' event is handled by the one game process before the
1117 'FocusOut' event by the other game process. In this case the
1118 X11 environment would end up with activated keyboard auto repeat,
1119 because unfortunately this is a global setting and not (which
1120 would be far better) set for each X11 window individually.
1121 The effect would be keyboard auto repeat while playing the game
1122 (game_status == GAME_MODE_PLAYING), which is not desired.
1123 To avoid this special case, we just wait 1/10 second before
1124 processing the 'FocusIn' event.
1127 if (game_status == GAME_MODE_PLAYING)
1130 KeyboardAutoRepeatOffUnlessAutoplay();
1133 if (old_joystick_status != -1)
1134 joystick.status = old_joystick_status;
1138 void HandleClientMessageEvent(ClientMessageEvent *event)
1140 if (CheckCloseWindowEvent(event))
1144 void HandleWindowManagerEvent(Event *event)
1146 #if defined(TARGET_SDL)
1147 SDLHandleWindowManagerEvent(event);
1151 void HandleButton(int mx, int my, int button, int button_nr)
1153 static int old_mx = 0, old_my = 0;
1154 boolean button_hold = FALSE;
1160 button_nr = -button_nr;
1169 #if defined(PLATFORM_ANDROID)
1170 // when playing, only handle gadgets when using "follow finger" controls
1171 boolean handle_gadgets =
1172 (game_status != GAME_MODE_PLAYING ||
1173 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1175 if (handle_gadgets &&
1176 HandleGadgets(mx, my, button))
1178 /* do not handle this button event anymore */
1179 mx = my = -32; /* force mouse event to be outside screen tiles */
1182 if (HandleGadgets(mx, my, button))
1184 /* do not handle this button event anymore */
1185 mx = my = -32; /* force mouse event to be outside screen tiles */
1189 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1192 /* do not use scroll wheel button events for anything other than gadgets */
1193 if (IS_WHEEL_BUTTON(button_nr))
1196 switch (game_status)
1198 case GAME_MODE_TITLE:
1199 HandleTitleScreen(mx, my, 0, 0, button);
1202 case GAME_MODE_MAIN:
1203 HandleMainMenu(mx, my, 0, 0, button);
1206 case GAME_MODE_PSEUDO_TYPENAME:
1207 HandleTypeName(0, KSYM_Return);
1210 case GAME_MODE_LEVELS:
1211 HandleChooseLevelSet(mx, my, 0, 0, button);
1214 case GAME_MODE_LEVELNR:
1215 HandleChooseLevelNr(mx, my, 0, 0, button);
1218 case GAME_MODE_SCORES:
1219 HandleHallOfFame(0, 0, 0, 0, button);
1222 case GAME_MODE_EDITOR:
1223 HandleLevelEditorIdle();
1226 case GAME_MODE_INFO:
1227 HandleInfoScreen(mx, my, 0, 0, button);
1230 case GAME_MODE_SETUP:
1231 HandleSetupScreen(mx, my, 0, 0, button);
1234 #if defined(TARGET_SDL2)
1235 case GAME_MODE_PLAYING:
1236 HandleFollowFinger(mx, my, button);
1240 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1241 GetKeyModState() & KMOD_Control)
1242 DumpTileFromScreen(mx, my);
1252 static boolean is_string_suffix(char *string, char *suffix)
1254 int string_len = strlen(string);
1255 int suffix_len = strlen(suffix);
1257 if (suffix_len > string_len)
1260 return (strEqual(&string[string_len - suffix_len], suffix));
1263 #define MAX_CHEAT_INPUT_LEN 32
1265 static void HandleKeysSpecial(Key key)
1267 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1268 char letter = getCharFromKey(key);
1269 int cheat_input_len = strlen(cheat_input);
1275 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1277 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1278 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1280 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1283 cheat_input[cheat_input_len++] = letter;
1284 cheat_input[cheat_input_len] = '\0';
1286 #if DEBUG_EVENTS_KEY
1287 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1290 if (game_status == GAME_MODE_MAIN)
1292 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1293 is_string_suffix(cheat_input, ":ist"))
1295 InsertSolutionTape();
1297 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1298 is_string_suffix(cheat_input, ":rg"))
1300 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1303 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1304 is_string_suffix(cheat_input, ":rs"))
1306 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1309 else if (is_string_suffix(cheat_input, ":reload-music") ||
1310 is_string_suffix(cheat_input, ":rm"))
1312 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1315 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1316 is_string_suffix(cheat_input, ":ra"))
1318 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1319 1 << ARTWORK_TYPE_SOUNDS |
1320 1 << ARTWORK_TYPE_MUSIC);
1323 else if (is_string_suffix(cheat_input, ":dump-level") ||
1324 is_string_suffix(cheat_input, ":dl"))
1328 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1329 is_string_suffix(cheat_input, ":dt"))
1333 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1334 is_string_suffix(cheat_input, ":ft"))
1336 /* fix single-player tapes that contain player input for more than one
1337 player (due to a bug in 3.3.1.2 and earlier versions), which results
1338 in playing levels with more than one player in multi-player mode,
1339 even though the tape was originally recorded in single-player mode */
1341 /* remove player input actions for all players but the first one */
1342 for (i = 1; i < MAX_PLAYERS; i++)
1343 tape.player_participates[i] = FALSE;
1345 tape.changed = TRUE;
1347 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1348 is_string_suffix(cheat_input, ":snl"))
1350 SaveNativeLevel(&level);
1353 else if (game_status == GAME_MODE_PLAYING)
1356 if (is_string_suffix(cheat_input, ".q"))
1357 DEBUG_SetMaximumDynamite();
1360 else if (game_status == GAME_MODE_EDITOR)
1362 if (is_string_suffix(cheat_input, ":dump-brush") ||
1363 is_string_suffix(cheat_input, ":DB"))
1367 else if (is_string_suffix(cheat_input, ":DDB"))
1374 void HandleKeysDebug(Key key)
1379 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1381 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1383 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1385 if (key == setup.debug.frame_delay_key[i] &&
1386 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1388 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1389 setup.debug.frame_delay[i] : GAME_FRAME_DELAY);
1391 if (!setup.debug.frame_delay_game_only)
1392 MenuFrameDelay = GameFrameDelay;
1394 SetVideoFrameDelay(GameFrameDelay);
1396 if (GameFrameDelay > ONE_SECOND_DELAY)
1397 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1398 else if (GameFrameDelay != 0)
1399 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1400 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1401 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1403 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1410 if (game_status == GAME_MODE_PLAYING)
1414 options.debug = !options.debug;
1416 Error(ERR_DEBUG, "debug mode %s",
1417 (options.debug ? "enabled" : "disabled"));
1419 else if (key == KSYM_v)
1421 Error(ERR_DEBUG, "currently using game engine version %d",
1422 game.engine_version);
1428 void HandleKey(Key key, int key_status)
1430 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1431 static boolean ignore_repeated_key = FALSE;
1432 static struct SetupKeyboardInfo ski;
1433 static struct SetupShortcutInfo ssi;
1442 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1443 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1444 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1445 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1446 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1447 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1452 if (game_status == GAME_MODE_PLAYING)
1454 /* only needed for single-step tape recording mode */
1455 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1456 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1457 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1458 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1461 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1463 byte key_action = 0;
1465 if (setup.input[pnr].use_joystick)
1468 ski = setup.input[pnr].key;
1470 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1471 if (key == *key_info[i].key_custom)
1472 key_action |= key_info[i].action;
1474 /* use combined snap+direction keys for the first player only */
1477 ssi = setup.shortcut;
1479 for (i = 0; i < NUM_DIRECTIONS; i++)
1480 if (key == *key_info[i].key_snap)
1481 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1484 /* clear delayed snap and drop actions in single step mode (see below) */
1485 if (tape.single_step)
1487 if (clear_snap_button[pnr])
1489 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1490 clear_snap_button[pnr] = FALSE;
1493 if (clear_drop_button[pnr])
1495 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1496 clear_drop_button[pnr] = FALSE;
1500 if (key_status == KEY_PRESSED)
1501 stored_player[pnr].action |= key_action;
1503 stored_player[pnr].action &= ~key_action;
1505 if (tape.single_step && tape.recording && tape.pausing)
1507 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1509 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1511 /* if snap key already pressed, don't snap when releasing (below) */
1512 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1513 element_snapped[pnr] = TRUE;
1515 /* if drop key already pressed, don't drop when releasing (below) */
1516 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1517 element_dropped[pnr] = TRUE;
1519 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1521 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1522 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1525 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1526 getRedDiskReleaseFlag_SP() == 0)
1527 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1529 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1532 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1534 if (key_action & KEY_BUTTON_SNAP)
1536 /* if snap key was released without moving (see above), snap now */
1537 if (!element_snapped[pnr])
1539 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1541 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1543 /* clear delayed snap button on next event */
1544 clear_snap_button[pnr] = TRUE;
1547 element_snapped[pnr] = FALSE;
1550 if (key_action & KEY_BUTTON_DROP &&
1551 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1553 /* if drop key was released without moving (see above), drop now */
1554 if (!element_dropped[pnr])
1556 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1558 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1559 getRedDiskReleaseFlag_SP() != 0)
1560 stored_player[pnr].action |= KEY_BUTTON_DROP;
1562 /* clear delayed drop button on next event */
1563 clear_drop_button[pnr] = TRUE;
1566 element_dropped[pnr] = FALSE;
1570 else if (tape.recording && tape.pausing)
1572 /* prevent key release events from un-pausing a paused game */
1573 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1574 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1580 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1581 if (key == key_info[i].key_default)
1582 joy |= key_info[i].action;
1587 if (key_status == KEY_PRESSED)
1588 key_joystick_mapping |= joy;
1590 key_joystick_mapping &= ~joy;
1595 if (game_status != GAME_MODE_PLAYING)
1596 key_joystick_mapping = 0;
1598 if (key_status == KEY_RELEASED)
1600 // reset flag to ignore repeated "key pressed" events after key release
1601 ignore_repeated_key = FALSE;
1606 if ((key == KSYM_F11 ||
1607 ((key == KSYM_Return ||
1608 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1609 video.fullscreen_available &&
1610 !ignore_repeated_key)
1612 setup.fullscreen = !setup.fullscreen;
1614 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1616 if (game_status == GAME_MODE_SETUP)
1617 RedrawSetupScreenAfterFullscreenToggle();
1619 // set flag to ignore repeated "key pressed" events
1620 ignore_repeated_key = TRUE;
1625 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1626 key == KSYM_minus || key == KSYM_KP_Subtract ||
1627 key == KSYM_plus || key == KSYM_KP_Add ||
1628 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1629 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1630 video.window_scaling_available &&
1631 !video.fullscreen_enabled)
1633 if (key == KSYM_0 || key == KSYM_KP_0)
1634 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1635 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1636 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1638 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1640 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1641 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1642 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1643 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1645 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1647 if (game_status == GAME_MODE_SETUP)
1648 RedrawSetupScreenAfterFullscreenToggle();
1653 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1654 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1661 if (game_status == GAME_MODE_MAIN &&
1662 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1664 StartGameActions(options.network, setup.autorecord, level.random_seed);
1669 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1671 if (key == setup.shortcut.save_game)
1673 else if (key == setup.shortcut.load_game)
1675 else if (key == setup.shortcut.toggle_pause)
1676 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1678 HandleTapeButtonKeys(key);
1679 HandleSoundButtonKeys(key);
1682 if (game_status == GAME_MODE_PLAYING && !network_playing)
1684 int centered_player_nr_next = -999;
1686 if (key == setup.shortcut.focus_player_all)
1687 centered_player_nr_next = -1;
1689 for (i = 0; i < MAX_PLAYERS; i++)
1690 if (key == setup.shortcut.focus_player[i])
1691 centered_player_nr_next = i;
1693 if (centered_player_nr_next != -999)
1695 game.centered_player_nr_next = centered_player_nr_next;
1696 game.set_centered_player = TRUE;
1700 tape.centered_player_nr_next = game.centered_player_nr_next;
1701 tape.set_centered_player = TRUE;
1706 HandleKeysSpecial(key);
1708 if (HandleGadgetsKeyInput(key))
1710 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1711 key = KSYM_UNDEFINED;
1714 switch (game_status)
1716 case GAME_MODE_PSEUDO_TYPENAME:
1717 HandleTypeName(0, key);
1720 case GAME_MODE_TITLE:
1721 case GAME_MODE_MAIN:
1722 case GAME_MODE_LEVELS:
1723 case GAME_MODE_LEVELNR:
1724 case GAME_MODE_SETUP:
1725 case GAME_MODE_INFO:
1726 case GAME_MODE_SCORES:
1731 if (game_status == GAME_MODE_TITLE)
1732 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1733 else if (game_status == GAME_MODE_MAIN)
1734 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1735 else if (game_status == GAME_MODE_LEVELS)
1736 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1737 else if (game_status == GAME_MODE_LEVELNR)
1738 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1739 else if (game_status == GAME_MODE_SETUP)
1740 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1741 else if (game_status == GAME_MODE_INFO)
1742 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1743 else if (game_status == GAME_MODE_SCORES)
1744 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1748 if (game_status != GAME_MODE_MAIN)
1749 FadeSkipNextFadeIn();
1751 if (game_status == GAME_MODE_TITLE)
1752 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1753 else if (game_status == GAME_MODE_LEVELS)
1754 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1755 else if (game_status == GAME_MODE_LEVELNR)
1756 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1757 else if (game_status == GAME_MODE_SETUP)
1758 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1759 else if (game_status == GAME_MODE_INFO)
1760 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1761 else if (game_status == GAME_MODE_SCORES)
1762 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1766 if (game_status == GAME_MODE_LEVELS)
1767 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1768 else if (game_status == GAME_MODE_LEVELNR)
1769 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1770 else if (game_status == GAME_MODE_SETUP)
1771 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1772 else if (game_status == GAME_MODE_INFO)
1773 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1774 else if (game_status == GAME_MODE_SCORES)
1775 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1778 case KSYM_Page_Down:
1779 if (game_status == GAME_MODE_LEVELS)
1780 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1781 else if (game_status == GAME_MODE_LEVELNR)
1782 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1783 else if (game_status == GAME_MODE_SETUP)
1784 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1785 else if (game_status == GAME_MODE_INFO)
1786 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1787 else if (game_status == GAME_MODE_SCORES)
1788 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1796 case GAME_MODE_EDITOR:
1797 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1798 HandleLevelEditorKeyInput(key);
1801 case GAME_MODE_PLAYING:
1806 RequestQuitGame(setup.ask_on_escape);
1816 if (key == KSYM_Escape)
1818 SetGameStatus(GAME_MODE_MAIN);
1826 HandleKeysDebug(key);
1829 void HandleNoEvent()
1831 // if (button_status && game_status != GAME_MODE_PLAYING)
1832 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1834 HandleButton(0, 0, button_status, -button_status);
1841 #if defined(NETWORK_AVALIABLE)
1842 if (options.network)
1846 switch (game_status)
1848 case GAME_MODE_MAIN:
1849 DrawPreviewLevelAnimation();
1852 case GAME_MODE_EDITOR:
1853 HandleLevelEditorIdle();
1856 #if defined(TARGET_SDL2)
1857 case GAME_MODE_PLAYING:
1858 HandleFollowFinger(-1, -1, -1);
1867 static int HandleJoystickForAllPlayers()
1872 for (i = 0; i < MAX_PLAYERS; i++)
1874 byte joy_action = 0;
1877 if (!setup.input[i].use_joystick)
1881 joy_action = Joystick(i);
1882 result |= joy_action;
1884 if (!setup.input[i].use_joystick)
1887 stored_player[i].action = joy_action;
1893 void HandleJoystick()
1895 int joystick = HandleJoystickForAllPlayers();
1896 int keyboard = key_joystick_mapping;
1897 int joy = (joystick | keyboard);
1898 int left = joy & JOY_LEFT;
1899 int right = joy & JOY_RIGHT;
1900 int up = joy & JOY_UP;
1901 int down = joy & JOY_DOWN;
1902 int button = joy & JOY_BUTTON;
1903 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1904 int dx = (left ? -1 : right ? 1 : 0);
1905 int dy = (up ? -1 : down ? 1 : 0);
1907 switch (game_status)
1909 case GAME_MODE_TITLE:
1910 case GAME_MODE_MAIN:
1911 case GAME_MODE_LEVELS:
1912 case GAME_MODE_LEVELNR:
1913 case GAME_MODE_SETUP:
1914 case GAME_MODE_INFO:
1916 static unsigned int joystickmove_delay = 0;
1918 if (joystick && !button &&
1919 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1920 newbutton = dx = dy = 0;
1922 if (game_status == GAME_MODE_TITLE)
1923 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1924 else if (game_status == GAME_MODE_MAIN)
1925 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1926 else if (game_status == GAME_MODE_LEVELS)
1927 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1928 else if (game_status == GAME_MODE_LEVELNR)
1929 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1930 else if (game_status == GAME_MODE_SETUP)
1931 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1932 else if (game_status == GAME_MODE_INFO)
1933 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1937 case GAME_MODE_SCORES:
1938 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1941 case GAME_MODE_PLAYING:
1942 if (tape.playing || keyboard)
1943 newbutton = ((joy & JOY_BUTTON) != 0);
1945 if (newbutton && AllPlayersGone)