1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_EVENTS 0
28 #define DEBUG_EVENTS_BUTTON (DEBUG_EVENTS * 0)
29 #define DEBUG_EVENTS_MOTION (DEBUG_EVENTS * 0)
30 #define DEBUG_EVENTS_WHEEL (DEBUG_EVENTS * 1)
31 #define DEBUG_EVENTS_WINDOW (DEBUG_EVENTS * 0)
32 #define DEBUG_EVENTS_FINGER (DEBUG_EVENTS * 0)
33 #define DEBUG_EVENTS_TEXT (DEBUG_EVENTS * 1)
34 #define DEBUG_EVENTS_KEY (DEBUG_EVENTS * 1)
37 static boolean cursor_inside_playfield = FALSE;
38 static int cursor_mode_last = CURSOR_DEFAULT;
39 static unsigned int special_cursor_delay = 0;
40 static unsigned int special_cursor_delay_value = 1000;
42 /* event filter especially needed for SDL event filtering due to
43 delay problems with lots of mouse motion events when mouse button
44 not pressed (X11 can handle this with 'PointerMotionHintMask') */
46 /* event filter addition for SDL2: as SDL2 does not have a function to enable
47 or disable keyboard auto-repeat, filter repeated keyboard events instead */
49 static int FilterEvents(const Event *event)
53 #if defined(TARGET_SDL2)
54 /* skip repeated key press events if keyboard auto-repeat is disabled */
55 if (event->type == EVENT_KEYPRESS &&
61 if (event->type == EVENT_BUTTONPRESS ||
62 event->type == EVENT_BUTTONRELEASE)
64 ((ButtonEvent *)event)->x -= video.screen_xoffset;
65 ((ButtonEvent *)event)->y -= video.screen_yoffset;
67 else if (event->type == EVENT_MOTIONNOTIFY)
69 ((MotionEvent *)event)->x -= video.screen_xoffset;
70 ((MotionEvent *)event)->y -= video.screen_yoffset;
73 /* non-motion events are directly passed to event handler functions */
74 if (event->type != EVENT_MOTIONNOTIFY)
77 motion = (MotionEvent *)event;
78 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
79 motion->y >= SY && motion->y < SY + SYSIZE);
81 /* do no reset mouse cursor before all pending events have been processed */
82 if (gfx.cursor_mode == cursor_mode_last &&
83 ((game_status == GAME_MODE_TITLE &&
84 gfx.cursor_mode == CURSOR_NONE) ||
85 (game_status == GAME_MODE_PLAYING &&
86 gfx.cursor_mode == CURSOR_PLAYFIELD)))
88 SetMouseCursor(CURSOR_DEFAULT);
90 DelayReached(&special_cursor_delay, 0);
92 cursor_mode_last = CURSOR_DEFAULT;
95 /* skip mouse motion events without pressed button outside level editor */
96 if (button_status == MB_RELEASED &&
97 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
103 /* to prevent delay problems, skip mouse motion events if the very next
104 event is also a mouse motion event (and therefore effectively only
105 handling the last of a row of mouse motion events in the event queue) */
107 static boolean SkipPressedMouseMotionEvent(const Event *event)
109 /* nothing to do if the current event is not a mouse motion event */
110 if (event->type != EVENT_MOTIONNOTIFY)
113 /* only skip motion events with pressed button outside the game */
114 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
121 PeekEvent(&next_event);
123 /* if next event is also a mouse motion event, skip the current one */
124 if (next_event.type == EVENT_MOTIONNOTIFY)
131 /* this is especially needed for event modifications for the Android target:
132 if mouse coordinates should be modified in the event filter function,
133 using a properly installed SDL event filter does not work, because in
134 the event filter, mouse coordinates in the event structure are still
135 physical pixel positions, not logical (scaled) screen positions, so this
136 has to be handled at a later stage in the event processing functions
137 (when device pixel positions are already converted to screen positions) */
139 boolean NextValidEvent(Event *event)
141 while (PendingEvent())
143 boolean handle_this_event = FALSE;
147 if (FilterEvents(event))
148 handle_this_event = TRUE;
150 if (SkipPressedMouseMotionEvent(event))
151 handle_this_event = FALSE;
153 if (handle_this_event)
163 unsigned int event_frame_delay = 0;
164 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
166 ResetDelayCounter(&event_frame_delay);
168 while (NextValidEvent(&event))
172 case EVENT_BUTTONPRESS:
173 case EVENT_BUTTONRELEASE:
174 HandleButtonEvent((ButtonEvent *) &event);
177 case EVENT_MOTIONNOTIFY:
178 HandleMotionEvent((MotionEvent *) &event);
181 #if defined(TARGET_SDL2)
182 case EVENT_WHEELMOTION:
183 HandleWheelEvent((WheelEvent *) &event);
186 case SDL_WINDOWEVENT:
187 HandleWindowEvent((WindowEvent *) &event);
190 case EVENT_FINGERPRESS:
191 case EVENT_FINGERRELEASE:
192 case EVENT_FINGERMOTION:
193 HandleFingerEvent((FingerEvent *) &event);
196 case EVENT_TEXTINPUT:
197 HandleTextEvent((TextEvent *) &event);
200 case SDL_APP_WILLENTERBACKGROUND:
201 case SDL_APP_DIDENTERBACKGROUND:
202 case SDL_APP_WILLENTERFOREGROUND:
203 case SDL_APP_DIDENTERFOREGROUND:
204 HandlePauseResumeEvent((PauseResumeEvent *) &event);
209 case EVENT_KEYRELEASE:
210 HandleKeyEvent((KeyEvent *) &event);
214 HandleOtherEvents(&event);
218 // do not handle events for longer than standard frame delay period
219 if (DelayReached(&event_frame_delay, event_frame_delay_value))
224 void HandleOtherEvents(Event *event)
229 HandleExposeEvent((ExposeEvent *) event);
232 case EVENT_UNMAPNOTIFY:
234 /* This causes the game to stop not only when iconified, but also
235 when on another virtual desktop, which might be not desired. */
236 SleepWhileUnmapped();
242 HandleFocusEvent((FocusChangeEvent *) event);
245 case EVENT_CLIENTMESSAGE:
246 HandleClientMessageEvent((ClientMessageEvent *) event);
249 #if defined(TARGET_SDL)
250 case SDL_JOYAXISMOTION:
251 case SDL_JOYBUTTONDOWN:
252 case SDL_JOYBUTTONUP:
253 HandleJoystickEvent(event);
257 HandleWindowManagerEvent(event);
266 void HandleMouseCursor()
268 if (game_status == GAME_MODE_TITLE)
270 /* when showing title screens, hide mouse pointer (if not moved) */
272 if (gfx.cursor_mode != CURSOR_NONE &&
273 DelayReached(&special_cursor_delay, special_cursor_delay_value))
275 SetMouseCursor(CURSOR_NONE);
278 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
281 /* when playing, display a special mouse pointer inside the playfield */
283 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
284 cursor_inside_playfield &&
285 DelayReached(&special_cursor_delay, special_cursor_delay_value))
287 SetMouseCursor(CURSOR_PLAYFIELD);
290 else if (gfx.cursor_mode != CURSOR_DEFAULT)
292 SetMouseCursor(CURSOR_DEFAULT);
295 /* this is set after all pending events have been processed */
296 cursor_mode_last = gfx.cursor_mode;
308 /* also execute after pending events have been processed before */
311 /* don't use all CPU time when idle; the main loop while playing
312 has its own synchronization and is CPU friendly, too */
314 if (game_status == GAME_MODE_PLAYING)
317 /* always copy backbuffer to visible screen for every video frame */
320 /* reset video frame delay to default (may change again while playing) */
321 SetVideoFrameDelay(MenuFrameDelay);
323 if (game_status == GAME_MODE_QUIT)
328 void ClearEventQueue()
330 while (PendingEvent())
338 case EVENT_BUTTONRELEASE:
339 button_status = MB_RELEASED;
342 case EVENT_KEYRELEASE:
347 HandleOtherEvents(&event);
353 void ClearPlayerAction()
357 /* simulate key release events for still pressed keys */
358 key_joystick_mapping = 0;
359 for (i = 0; i < MAX_PLAYERS; i++)
360 stored_player[i].action = 0;
363 void SleepWhileUnmapped()
365 boolean window_unmapped = TRUE;
367 KeyboardAutoRepeatOn();
369 while (window_unmapped)
377 case EVENT_BUTTONRELEASE:
378 button_status = MB_RELEASED;
381 case EVENT_KEYRELEASE:
382 key_joystick_mapping = 0;
385 case EVENT_MAPNOTIFY:
386 window_unmapped = FALSE;
389 case EVENT_UNMAPNOTIFY:
390 /* this is only to surely prevent the 'should not happen' case
391 * of recursively looping between 'SleepWhileUnmapped()' and
392 * 'HandleOtherEvents()' which usually calls this funtion.
397 HandleOtherEvents(&event);
402 if (game_status == GAME_MODE_PLAYING)
403 KeyboardAutoRepeatOffUnlessAutoplay();
406 void HandleExposeEvent(ExposeEvent *event)
410 void HandleButtonEvent(ButtonEvent *event)
412 #if DEBUG_EVENTS_BUTTON
413 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
415 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
419 #if defined(HAS_SCREEN_KEYBOARD)
420 if (video.shifted_up)
421 event->y += video.shifted_up_pos;
424 motion_status = FALSE;
426 if (event->type == EVENT_BUTTONPRESS)
427 button_status = event->button;
429 button_status = MB_RELEASED;
431 HandleButton(event->x, event->y, button_status, event->button);
434 void HandleMotionEvent(MotionEvent *event)
436 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
439 motion_status = TRUE;
441 #if DEBUG_EVENTS_MOTION
442 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
443 button_status, event->x, event->y);
446 HandleButton(event->x, event->y, button_status, button_status);
449 #if defined(TARGET_SDL2)
451 void HandleWheelEvent(WheelEvent *event)
455 #if DEBUG_EVENTS_WHEEL
457 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
458 event->which, event->x, event->y);
460 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
461 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
462 event->which, event->x, event->y,
463 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
464 "SDL_MOUSEWHEEL_FLIPPED"));
468 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
469 event->x > 0 ? MB_WHEEL_RIGHT :
470 event->y < 0 ? MB_WHEEL_DOWN :
471 event->y > 0 ? MB_WHEEL_UP : 0);
473 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
474 // accelerated mouse wheel available on Mac and Windows
475 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
477 // no accelerated mouse wheel available on Unix/Linux
478 wheel_steps = DEFAULT_WHEEL_STEPS;
481 motion_status = FALSE;
483 button_status = button_nr;
484 HandleButton(0, 0, button_status, -button_nr);
486 button_status = MB_RELEASED;
487 HandleButton(0, 0, button_status, -button_nr);
490 void HandleWindowEvent(WindowEvent *event)
492 #if DEBUG_EVENTS_WINDOW
493 int subtype = event->event;
496 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
497 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
498 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
499 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
500 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
501 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
502 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
503 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
504 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
505 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
506 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
507 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
508 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
509 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
512 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
513 event_name, event->data1, event->data2);
517 // (not needed, as the screen gets redrawn every 20 ms anyway)
518 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
519 event->event == SDL_WINDOWEVENT_RESIZED ||
520 event->event == SDL_WINDOWEVENT_EXPOSED)
524 if (event->event == SDL_WINDOWEVENT_RESIZED)
526 if (!video.fullscreen_enabled)
528 int new_window_width = event->data1;
529 int new_window_height = event->data2;
531 // if window size has changed after resizing, calculate new scaling factor
532 if (new_window_width != video.window_width ||
533 new_window_height != video.window_height)
535 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
536 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
538 // (extreme window scaling allowed, but cannot be saved permanently)
539 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
540 setup.window_scaling_percent =
541 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
542 MAX_WINDOW_SCALING_PERCENT);
544 video.window_width = new_window_width;
545 video.window_height = new_window_height;
547 if (game_status == GAME_MODE_SETUP)
548 RedrawSetupScreenAfterFullscreenToggle();
553 #if defined(PLATFORM_ANDROID)
556 int new_display_width = event->data1;
557 int new_display_height = event->data2;
559 // if fullscreen display size has changed, device has been rotated
560 if (new_display_width != video.display_width ||
561 new_display_height != video.display_height)
563 video.display_width = new_display_width;
564 video.display_height = new_display_height;
566 SDLSetScreenProperties();
573 #define NUM_TOUCH_FINGERS 3
578 SDL_FingerID finger_id;
581 } touch_info[NUM_TOUCH_FINGERS];
583 void HandleFingerEvent(FingerEvent *event)
585 static Key motion_key_x = KSYM_UNDEFINED;
586 static Key motion_key_y = KSYM_UNDEFINED;
587 static Key button_key = KSYM_UNDEFINED;
588 static float motion_x1, motion_y1;
589 static float button_x1, button_y1;
590 static SDL_FingerID motion_id = -1;
591 static SDL_FingerID button_id = -1;
592 int move_trigger_distance_percent = setup.touch.move_distance;
593 int drop_trigger_distance_percent = setup.touch.drop_distance;
594 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
595 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
596 float event_x = event->x;
597 float event_y = event->y;
599 #if DEBUG_EVENTS_FINGER
600 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
601 event->type == EVENT_FINGERPRESS ? "pressed" :
602 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
606 event->dx, event->dy,
610 if (game_status != GAME_MODE_PLAYING)
613 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
616 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
618 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
620 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
622 event_y = (event_y - ypos) / (1 - ypos);
624 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
625 event_y > 2.0 / 3.0 && event_y < 1 ?
626 setup.input[0].key.snap :
627 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
628 event_y > 2.0 / 3.0 && event_y < 1 ?
629 setup.input[0].key.drop :
630 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
631 event_y > 0 && event_y < 1.0 / 3.0 ?
632 setup.input[0].key.up :
633 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
634 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
635 setup.input[0].key.left :
636 event_x > 8.0 / 9.0 && event_x < 1 &&
637 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
638 setup.input[0].key.right :
639 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
640 event_y > 2.0 / 3.0 && event_y < 1 ?
641 setup.input[0].key.down :
644 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
648 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
649 getKeyNameFromKey(key), key_status_name, event->fingerId);
651 // check if we already know this touch event's finger id
652 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
654 if (touch_info[i].touched &&
655 touch_info[i].finger_id == event->fingerId)
657 // Error(ERR_DEBUG, "MARK 1: %d", i);
663 if (i >= NUM_TOUCH_FINGERS)
665 if (key_status == KEY_PRESSED)
667 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
669 // unknown finger id -- get new, empty slot, if available
670 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
672 if (touch_info[i].counter < oldest_counter)
675 oldest_counter = touch_info[i].counter;
677 // Error(ERR_DEBUG, "MARK 2: %d", i);
680 if (!touch_info[i].touched)
682 // Error(ERR_DEBUG, "MARK 3: %d", i);
688 if (i >= NUM_TOUCH_FINGERS)
690 // all slots allocated -- use oldest slot
693 // Error(ERR_DEBUG, "MARK 4: %d", i);
698 // release of previously unknown key (should not happen)
700 if (key != KSYM_UNDEFINED)
702 HandleKey(key, KEY_RELEASED);
704 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
705 getKeyNameFromKey(key), "KEY_RELEASED", i);
710 if (i < NUM_TOUCH_FINGERS)
712 if (key_status == KEY_PRESSED)
714 if (touch_info[i].key != key)
716 if (touch_info[i].key != KSYM_UNDEFINED)
718 HandleKey(touch_info[i].key, KEY_RELEASED);
720 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
721 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
724 if (key != KSYM_UNDEFINED)
726 HandleKey(key, KEY_PRESSED);
728 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
729 getKeyNameFromKey(key), "KEY_PRESSED", i);
733 touch_info[i].touched = TRUE;
734 touch_info[i].finger_id = event->fingerId;
735 touch_info[i].counter = Counter();
736 touch_info[i].key = key;
740 if (touch_info[i].key != KSYM_UNDEFINED)
742 HandleKey(touch_info[i].key, KEY_RELEASED);
744 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
745 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
748 touch_info[i].touched = FALSE;
749 touch_info[i].finger_id = 0;
750 touch_info[i].counter = 0;
751 touch_info[i].key = 0;
758 // use touch direction control
760 if (event->type == EVENT_FINGERPRESS)
762 if (event_x > 1.0 / 3.0)
766 motion_id = event->fingerId;
771 motion_key_x = KSYM_UNDEFINED;
772 motion_key_y = KSYM_UNDEFINED;
774 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
780 button_id = event->fingerId;
785 button_key = setup.input[0].key.snap;
787 HandleKey(button_key, KEY_PRESSED);
789 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
792 else if (event->type == EVENT_FINGERRELEASE)
794 if (event->fingerId == motion_id)
798 if (motion_key_x != KSYM_UNDEFINED)
799 HandleKey(motion_key_x, KEY_RELEASED);
800 if (motion_key_y != KSYM_UNDEFINED)
801 HandleKey(motion_key_y, KEY_RELEASED);
803 motion_key_x = KSYM_UNDEFINED;
804 motion_key_y = KSYM_UNDEFINED;
806 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
808 else if (event->fingerId == button_id)
812 if (button_key != KSYM_UNDEFINED)
813 HandleKey(button_key, KEY_RELEASED);
815 button_key = KSYM_UNDEFINED;
817 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
820 else if (event->type == EVENT_FINGERMOTION)
822 if (event->fingerId == motion_id)
824 float distance_x = ABS(event_x - motion_x1);
825 float distance_y = ABS(event_y - motion_y1);
826 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
827 event_x > motion_x1 ? setup.input[0].key.right :
829 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
830 event_y > motion_y1 ? setup.input[0].key.down :
833 if (distance_x < move_trigger_distance / 2 ||
834 distance_x < distance_y)
835 new_motion_key_x = KSYM_UNDEFINED;
837 if (distance_y < move_trigger_distance / 2 ||
838 distance_y < distance_x)
839 new_motion_key_y = KSYM_UNDEFINED;
841 if (distance_x > move_trigger_distance ||
842 distance_y > move_trigger_distance)
844 if (new_motion_key_x != motion_key_x)
846 if (motion_key_x != KSYM_UNDEFINED)
847 HandleKey(motion_key_x, KEY_RELEASED);
848 if (new_motion_key_x != KSYM_UNDEFINED)
849 HandleKey(new_motion_key_x, KEY_PRESSED);
852 if (new_motion_key_y != motion_key_y)
854 if (motion_key_y != KSYM_UNDEFINED)
855 HandleKey(motion_key_y, KEY_RELEASED);
856 if (new_motion_key_y != KSYM_UNDEFINED)
857 HandleKey(new_motion_key_y, KEY_PRESSED);
863 motion_key_x = new_motion_key_x;
864 motion_key_y = new_motion_key_y;
866 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
869 else if (event->fingerId == button_id)
871 float distance_x = ABS(event_x - button_x1);
872 float distance_y = ABS(event_y - button_y1);
874 if (distance_x < drop_trigger_distance / 2 &&
875 distance_y > drop_trigger_distance)
877 if (button_key == setup.input[0].key.snap)
878 HandleKey(button_key, KEY_RELEASED);
883 button_key = setup.input[0].key.drop;
885 HandleKey(button_key, KEY_PRESSED);
887 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
893 static void HandleFollowFinger(int mx, int my, int button)
895 static int old_mx = 0, old_my = 0;
896 static Key motion_key_x = KSYM_UNDEFINED;
897 static Key motion_key_y = KSYM_UNDEFINED;
898 static boolean started_on_player = FALSE;
899 static boolean player_is_dropping = FALSE;
900 static int player_drop_count = 0;
901 static int last_player_x = -1;
902 static int last_player_y = -1;
904 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
907 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
909 touch_info[0].touched = TRUE;
910 touch_info[0].key = 0;
917 started_on_player = FALSE;
918 player_is_dropping = FALSE;
919 player_drop_count = 0;
923 motion_key_x = KSYM_UNDEFINED;
924 motion_key_y = KSYM_UNDEFINED;
926 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
929 else if (button == MB_RELEASED && touch_info[0].touched)
931 touch_info[0].touched = FALSE;
932 touch_info[0].key = 0;
937 if (motion_key_x != KSYM_UNDEFINED)
938 HandleKey(motion_key_x, KEY_RELEASED);
939 if (motion_key_y != KSYM_UNDEFINED)
940 HandleKey(motion_key_y, KEY_RELEASED);
942 if (started_on_player)
944 if (player_is_dropping)
946 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
948 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
952 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
954 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
958 motion_key_x = KSYM_UNDEFINED;
959 motion_key_y = KSYM_UNDEFINED;
961 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
964 if (touch_info[0].touched)
966 int src_x = local_player->jx;
967 int src_y = local_player->jy;
968 int dst_x = getLevelFromScreenX(old_mx);
969 int dst_y = getLevelFromScreenY(old_my);
970 int dx = dst_x - src_x;
971 int dy = dst_y - src_y;
972 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
973 dx > 0 ? setup.input[0].key.right :
975 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
976 dy > 0 ? setup.input[0].key.down :
979 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
980 (last_player_x != local_player->jx ||
981 last_player_y != local_player->jy))
983 // in case of asymmetric diagonal movement, use "preferred" direction
985 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
987 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
988 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
990 local_player->last_move_dir = last_move_dir;
992 // (required to prevent accidentally forcing direction for next movement)
993 last_player_x = local_player->jx;
994 last_player_y = local_player->jy;
997 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
999 started_on_player = TRUE;
1000 player_drop_count = getPlayerInventorySize(0);
1001 player_is_dropping = (player_drop_count > 0);
1003 if (player_is_dropping)
1005 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1007 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1011 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1013 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1016 else if (dx != 0 || dy != 0)
1018 if (player_is_dropping &&
1019 player_drop_count == getPlayerInventorySize(0))
1021 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1023 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1024 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1026 player_is_dropping = FALSE;
1030 if (new_motion_key_x != motion_key_x)
1032 Error(ERR_DEBUG, "---------- %s %s ----------",
1033 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1034 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1036 if (motion_key_x != KSYM_UNDEFINED)
1037 HandleKey(motion_key_x, KEY_RELEASED);
1038 if (new_motion_key_x != KSYM_UNDEFINED)
1039 HandleKey(new_motion_key_x, KEY_PRESSED);
1042 if (new_motion_key_y != motion_key_y)
1044 Error(ERR_DEBUG, "---------- %s %s ----------",
1045 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1046 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1048 if (motion_key_y != KSYM_UNDEFINED)
1049 HandleKey(motion_key_y, KEY_RELEASED);
1050 if (new_motion_key_y != KSYM_UNDEFINED)
1051 HandleKey(new_motion_key_y, KEY_PRESSED);
1054 motion_key_x = new_motion_key_x;
1055 motion_key_y = new_motion_key_y;
1059 static boolean checkTextInputKeyModState()
1061 // when playing, only handle raw key events and ignore text input
1062 if (game_status == GAME_MODE_PLAYING)
1065 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1068 void HandleTextEvent(TextEvent *event)
1070 char *text = event->text;
1071 Key key = getKeyFromKeyName(text);
1073 #if DEBUG_EVENTS_TEXT
1074 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1077 text[0], (int)(text[0]),
1079 getKeyNameFromKey(key),
1083 #if !defined(HAS_SCREEN_KEYBOARD)
1084 // non-mobile devices: only handle key input with modifier keys pressed here
1085 // (every other key input is handled directly as physical key input event)
1086 if (!checkTextInputKeyModState())
1090 // process text input as "classic" (with uppercase etc.) key input event
1091 HandleKey(key, KEY_PRESSED);
1092 HandleKey(key, KEY_RELEASED);
1095 void HandlePauseResumeEvent(PauseResumeEvent *event)
1097 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1101 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1109 void HandleKeyEvent(KeyEvent *event)
1111 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1112 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1113 Key key = GetEventKey(event, with_modifiers);
1114 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1116 #if DEBUG_EVENTS_KEY
1117 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1118 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1119 event->keysym.scancode,
1124 getKeyNameFromKey(key));
1127 #if defined(PLATFORM_ANDROID)
1128 // always map the "back" button to the "escape" key on Android devices
1129 if (key == KSYM_Back)
1133 HandleKeyModState(keymod, key_status);
1135 #if defined(TARGET_SDL2)
1136 // only handle raw key input without text modifier keys pressed
1137 if (!checkTextInputKeyModState())
1138 HandleKey(key, key_status);
1140 HandleKey(key, key_status);
1144 void HandleFocusEvent(FocusChangeEvent *event)
1146 static int old_joystick_status = -1;
1148 if (event->type == EVENT_FOCUSOUT)
1150 KeyboardAutoRepeatOn();
1151 old_joystick_status = joystick.status;
1152 joystick.status = JOYSTICK_NOT_AVAILABLE;
1154 ClearPlayerAction();
1156 else if (event->type == EVENT_FOCUSIN)
1158 /* When there are two Rocks'n'Diamonds windows which overlap and
1159 the player moves the pointer from one game window to the other,
1160 a 'FocusOut' event is generated for the window the pointer is
1161 leaving and a 'FocusIn' event is generated for the window the
1162 pointer is entering. In some cases, it can happen that the
1163 'FocusIn' event is handled by the one game process before the
1164 'FocusOut' event by the other game process. In this case the
1165 X11 environment would end up with activated keyboard auto repeat,
1166 because unfortunately this is a global setting and not (which
1167 would be far better) set for each X11 window individually.
1168 The effect would be keyboard auto repeat while playing the game
1169 (game_status == GAME_MODE_PLAYING), which is not desired.
1170 To avoid this special case, we just wait 1/10 second before
1171 processing the 'FocusIn' event.
1174 if (game_status == GAME_MODE_PLAYING)
1177 KeyboardAutoRepeatOffUnlessAutoplay();
1180 if (old_joystick_status != -1)
1181 joystick.status = old_joystick_status;
1185 void HandleClientMessageEvent(ClientMessageEvent *event)
1187 if (CheckCloseWindowEvent(event))
1191 void HandleWindowManagerEvent(Event *event)
1193 #if defined(TARGET_SDL)
1194 SDLHandleWindowManagerEvent(event);
1198 void HandleButton(int mx, int my, int button, int button_nr)
1200 static int old_mx = 0, old_my = 0;
1201 boolean button_hold = FALSE;
1207 button_nr = -button_nr;
1216 #if defined(PLATFORM_ANDROID)
1217 // when playing, only handle gadgets when using "follow finger" controls
1218 boolean handle_gadgets =
1219 (game_status != GAME_MODE_PLAYING ||
1220 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1222 if (handle_gadgets &&
1223 HandleGadgets(mx, my, button))
1225 /* do not handle this button event anymore */
1226 mx = my = -32; /* force mouse event to be outside screen tiles */
1229 if (HandleGadgets(mx, my, button))
1231 /* do not handle this button event anymore */
1232 mx = my = -32; /* force mouse event to be outside screen tiles */
1236 if (HandleGlobalAnimClicks(mx, my, button))
1238 /* do not handle this button event anymore */
1239 mx = my = -32; /* force mouse event to be outside screen tiles */
1242 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1245 /* do not use scroll wheel button events for anything other than gadgets */
1246 if (IS_WHEEL_BUTTON(button_nr))
1249 switch (game_status)
1251 case GAME_MODE_TITLE:
1252 HandleTitleScreen(mx, my, 0, 0, button);
1255 case GAME_MODE_MAIN:
1256 HandleMainMenu(mx, my, 0, 0, button);
1259 case GAME_MODE_PSEUDO_TYPENAME:
1260 HandleTypeName(0, KSYM_Return);
1263 case GAME_MODE_LEVELS:
1264 HandleChooseLevelSet(mx, my, 0, 0, button);
1267 case GAME_MODE_LEVELNR:
1268 HandleChooseLevelNr(mx, my, 0, 0, button);
1271 case GAME_MODE_SCORES:
1272 HandleHallOfFame(0, 0, 0, 0, button);
1275 case GAME_MODE_EDITOR:
1276 HandleLevelEditorIdle();
1279 case GAME_MODE_INFO:
1280 HandleInfoScreen(mx, my, 0, 0, button);
1283 case GAME_MODE_SETUP:
1284 HandleSetupScreen(mx, my, 0, 0, button);
1287 #if defined(TARGET_SDL2)
1288 case GAME_MODE_PLAYING:
1289 HandleFollowFinger(mx, my, button);
1293 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1294 GetKeyModState() & KMOD_Control)
1295 DumpTileFromScreen(mx, my);
1305 static boolean is_string_suffix(char *string, char *suffix)
1307 int string_len = strlen(string);
1308 int suffix_len = strlen(suffix);
1310 if (suffix_len > string_len)
1313 return (strEqual(&string[string_len - suffix_len], suffix));
1316 #define MAX_CHEAT_INPUT_LEN 32
1318 static void HandleKeysSpecial(Key key)
1320 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1321 char letter = getCharFromKey(key);
1322 int cheat_input_len = strlen(cheat_input);
1328 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1330 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1331 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1333 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1336 cheat_input[cheat_input_len++] = letter;
1337 cheat_input[cheat_input_len] = '\0';
1339 #if DEBUG_EVENTS_KEY
1340 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1343 if (game_status == GAME_MODE_MAIN)
1345 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1346 is_string_suffix(cheat_input, ":ist"))
1348 InsertSolutionTape();
1350 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1351 is_string_suffix(cheat_input, ":rg"))
1353 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1356 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1357 is_string_suffix(cheat_input, ":rs"))
1359 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1362 else if (is_string_suffix(cheat_input, ":reload-music") ||
1363 is_string_suffix(cheat_input, ":rm"))
1365 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1368 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1369 is_string_suffix(cheat_input, ":ra"))
1371 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1372 1 << ARTWORK_TYPE_SOUNDS |
1373 1 << ARTWORK_TYPE_MUSIC);
1376 else if (is_string_suffix(cheat_input, ":dump-level") ||
1377 is_string_suffix(cheat_input, ":dl"))
1381 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1382 is_string_suffix(cheat_input, ":dt"))
1386 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1387 is_string_suffix(cheat_input, ":ft"))
1389 /* fix single-player tapes that contain player input for more than one
1390 player (due to a bug in 3.3.1.2 and earlier versions), which results
1391 in playing levels with more than one player in multi-player mode,
1392 even though the tape was originally recorded in single-player mode */
1394 /* remove player input actions for all players but the first one */
1395 for (i = 1; i < MAX_PLAYERS; i++)
1396 tape.player_participates[i] = FALSE;
1398 tape.changed = TRUE;
1400 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1401 is_string_suffix(cheat_input, ":snl"))
1403 SaveNativeLevel(&level);
1406 else if (game_status == GAME_MODE_PLAYING)
1409 if (is_string_suffix(cheat_input, ".q"))
1410 DEBUG_SetMaximumDynamite();
1413 else if (game_status == GAME_MODE_EDITOR)
1415 if (is_string_suffix(cheat_input, ":dump-brush") ||
1416 is_string_suffix(cheat_input, ":DB"))
1420 else if (is_string_suffix(cheat_input, ":DDB"))
1427 void HandleKeysDebug(Key key)
1432 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1434 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1436 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1438 if (key == setup.debug.frame_delay_key[i] &&
1439 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1441 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1442 setup.debug.frame_delay[i] : setup.game_frame_delay);
1444 if (!setup.debug.frame_delay_game_only)
1445 MenuFrameDelay = GameFrameDelay;
1447 SetVideoFrameDelay(GameFrameDelay);
1449 if (GameFrameDelay > ONE_SECOND_DELAY)
1450 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1451 else if (GameFrameDelay != 0)
1452 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1453 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1454 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1456 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1463 if (game_status == GAME_MODE_PLAYING)
1467 options.debug = !options.debug;
1469 Error(ERR_DEBUG, "debug mode %s",
1470 (options.debug ? "enabled" : "disabled"));
1472 else if (key == KSYM_v)
1474 Error(ERR_DEBUG, "currently using game engine version %d",
1475 game.engine_version);
1481 void HandleKey(Key key, int key_status)
1483 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1484 static boolean ignore_repeated_key = FALSE;
1485 static struct SetupKeyboardInfo ski;
1486 static struct SetupShortcutInfo ssi;
1495 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1496 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1497 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1498 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1499 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1500 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1505 if (game_status == GAME_MODE_PLAYING)
1507 /* only needed for single-step tape recording mode */
1508 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1509 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1510 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1511 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1514 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1516 byte key_action = 0;
1518 if (setup.input[pnr].use_joystick)
1521 ski = setup.input[pnr].key;
1523 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1524 if (key == *key_info[i].key_custom)
1525 key_action |= key_info[i].action;
1527 /* use combined snap+direction keys for the first player only */
1530 ssi = setup.shortcut;
1532 for (i = 0; i < NUM_DIRECTIONS; i++)
1533 if (key == *key_info[i].key_snap)
1534 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1537 /* clear delayed snap and drop actions in single step mode (see below) */
1538 if (tape.single_step)
1540 if (clear_snap_button[pnr])
1542 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1543 clear_snap_button[pnr] = FALSE;
1546 if (clear_drop_button[pnr])
1548 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1549 clear_drop_button[pnr] = FALSE;
1553 if (key_status == KEY_PRESSED)
1554 stored_player[pnr].action |= key_action;
1556 stored_player[pnr].action &= ~key_action;
1558 if (tape.single_step && tape.recording && tape.pausing)
1560 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1562 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1564 /* if snap key already pressed, don't snap when releasing (below) */
1565 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1566 element_snapped[pnr] = TRUE;
1568 /* if drop key already pressed, don't drop when releasing (below) */
1569 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1570 element_dropped[pnr] = TRUE;
1572 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1574 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1575 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1578 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1579 getRedDiskReleaseFlag_SP() == 0)
1580 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1582 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1585 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1587 if (key_action & KEY_BUTTON_SNAP)
1589 /* if snap key was released without moving (see above), snap now */
1590 if (!element_snapped[pnr])
1592 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1594 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1596 /* clear delayed snap button on next event */
1597 clear_snap_button[pnr] = TRUE;
1600 element_snapped[pnr] = FALSE;
1603 if (key_action & KEY_BUTTON_DROP &&
1604 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1606 /* if drop key was released without moving (see above), drop now */
1607 if (!element_dropped[pnr])
1609 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1611 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1612 getRedDiskReleaseFlag_SP() != 0)
1613 stored_player[pnr].action |= KEY_BUTTON_DROP;
1615 /* clear delayed drop button on next event */
1616 clear_drop_button[pnr] = TRUE;
1619 element_dropped[pnr] = FALSE;
1623 else if (tape.recording && tape.pausing)
1625 /* prevent key release events from un-pausing a paused game */
1626 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1627 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1633 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1634 if (key == key_info[i].key_default)
1635 joy |= key_info[i].action;
1640 if (key_status == KEY_PRESSED)
1641 key_joystick_mapping |= joy;
1643 key_joystick_mapping &= ~joy;
1648 if (game_status != GAME_MODE_PLAYING)
1649 key_joystick_mapping = 0;
1651 if (key_status == KEY_RELEASED)
1653 // reset flag to ignore repeated "key pressed" events after key release
1654 ignore_repeated_key = FALSE;
1659 if ((key == KSYM_F11 ||
1660 ((key == KSYM_Return ||
1661 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1662 video.fullscreen_available &&
1663 !ignore_repeated_key)
1665 setup.fullscreen = !setup.fullscreen;
1667 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1669 if (game_status == GAME_MODE_SETUP)
1670 RedrawSetupScreenAfterFullscreenToggle();
1672 // set flag to ignore repeated "key pressed" events
1673 ignore_repeated_key = TRUE;
1678 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1679 key == KSYM_minus || key == KSYM_KP_Subtract ||
1680 key == KSYM_plus || key == KSYM_KP_Add ||
1681 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1682 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1683 video.window_scaling_available &&
1684 !video.fullscreen_enabled)
1686 if (key == KSYM_0 || key == KSYM_KP_0)
1687 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1688 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1689 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1691 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1693 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1694 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1695 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1696 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1698 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1700 if (game_status == GAME_MODE_SETUP)
1701 RedrawSetupScreenAfterFullscreenToggle();
1706 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1707 key == KSYM_Return ||
1708 key == KSYM_Escape)))
1710 /* do not handle this key event anymore */
1711 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1715 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1716 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1723 if (game_status == GAME_MODE_MAIN &&
1724 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1726 StartGameActions(options.network, setup.autorecord, level.random_seed);
1731 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1733 if (key == setup.shortcut.save_game)
1735 else if (key == setup.shortcut.load_game)
1737 else if (key == setup.shortcut.toggle_pause)
1738 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1740 HandleTapeButtonKeys(key);
1741 HandleSoundButtonKeys(key);
1744 if (game_status == GAME_MODE_PLAYING && !network_playing)
1746 int centered_player_nr_next = -999;
1748 if (key == setup.shortcut.focus_player_all)
1749 centered_player_nr_next = -1;
1751 for (i = 0; i < MAX_PLAYERS; i++)
1752 if (key == setup.shortcut.focus_player[i])
1753 centered_player_nr_next = i;
1755 if (centered_player_nr_next != -999)
1757 game.centered_player_nr_next = centered_player_nr_next;
1758 game.set_centered_player = TRUE;
1762 tape.centered_player_nr_next = game.centered_player_nr_next;
1763 tape.set_centered_player = TRUE;
1768 HandleKeysSpecial(key);
1770 if (HandleGadgetsKeyInput(key))
1772 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1773 key = KSYM_UNDEFINED;
1776 switch (game_status)
1778 case GAME_MODE_PSEUDO_TYPENAME:
1779 HandleTypeName(0, key);
1782 case GAME_MODE_TITLE:
1783 case GAME_MODE_MAIN:
1784 case GAME_MODE_LEVELS:
1785 case GAME_MODE_LEVELNR:
1786 case GAME_MODE_SETUP:
1787 case GAME_MODE_INFO:
1788 case GAME_MODE_SCORES:
1793 if (game_status == GAME_MODE_TITLE)
1794 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1795 else if (game_status == GAME_MODE_MAIN)
1796 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1797 else if (game_status == GAME_MODE_LEVELS)
1798 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1799 else if (game_status == GAME_MODE_LEVELNR)
1800 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1801 else if (game_status == GAME_MODE_SETUP)
1802 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1803 else if (game_status == GAME_MODE_INFO)
1804 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1805 else if (game_status == GAME_MODE_SCORES)
1806 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1810 if (game_status != GAME_MODE_MAIN)
1811 FadeSkipNextFadeIn();
1813 if (game_status == GAME_MODE_TITLE)
1814 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1815 else if (game_status == GAME_MODE_LEVELS)
1816 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1817 else if (game_status == GAME_MODE_LEVELNR)
1818 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1819 else if (game_status == GAME_MODE_SETUP)
1820 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1821 else if (game_status == GAME_MODE_INFO)
1822 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1823 else if (game_status == GAME_MODE_SCORES)
1824 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1828 if (game_status == GAME_MODE_LEVELS)
1829 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1830 else if (game_status == GAME_MODE_LEVELNR)
1831 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1832 else if (game_status == GAME_MODE_SETUP)
1833 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1834 else if (game_status == GAME_MODE_INFO)
1835 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1836 else if (game_status == GAME_MODE_SCORES)
1837 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1840 case KSYM_Page_Down:
1841 if (game_status == GAME_MODE_LEVELS)
1842 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1843 else if (game_status == GAME_MODE_LEVELNR)
1844 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1845 else if (game_status == GAME_MODE_SETUP)
1846 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1847 else if (game_status == GAME_MODE_INFO)
1848 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1849 else if (game_status == GAME_MODE_SCORES)
1850 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1858 case GAME_MODE_EDITOR:
1859 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1860 HandleLevelEditorKeyInput(key);
1863 case GAME_MODE_PLAYING:
1868 RequestQuitGame(setup.ask_on_escape);
1878 if (key == KSYM_Escape)
1880 SetGameStatus(GAME_MODE_MAIN);
1888 HandleKeysDebug(key);
1891 void HandleNoEvent()
1893 // if (button_status && game_status != GAME_MODE_PLAYING)
1894 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1896 HandleButton(0, 0, button_status, -button_status);
1903 #if defined(NETWORK_AVALIABLE)
1904 if (options.network)
1908 switch (game_status)
1910 case GAME_MODE_MAIN:
1911 DrawPreviewLevelAnimation();
1914 case GAME_MODE_EDITOR:
1915 HandleLevelEditorIdle();
1918 #if defined(TARGET_SDL2)
1919 case GAME_MODE_PLAYING:
1920 HandleFollowFinger(-1, -1, -1);
1929 static int HandleJoystickForAllPlayers()
1934 for (i = 0; i < MAX_PLAYERS; i++)
1936 byte joy_action = 0;
1939 if (!setup.input[i].use_joystick)
1943 joy_action = Joystick(i);
1944 result |= joy_action;
1946 if (!setup.input[i].use_joystick)
1949 stored_player[i].action = joy_action;
1955 void HandleJoystick()
1957 int joystick = HandleJoystickForAllPlayers();
1958 int keyboard = key_joystick_mapping;
1959 int joy = (joystick | keyboard);
1960 int left = joy & JOY_LEFT;
1961 int right = joy & JOY_RIGHT;
1962 int up = joy & JOY_UP;
1963 int down = joy & JOY_DOWN;
1964 int button = joy & JOY_BUTTON;
1965 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1966 int dx = (left ? -1 : right ? 1 : 0);
1967 int dy = (up ? -1 : down ? 1 : 0);
1969 if (HandleGlobalAnimClicks(-1, -1, newbutton))
1971 /* do not handle this button event anymore */
1975 switch (game_status)
1977 case GAME_MODE_TITLE:
1978 case GAME_MODE_MAIN:
1979 case GAME_MODE_LEVELS:
1980 case GAME_MODE_LEVELNR:
1981 case GAME_MODE_SETUP:
1982 case GAME_MODE_INFO:
1984 static unsigned int joystickmove_delay = 0;
1986 if (joystick && !button &&
1987 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1988 newbutton = dx = dy = 0;
1990 if (game_status == GAME_MODE_TITLE)
1991 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1992 else if (game_status == GAME_MODE_MAIN)
1993 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1994 else if (game_status == GAME_MODE_LEVELS)
1995 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1996 else if (game_status == GAME_MODE_LEVELNR)
1997 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1998 else if (game_status == GAME_MODE_SETUP)
1999 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2000 else if (game_status == GAME_MODE_INFO)
2001 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2005 case GAME_MODE_SCORES:
2006 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2009 case GAME_MODE_PLAYING:
2010 if (tape.playing || keyboard)
2011 newbutton = ((joy & JOY_BUTTON) != 0);
2013 if (newbutton && AllPlayersGone)