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 FilterEventsExt(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 #if defined(TARGET_SDL2)
92 int FilterEvents(void *userdata, Event *event)
94 return FilterEventsExt(event);
97 int FilterEvents(const Event *event)
99 return FilterEventsExt(event);
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 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 only really needed for non-SDL targets to filter unwanted events;
132 when using SDL with properly installed event filter, this function can be
133 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
135 boolean NextValidEvent(Event *event)
137 while (PendingEvent())
139 boolean handle_this_event = FALSE;
143 if (FilterEventsExt(event))
144 handle_this_event = TRUE;
146 if (SkipPressedMouseMotionEvent(event))
147 handle_this_event = FALSE;
149 if (handle_this_event)
159 unsigned int event_frame_delay = 0;
160 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
162 ResetDelayCounter(&event_frame_delay);
164 while (NextValidEvent(&event))
168 case EVENT_BUTTONPRESS:
169 case EVENT_BUTTONRELEASE:
170 HandleButtonEvent((ButtonEvent *) &event);
173 case EVENT_MOTIONNOTIFY:
174 HandleMotionEvent((MotionEvent *) &event);
177 #if defined(TARGET_SDL2)
178 case EVENT_WHEELMOTION:
179 HandleWheelEvent((WheelEvent *) &event);
182 case SDL_WINDOWEVENT:
183 HandleWindowEvent((WindowEvent *) &event);
186 case EVENT_FINGERPRESS:
187 case EVENT_FINGERRELEASE:
188 case EVENT_FINGERMOTION:
189 HandleFingerEvent((FingerEvent *) &event);
192 case EVENT_TEXTINPUT:
193 HandleTextEvent((TextEvent *) &event);
196 case SDL_APP_WILLENTERBACKGROUND:
197 case SDL_APP_DIDENTERBACKGROUND:
198 case SDL_APP_WILLENTERFOREGROUND:
199 case SDL_APP_DIDENTERFOREGROUND:
200 HandlePauseResumeEvent((PauseResumeEvent *) &event);
205 case EVENT_KEYRELEASE:
206 HandleKeyEvent((KeyEvent *) &event);
210 HandleOtherEvents(&event);
214 // do not handle events for longer than standard frame delay period
215 if (DelayReached(&event_frame_delay, event_frame_delay_value))
220 void HandleOtherEvents(Event *event)
225 HandleExposeEvent((ExposeEvent *) event);
228 case EVENT_UNMAPNOTIFY:
230 /* This causes the game to stop not only when iconified, but also
231 when on another virtual desktop, which might be not desired. */
232 SleepWhileUnmapped();
238 HandleFocusEvent((FocusChangeEvent *) event);
241 case EVENT_CLIENTMESSAGE:
242 HandleClientMessageEvent((ClientMessageEvent *) event);
245 #if defined(TARGET_SDL)
246 case SDL_JOYAXISMOTION:
247 case SDL_JOYBUTTONDOWN:
248 case SDL_JOYBUTTONUP:
249 HandleJoystickEvent(event);
253 HandleWindowManagerEvent(event);
262 void HandleMouseCursor()
264 if (game_status == GAME_MODE_TITLE)
266 /* when showing title screens, hide mouse pointer (if not moved) */
268 if (gfx.cursor_mode != CURSOR_NONE &&
269 DelayReached(&special_cursor_delay, special_cursor_delay_value))
271 SetMouseCursor(CURSOR_NONE);
274 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
277 /* when playing, display a special mouse pointer inside the playfield */
279 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
280 cursor_inside_playfield &&
281 DelayReached(&special_cursor_delay, special_cursor_delay_value))
283 SetMouseCursor(CURSOR_PLAYFIELD);
286 else if (gfx.cursor_mode != CURSOR_DEFAULT)
288 SetMouseCursor(CURSOR_DEFAULT);
291 /* this is set after all pending events have been processed */
292 cursor_mode_last = gfx.cursor_mode;
304 /* also execute after pending events have been processed before */
307 /* don't use all CPU time when idle; the main loop while playing
308 has its own synchronization and is CPU friendly, too */
310 if (game_status == GAME_MODE_PLAYING)
313 /* always copy backbuffer to visible screen for every video frame */
316 /* reset video frame delay to default (may change again while playing) */
317 SetVideoFrameDelay(MenuFrameDelay);
319 if (game_status == GAME_MODE_QUIT)
324 void ClearEventQueue()
326 while (PendingEvent())
334 case EVENT_BUTTONRELEASE:
335 button_status = MB_RELEASED;
338 case EVENT_KEYRELEASE:
343 HandleOtherEvents(&event);
349 void ClearPlayerAction()
353 /* simulate key release events for still pressed keys */
354 key_joystick_mapping = 0;
355 for (i = 0; i < MAX_PLAYERS; i++)
356 stored_player[i].action = 0;
359 void SleepWhileUnmapped()
361 boolean window_unmapped = TRUE;
363 KeyboardAutoRepeatOn();
365 while (window_unmapped)
373 case EVENT_BUTTONRELEASE:
374 button_status = MB_RELEASED;
377 case EVENT_KEYRELEASE:
378 key_joystick_mapping = 0;
381 case EVENT_MAPNOTIFY:
382 window_unmapped = FALSE;
385 case EVENT_UNMAPNOTIFY:
386 /* this is only to surely prevent the 'should not happen' case
387 * of recursively looping between 'SleepWhileUnmapped()' and
388 * 'HandleOtherEvents()' which usually calls this funtion.
393 HandleOtherEvents(&event);
398 if (game_status == GAME_MODE_PLAYING)
399 KeyboardAutoRepeatOffUnlessAutoplay();
402 void HandleExposeEvent(ExposeEvent *event)
406 void HandleButtonEvent(ButtonEvent *event)
408 #if DEBUG_EVENTS_BUTTON
409 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
411 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
415 #if defined(HAS_SCREEN_KEYBOARD)
416 if (video.shifted_up)
417 event->y += video.shifted_up_pos;
420 motion_status = FALSE;
422 if (event->type == EVENT_BUTTONPRESS)
423 button_status = event->button;
425 button_status = MB_RELEASED;
427 HandleButton(event->x, event->y, button_status, event->button);
430 void HandleMotionEvent(MotionEvent *event)
432 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
435 motion_status = TRUE;
437 #if DEBUG_EVENTS_MOTION
438 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
439 button_status, event->x, event->y);
442 HandleButton(event->x, event->y, button_status, button_status);
445 #if defined(TARGET_SDL2)
447 void HandleWheelEvent(WheelEvent *event)
451 #if DEBUG_EVENTS_WHEEL
453 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
454 event->which, event->x, event->y);
456 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
457 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
458 event->which, event->x, event->y,
459 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
460 "SDL_MOUSEWHEEL_FLIPPED"));
464 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
465 event->x > 0 ? MB_WHEEL_RIGHT :
466 event->y < 0 ? MB_WHEEL_DOWN :
467 event->y > 0 ? MB_WHEEL_UP : 0);
469 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
470 // accelerated mouse wheel available on Mac and Windows
471 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
473 // no accelerated mouse wheel available on Unix/Linux
474 wheel_steps = DEFAULT_WHEEL_STEPS;
477 motion_status = FALSE;
479 button_status = button_nr;
480 HandleButton(0, 0, button_status, -button_nr);
482 button_status = MB_RELEASED;
483 HandleButton(0, 0, button_status, -button_nr);
486 void HandleWindowEvent(WindowEvent *event)
488 #if DEBUG_EVENTS_WINDOW
489 int subtype = event->event;
492 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
493 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
494 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
495 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
496 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
497 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
498 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
499 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
500 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
501 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
502 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
503 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
504 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
505 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
508 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
509 event_name, event->data1, event->data2);
513 // (not needed, as the screen gets redrawn every 20 ms anyway)
514 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
515 event->event == SDL_WINDOWEVENT_RESIZED ||
516 event->event == SDL_WINDOWEVENT_EXPOSED)
520 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
522 int new_window_width = event->data1;
523 int new_window_height = event->data2;
525 // if window size has changed after resizing, calculate new scaling factor
526 if (new_window_width != video.window_width ||
527 new_window_height != video.window_height)
529 int new_xpercent = (100 * new_window_width / video.width);
530 int new_ypercent = (100 * new_window_height / video.height);
532 // (extreme window scaling allowed, but cannot be saved permanently)
533 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
534 setup.window_scaling_percent =
535 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
536 MAX_WINDOW_SCALING_PERCENT);
538 video.window_width = new_window_width;
539 video.window_height = new_window_height;
541 if (game_status == GAME_MODE_SETUP)
542 RedrawSetupScreenAfterFullscreenToggle();
549 #define NUM_TOUCH_FINGERS 3
554 SDL_FingerID finger_id;
557 } touch_info[NUM_TOUCH_FINGERS];
559 void HandleFingerEvent(FingerEvent *event)
561 static Key motion_key_x = KSYM_UNDEFINED;
562 static Key motion_key_y = KSYM_UNDEFINED;
563 static Key button_key = KSYM_UNDEFINED;
564 static float motion_x1, motion_y1;
565 static float button_x1, button_y1;
566 static SDL_FingerID motion_id = -1;
567 static SDL_FingerID button_id = -1;
568 int move_trigger_distance_percent = setup.touch.move_distance;
569 int drop_trigger_distance_percent = setup.touch.drop_distance;
570 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
571 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
572 float event_x = event->x;
573 float event_y = event->y;
575 #if DEBUG_EVENTS_FINGER
576 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
577 event->type == EVENT_FINGERPRESS ? "pressed" :
578 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
582 event->dx, event->dy,
586 if (game_status != GAME_MODE_PLAYING)
589 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
592 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
594 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
596 Key key = (event->x < 1.0 / 3.0 ?
597 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
598 setup.input[0].key.drop) :
599 event->x > 2.0 / 3.0 ?
600 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
601 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
602 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
603 setup.input[0].key.right) :
605 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
609 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
610 getKeyNameFromKey(key), key_status_name, event->fingerId);
612 // check if we already know this touch event's finger id
613 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
615 if (touch_info[i].touched &&
616 touch_info[i].finger_id == event->fingerId)
618 // Error(ERR_DEBUG, "MARK 1: %d", i);
624 if (i >= NUM_TOUCH_FINGERS)
626 if (key_status == KEY_PRESSED)
628 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
630 // unknown finger id -- get new, empty slot, if available
631 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
633 if (touch_info[i].counter < oldest_counter)
636 oldest_counter = touch_info[i].counter;
638 // Error(ERR_DEBUG, "MARK 2: %d", i);
641 if (!touch_info[i].touched)
643 // Error(ERR_DEBUG, "MARK 3: %d", i);
649 if (i >= NUM_TOUCH_FINGERS)
651 // all slots allocated -- use oldest slot
654 // Error(ERR_DEBUG, "MARK 4: %d", i);
659 // release of previously unknown key (should not happen)
661 if (key != KSYM_UNDEFINED)
663 HandleKey(key, KEY_RELEASED);
665 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
666 getKeyNameFromKey(key), "KEY_RELEASED", i);
671 if (i < NUM_TOUCH_FINGERS)
673 if (key_status == KEY_PRESSED)
675 if (touch_info[i].key != key)
677 if (touch_info[i].key != KSYM_UNDEFINED)
679 HandleKey(touch_info[i].key, KEY_RELEASED);
681 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
682 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
685 if (key != KSYM_UNDEFINED)
687 HandleKey(key, KEY_PRESSED);
689 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
690 getKeyNameFromKey(key), "KEY_PRESSED", i);
694 touch_info[i].touched = TRUE;
695 touch_info[i].finger_id = event->fingerId;
696 touch_info[i].counter = Counter();
697 touch_info[i].key = key;
701 if (touch_info[i].key != KSYM_UNDEFINED)
703 HandleKey(touch_info[i].key, KEY_RELEASED);
705 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
706 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
709 touch_info[i].touched = FALSE;
710 touch_info[i].finger_id = 0;
711 touch_info[i].counter = 0;
712 touch_info[i].key = 0;
719 // use touch direction control
721 if (event->type == EVENT_FINGERPRESS)
723 if (event_x > 1.0 / 3.0)
727 motion_id = event->fingerId;
732 motion_key_x = KSYM_UNDEFINED;
733 motion_key_y = KSYM_UNDEFINED;
735 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
741 button_id = event->fingerId;
746 button_key = setup.input[0].key.snap;
748 HandleKey(button_key, KEY_PRESSED);
750 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
753 else if (event->type == EVENT_FINGERRELEASE)
755 if (event->fingerId == motion_id)
759 if (motion_key_x != KSYM_UNDEFINED)
760 HandleKey(motion_key_x, KEY_RELEASED);
761 if (motion_key_y != KSYM_UNDEFINED)
762 HandleKey(motion_key_y, KEY_RELEASED);
764 motion_key_x = KSYM_UNDEFINED;
765 motion_key_y = KSYM_UNDEFINED;
767 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
769 else if (event->fingerId == button_id)
773 if (button_key != KSYM_UNDEFINED)
774 HandleKey(button_key, KEY_RELEASED);
776 button_key = KSYM_UNDEFINED;
778 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
781 else if (event->type == EVENT_FINGERMOTION)
783 if (event->fingerId == motion_id)
785 float distance_x = ABS(event_x - motion_x1);
786 float distance_y = ABS(event_y - motion_y1);
787 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
788 event_x > motion_x1 ? setup.input[0].key.right :
790 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
791 event_y > motion_y1 ? setup.input[0].key.down :
794 if (distance_x < move_trigger_distance / 2 ||
795 distance_x < distance_y)
796 new_motion_key_x = KSYM_UNDEFINED;
798 if (distance_y < move_trigger_distance / 2 ||
799 distance_y < distance_x)
800 new_motion_key_y = KSYM_UNDEFINED;
802 if (distance_x > move_trigger_distance ||
803 distance_y > move_trigger_distance)
805 if (new_motion_key_x != motion_key_x)
807 if (motion_key_x != KSYM_UNDEFINED)
808 HandleKey(motion_key_x, KEY_RELEASED);
809 if (new_motion_key_x != KSYM_UNDEFINED)
810 HandleKey(new_motion_key_x, KEY_PRESSED);
813 if (new_motion_key_y != motion_key_y)
815 if (motion_key_y != KSYM_UNDEFINED)
816 HandleKey(motion_key_y, KEY_RELEASED);
817 if (new_motion_key_y != KSYM_UNDEFINED)
818 HandleKey(new_motion_key_y, KEY_PRESSED);
824 motion_key_x = new_motion_key_x;
825 motion_key_y = new_motion_key_y;
827 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
830 else if (event->fingerId == button_id)
832 float distance_x = ABS(event_x - button_x1);
833 float distance_y = ABS(event_y - button_y1);
835 if (distance_x < drop_trigger_distance / 2 &&
836 distance_y > drop_trigger_distance)
838 if (button_key == setup.input[0].key.snap)
839 HandleKey(button_key, KEY_RELEASED);
844 button_key = setup.input[0].key.drop;
846 HandleKey(button_key, KEY_PRESSED);
848 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
854 static void HandleFollowFinger(int mx, int my, int button)
856 static int old_mx = 0, old_my = 0;
857 static Key motion_key_x = KSYM_UNDEFINED;
858 static Key motion_key_y = KSYM_UNDEFINED;
859 static boolean started_on_player = FALSE;
860 static boolean player_is_dropping = FALSE;
861 static int player_drop_count = 0;
862 static int last_player_x = -1;
863 static int last_player_y = -1;
865 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
868 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
870 touch_info[0].touched = TRUE;
871 touch_info[0].key = 0;
878 started_on_player = FALSE;
879 player_is_dropping = FALSE;
880 player_drop_count = 0;
884 motion_key_x = KSYM_UNDEFINED;
885 motion_key_y = KSYM_UNDEFINED;
887 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
890 else if (button == MB_RELEASED && touch_info[0].touched)
892 touch_info[0].touched = FALSE;
893 touch_info[0].key = 0;
898 if (motion_key_x != KSYM_UNDEFINED)
899 HandleKey(motion_key_x, KEY_RELEASED);
900 if (motion_key_y != KSYM_UNDEFINED)
901 HandleKey(motion_key_y, KEY_RELEASED);
903 if (started_on_player)
905 if (player_is_dropping)
907 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
909 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
913 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
915 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
919 motion_key_x = KSYM_UNDEFINED;
920 motion_key_y = KSYM_UNDEFINED;
922 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
925 if (touch_info[0].touched)
927 int src_x = local_player->jx;
928 int src_y = local_player->jy;
929 int dst_x = getLevelFromScreenX(old_mx);
930 int dst_y = getLevelFromScreenY(old_my);
931 int dx = dst_x - src_x;
932 int dy = dst_y - src_y;
933 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
934 dx > 0 ? setup.input[0].key.right :
936 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
937 dy > 0 ? setup.input[0].key.down :
940 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
941 (last_player_x != local_player->jx ||
942 last_player_y != local_player->jy))
944 // in case of asymmetric diagonal movement, use "preferred" direction
946 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
948 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
949 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
951 local_player->last_move_dir = last_move_dir;
953 // (required to prevent accidentally forcing direction for next movement)
954 last_player_x = local_player->jx;
955 last_player_y = local_player->jy;
958 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
960 started_on_player = TRUE;
961 player_drop_count = getPlayerInventorySize(0);
962 player_is_dropping = (player_drop_count > 0);
964 if (player_is_dropping)
966 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
968 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
972 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
974 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
977 else if (dx != 0 || dy != 0)
979 if (player_is_dropping &&
980 player_drop_count == getPlayerInventorySize(0))
982 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
984 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
985 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
987 player_is_dropping = FALSE;
991 if (new_motion_key_x != motion_key_x)
993 Error(ERR_DEBUG, "---------- %s %s ----------",
994 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
995 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
997 if (motion_key_x != KSYM_UNDEFINED)
998 HandleKey(motion_key_x, KEY_RELEASED);
999 if (new_motion_key_x != KSYM_UNDEFINED)
1000 HandleKey(new_motion_key_x, KEY_PRESSED);
1003 if (new_motion_key_y != motion_key_y)
1005 Error(ERR_DEBUG, "---------- %s %s ----------",
1006 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1007 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1009 if (motion_key_y != KSYM_UNDEFINED)
1010 HandleKey(motion_key_y, KEY_RELEASED);
1011 if (new_motion_key_y != KSYM_UNDEFINED)
1012 HandleKey(new_motion_key_y, KEY_PRESSED);
1015 motion_key_x = new_motion_key_x;
1016 motion_key_y = new_motion_key_y;
1020 static boolean checkTextInputKeyModState()
1022 // when playing, only handle raw key events and ignore text input
1023 if (game_status == GAME_MODE_PLAYING)
1026 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1029 void HandleTextEvent(TextEvent *event)
1031 char *text = event->text;
1032 Key key = getKeyFromKeyName(text);
1034 #if DEBUG_EVENTS_TEXT
1035 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1038 text[0], (int)(text[0]),
1040 getKeyNameFromKey(key),
1044 #if !defined(HAS_SCREEN_KEYBOARD)
1045 // non-mobile devices: only handle key input with modifier keys pressed here
1046 // (every other key input is handled directly as physical key input event)
1047 if (!checkTextInputKeyModState())
1051 // process text input as "classic" (with uppercase etc.) key input event
1052 HandleKey(key, KEY_PRESSED);
1053 HandleKey(key, KEY_RELEASED);
1056 void HandlePauseResumeEvent(PauseResumeEvent *event)
1058 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1062 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1070 void HandleKeyEvent(KeyEvent *event)
1072 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1073 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1074 Key key = GetEventKey(event, with_modifiers);
1075 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1077 #if DEBUG_EVENTS_KEY
1078 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1079 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1080 event->keysym.scancode,
1085 getKeyNameFromKey(key));
1088 #if defined(PLATFORM_ANDROID)
1089 // always map the "back" button to the "escape" key on Android devices
1090 if (key == KSYM_Back)
1094 HandleKeyModState(keymod, key_status);
1096 #if defined(TARGET_SDL2)
1097 // only handle raw key input without text modifier keys pressed
1098 if (!checkTextInputKeyModState())
1099 HandleKey(key, key_status);
1101 HandleKey(key, key_status);
1105 void HandleFocusEvent(FocusChangeEvent *event)
1107 static int old_joystick_status = -1;
1109 if (event->type == EVENT_FOCUSOUT)
1111 KeyboardAutoRepeatOn();
1112 old_joystick_status = joystick.status;
1113 joystick.status = JOYSTICK_NOT_AVAILABLE;
1115 ClearPlayerAction();
1117 else if (event->type == EVENT_FOCUSIN)
1119 /* When there are two Rocks'n'Diamonds windows which overlap and
1120 the player moves the pointer from one game window to the other,
1121 a 'FocusOut' event is generated for the window the pointer is
1122 leaving and a 'FocusIn' event is generated for the window the
1123 pointer is entering. In some cases, it can happen that the
1124 'FocusIn' event is handled by the one game process before the
1125 'FocusOut' event by the other game process. In this case the
1126 X11 environment would end up with activated keyboard auto repeat,
1127 because unfortunately this is a global setting and not (which
1128 would be far better) set for each X11 window individually.
1129 The effect would be keyboard auto repeat while playing the game
1130 (game_status == GAME_MODE_PLAYING), which is not desired.
1131 To avoid this special case, we just wait 1/10 second before
1132 processing the 'FocusIn' event.
1135 if (game_status == GAME_MODE_PLAYING)
1138 KeyboardAutoRepeatOffUnlessAutoplay();
1141 if (old_joystick_status != -1)
1142 joystick.status = old_joystick_status;
1146 void HandleClientMessageEvent(ClientMessageEvent *event)
1148 if (CheckCloseWindowEvent(event))
1152 void HandleWindowManagerEvent(Event *event)
1154 #if defined(TARGET_SDL)
1155 SDLHandleWindowManagerEvent(event);
1159 void HandleButton(int mx, int my, int button, int button_nr)
1161 static int old_mx = 0, old_my = 0;
1162 boolean button_hold = FALSE;
1168 button_nr = -button_nr;
1177 #if defined(PLATFORM_ANDROID)
1178 // when playing, only handle gadgets when using "follow finger" controls
1179 boolean handle_gadgets =
1180 (game_status != GAME_MODE_PLAYING ||
1181 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1183 if (handle_gadgets &&
1184 HandleGadgets(mx, my, button))
1186 /* do not handle this button event anymore */
1187 mx = my = -32; /* force mouse event to be outside screen tiles */
1190 if (HandleGadgets(mx, my, button))
1192 /* do not handle this button event anymore */
1193 mx = my = -32; /* force mouse event to be outside screen tiles */
1197 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1200 /* do not use scroll wheel button events for anything other than gadgets */
1201 if (IS_WHEEL_BUTTON(button_nr))
1204 switch (game_status)
1206 case GAME_MODE_TITLE:
1207 HandleTitleScreen(mx, my, 0, 0, button);
1210 case GAME_MODE_MAIN:
1211 HandleMainMenu(mx, my, 0, 0, button);
1214 case GAME_MODE_PSEUDO_TYPENAME:
1215 HandleTypeName(0, KSYM_Return);
1218 case GAME_MODE_LEVELS:
1219 HandleChooseLevelSet(mx, my, 0, 0, button);
1222 case GAME_MODE_LEVELNR:
1223 HandleChooseLevelNr(mx, my, 0, 0, button);
1226 case GAME_MODE_SCORES:
1227 HandleHallOfFame(0, 0, 0, 0, button);
1230 case GAME_MODE_EDITOR:
1231 HandleLevelEditorIdle();
1234 case GAME_MODE_INFO:
1235 HandleInfoScreen(mx, my, 0, 0, button);
1238 case GAME_MODE_SETUP:
1239 HandleSetupScreen(mx, my, 0, 0, button);
1242 case GAME_MODE_PLAYING:
1243 HandleFollowFinger(mx, my, button);
1246 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1247 GetKeyModState() & KMOD_Control)
1248 DumpTileFromScreen(mx, my);
1258 static boolean is_string_suffix(char *string, char *suffix)
1260 int string_len = strlen(string);
1261 int suffix_len = strlen(suffix);
1263 if (suffix_len > string_len)
1266 return (strEqual(&string[string_len - suffix_len], suffix));
1269 #define MAX_CHEAT_INPUT_LEN 32
1271 static void HandleKeysSpecial(Key key)
1273 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1274 char letter = getCharFromKey(key);
1275 int cheat_input_len = strlen(cheat_input);
1281 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1283 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1284 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1286 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1289 cheat_input[cheat_input_len++] = letter;
1290 cheat_input[cheat_input_len] = '\0';
1292 #if DEBUG_EVENTS_KEY
1293 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1296 if (game_status == GAME_MODE_MAIN)
1298 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1299 is_string_suffix(cheat_input, ":ist"))
1301 InsertSolutionTape();
1303 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1304 is_string_suffix(cheat_input, ":rg"))
1306 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1309 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1310 is_string_suffix(cheat_input, ":rs"))
1312 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1315 else if (is_string_suffix(cheat_input, ":reload-music") ||
1316 is_string_suffix(cheat_input, ":rm"))
1318 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1321 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1322 is_string_suffix(cheat_input, ":ra"))
1324 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1325 1 << ARTWORK_TYPE_SOUNDS |
1326 1 << ARTWORK_TYPE_MUSIC);
1329 else if (is_string_suffix(cheat_input, ":dump-level") ||
1330 is_string_suffix(cheat_input, ":dl"))
1334 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1335 is_string_suffix(cheat_input, ":dt"))
1339 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1340 is_string_suffix(cheat_input, ":ft"))
1342 /* fix single-player tapes that contain player input for more than one
1343 player (due to a bug in 3.3.1.2 and earlier versions), which results
1344 in playing levels with more than one player in multi-player mode,
1345 even though the tape was originally recorded in single-player mode */
1347 /* remove player input actions for all players but the first one */
1348 for (i = 1; i < MAX_PLAYERS; i++)
1349 tape.player_participates[i] = FALSE;
1351 tape.changed = TRUE;
1353 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1354 is_string_suffix(cheat_input, ":snl"))
1356 SaveNativeLevel(&level);
1359 else if (game_status == GAME_MODE_PLAYING)
1362 if (is_string_suffix(cheat_input, ".q"))
1363 DEBUG_SetMaximumDynamite();
1366 else if (game_status == GAME_MODE_EDITOR)
1368 if (is_string_suffix(cheat_input, ":dump-brush") ||
1369 is_string_suffix(cheat_input, ":DB"))
1373 else if (is_string_suffix(cheat_input, ":DDB"))
1380 void HandleKeysDebug(Key key)
1385 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1387 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1389 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1391 if (key == setup.debug.frame_delay_key[i] &&
1392 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1394 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1395 setup.debug.frame_delay[i] : GAME_FRAME_DELAY);
1397 if (!setup.debug.frame_delay_game_only)
1398 MenuFrameDelay = GameFrameDelay;
1400 SetVideoFrameDelay(GameFrameDelay);
1402 if (GameFrameDelay > ONE_SECOND_DELAY)
1403 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1404 else if (GameFrameDelay != 0)
1405 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1406 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1407 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1409 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1416 if (game_status == GAME_MODE_PLAYING)
1420 options.debug = !options.debug;
1422 Error(ERR_DEBUG, "debug mode %s",
1423 (options.debug ? "enabled" : "disabled"));
1425 else if (key == KSYM_v)
1427 Error(ERR_DEBUG, "currently using game engine version %d",
1428 game.engine_version);
1434 void HandleKey(Key key, int key_status)
1436 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1437 static boolean ignore_repeated_key = FALSE;
1438 static struct SetupKeyboardInfo ski;
1439 static struct SetupShortcutInfo ssi;
1448 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1449 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1450 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1451 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1452 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1453 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1458 if (game_status == GAME_MODE_PLAYING)
1460 /* only needed for single-step tape recording mode */
1461 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1462 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1463 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1464 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1467 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1469 byte key_action = 0;
1471 if (setup.input[pnr].use_joystick)
1474 ski = setup.input[pnr].key;
1476 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1477 if (key == *key_info[i].key_custom)
1478 key_action |= key_info[i].action;
1480 /* use combined snap+direction keys for the first player only */
1483 ssi = setup.shortcut;
1485 for (i = 0; i < NUM_DIRECTIONS; i++)
1486 if (key == *key_info[i].key_snap)
1487 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1490 /* clear delayed snap and drop actions in single step mode (see below) */
1491 if (tape.single_step)
1493 if (clear_snap_button[pnr])
1495 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1496 clear_snap_button[pnr] = FALSE;
1499 if (clear_drop_button[pnr])
1501 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1502 clear_drop_button[pnr] = FALSE;
1506 if (key_status == KEY_PRESSED)
1507 stored_player[pnr].action |= key_action;
1509 stored_player[pnr].action &= ~key_action;
1511 if (tape.single_step && tape.recording && tape.pausing)
1513 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1515 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1517 /* if snap key already pressed, don't snap when releasing (below) */
1518 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1519 element_snapped[pnr] = TRUE;
1521 /* if drop key already pressed, don't drop when releasing (below) */
1522 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1523 element_dropped[pnr] = TRUE;
1525 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1527 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1528 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1531 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1532 getRedDiskReleaseFlag_SP() == 0)
1533 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1535 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1538 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1540 if (key_action & KEY_BUTTON_SNAP)
1542 /* if snap key was released without moving (see above), snap now */
1543 if (!element_snapped[pnr])
1545 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1547 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1549 /* clear delayed snap button on next event */
1550 clear_snap_button[pnr] = TRUE;
1553 element_snapped[pnr] = FALSE;
1556 if (key_action & KEY_BUTTON_DROP &&
1557 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1559 /* if drop key was released without moving (see above), drop now */
1560 if (!element_dropped[pnr])
1562 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1564 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1565 getRedDiskReleaseFlag_SP() != 0)
1566 stored_player[pnr].action |= KEY_BUTTON_DROP;
1568 /* clear delayed drop button on next event */
1569 clear_drop_button[pnr] = TRUE;
1572 element_dropped[pnr] = FALSE;
1576 else if (tape.recording && tape.pausing)
1578 /* prevent key release events from un-pausing a paused game */
1579 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1580 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1586 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1587 if (key == key_info[i].key_default)
1588 joy |= key_info[i].action;
1593 if (key_status == KEY_PRESSED)
1594 key_joystick_mapping |= joy;
1596 key_joystick_mapping &= ~joy;
1601 if (game_status != GAME_MODE_PLAYING)
1602 key_joystick_mapping = 0;
1604 if (key_status == KEY_RELEASED)
1606 // reset flag to ignore repeated "key pressed" events after key release
1607 ignore_repeated_key = FALSE;
1612 if ((key == KSYM_F11 ||
1613 ((key == KSYM_Return ||
1614 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1615 video.fullscreen_available &&
1616 !ignore_repeated_key)
1618 setup.fullscreen = !setup.fullscreen;
1620 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1622 if (game_status == GAME_MODE_SETUP)
1623 RedrawSetupScreenAfterFullscreenToggle();
1625 // set flag to ignore repeated "key pressed" events
1626 ignore_repeated_key = TRUE;
1631 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1632 key == KSYM_minus || key == KSYM_KP_Subtract ||
1633 key == KSYM_plus || key == KSYM_KP_Add ||
1634 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1635 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1636 video.window_scaling_available &&
1637 !video.fullscreen_enabled)
1639 if (key == KSYM_0 || key == KSYM_KP_0)
1640 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1641 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1642 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1644 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1646 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1647 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1648 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1649 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1651 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1653 if (game_status == GAME_MODE_SETUP)
1654 RedrawSetupScreenAfterFullscreenToggle();
1659 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1660 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1667 if (game_status == GAME_MODE_MAIN &&
1668 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1670 StartGameActions(options.network, setup.autorecord, level.random_seed);
1675 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1677 if (key == setup.shortcut.save_game)
1679 else if (key == setup.shortcut.load_game)
1681 else if (key == setup.shortcut.toggle_pause)
1682 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1684 HandleTapeButtonKeys(key);
1685 HandleSoundButtonKeys(key);
1688 if (game_status == GAME_MODE_PLAYING && !network_playing)
1690 int centered_player_nr_next = -999;
1692 if (key == setup.shortcut.focus_player_all)
1693 centered_player_nr_next = -1;
1695 for (i = 0; i < MAX_PLAYERS; i++)
1696 if (key == setup.shortcut.focus_player[i])
1697 centered_player_nr_next = i;
1699 if (centered_player_nr_next != -999)
1701 game.centered_player_nr_next = centered_player_nr_next;
1702 game.set_centered_player = TRUE;
1706 tape.centered_player_nr_next = game.centered_player_nr_next;
1707 tape.set_centered_player = TRUE;
1712 HandleKeysSpecial(key);
1714 if (HandleGadgetsKeyInput(key))
1716 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1717 key = KSYM_UNDEFINED;
1720 switch (game_status)
1722 case GAME_MODE_PSEUDO_TYPENAME:
1723 HandleTypeName(0, key);
1726 case GAME_MODE_TITLE:
1727 case GAME_MODE_MAIN:
1728 case GAME_MODE_LEVELS:
1729 case GAME_MODE_LEVELNR:
1730 case GAME_MODE_SETUP:
1731 case GAME_MODE_INFO:
1732 case GAME_MODE_SCORES:
1737 if (game_status == GAME_MODE_TITLE)
1738 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1739 else if (game_status == GAME_MODE_MAIN)
1740 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1741 else if (game_status == GAME_MODE_LEVELS)
1742 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1743 else if (game_status == GAME_MODE_LEVELNR)
1744 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1745 else if (game_status == GAME_MODE_SETUP)
1746 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1747 else if (game_status == GAME_MODE_INFO)
1748 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1749 else if (game_status == GAME_MODE_SCORES)
1750 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1754 if (game_status != GAME_MODE_MAIN)
1755 FadeSkipNextFadeIn();
1757 if (game_status == GAME_MODE_TITLE)
1758 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1759 else if (game_status == GAME_MODE_LEVELS)
1760 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1761 else if (game_status == GAME_MODE_LEVELNR)
1762 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1763 else if (game_status == GAME_MODE_SETUP)
1764 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1765 else if (game_status == GAME_MODE_INFO)
1766 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1767 else if (game_status == GAME_MODE_SCORES)
1768 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1772 if (game_status == GAME_MODE_LEVELS)
1773 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1774 else if (game_status == GAME_MODE_LEVELNR)
1775 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1776 else if (game_status == GAME_MODE_SETUP)
1777 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1778 else if (game_status == GAME_MODE_INFO)
1779 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1780 else if (game_status == GAME_MODE_SCORES)
1781 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1784 case KSYM_Page_Down:
1785 if (game_status == GAME_MODE_LEVELS)
1786 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1787 else if (game_status == GAME_MODE_LEVELNR)
1788 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1789 else if (game_status == GAME_MODE_SETUP)
1790 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1791 else if (game_status == GAME_MODE_INFO)
1792 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1793 else if (game_status == GAME_MODE_SCORES)
1794 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1802 case GAME_MODE_EDITOR:
1803 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1804 HandleLevelEditorKeyInput(key);
1807 case GAME_MODE_PLAYING:
1812 RequestQuitGame(setup.ask_on_escape);
1822 if (key == KSYM_Escape)
1824 SetGameStatus(GAME_MODE_MAIN);
1832 HandleKeysDebug(key);
1835 void HandleNoEvent()
1837 // if (button_status && game_status != GAME_MODE_PLAYING)
1838 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1840 HandleButton(0, 0, button_status, -button_status);
1847 #if defined(NETWORK_AVALIABLE)
1848 if (options.network)
1852 switch (game_status)
1854 case GAME_MODE_MAIN:
1855 DrawPreviewLevelAnimation();
1858 case GAME_MODE_EDITOR:
1859 HandleLevelEditorIdle();
1862 case GAME_MODE_PLAYING:
1863 HandleFollowFinger(-1, -1, -1);
1871 static int HandleJoystickForAllPlayers()
1876 for (i = 0; i < MAX_PLAYERS; i++)
1878 byte joy_action = 0;
1881 if (!setup.input[i].use_joystick)
1885 joy_action = Joystick(i);
1886 result |= joy_action;
1888 if (!setup.input[i].use_joystick)
1891 stored_player[i].action = joy_action;
1897 void HandleJoystick()
1899 int joystick = HandleJoystickForAllPlayers();
1900 int keyboard = key_joystick_mapping;
1901 int joy = (joystick | keyboard);
1902 int left = joy & JOY_LEFT;
1903 int right = joy & JOY_RIGHT;
1904 int up = joy & JOY_UP;
1905 int down = joy & JOY_DOWN;
1906 int button = joy & JOY_BUTTON;
1907 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1908 int dx = (left ? -1 : right ? 1 : 0);
1909 int dy = (up ? -1 : down ? 1 : 0);
1911 switch (game_status)
1913 case GAME_MODE_TITLE:
1914 case GAME_MODE_MAIN:
1915 case GAME_MODE_LEVELS:
1916 case GAME_MODE_LEVELNR:
1917 case GAME_MODE_SETUP:
1918 case GAME_MODE_INFO:
1920 static unsigned int joystickmove_delay = 0;
1922 if (joystick && !button &&
1923 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1924 newbutton = dx = dy = 0;
1926 if (game_status == GAME_MODE_TITLE)
1927 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1928 else if (game_status == GAME_MODE_MAIN)
1929 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1930 else if (game_status == GAME_MODE_LEVELS)
1931 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1932 else if (game_status == GAME_MODE_LEVELNR)
1933 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1934 else if (game_status == GAME_MODE_SETUP)
1935 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1936 else if (game_status == GAME_MODE_INFO)
1937 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1941 case GAME_MODE_SCORES:
1942 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1945 case GAME_MODE_PLAYING:
1946 if (tape.playing || keyboard)
1947 newbutton = ((joy & JOY_BUTTON) != 0);
1949 if (newbutton && AllPlayersGone)