1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_EVENTS 0
28 #define DEBUG_EVENTS_BUTTON (DEBUG_EVENTS * 0)
29 #define DEBUG_EVENTS_MOTION (DEBUG_EVENTS * 0)
30 #define DEBUG_EVENTS_WHEEL (DEBUG_EVENTS * 1)
31 #define DEBUG_EVENTS_WINDOW (DEBUG_EVENTS * 0)
32 #define DEBUG_EVENTS_FINGER (DEBUG_EVENTS * 0)
33 #define DEBUG_EVENTS_TEXT (DEBUG_EVENTS * 1)
34 #define DEBUG_EVENTS_KEY (DEBUG_EVENTS * 1)
37 static boolean cursor_inside_playfield = FALSE;
38 static int cursor_mode_last = CURSOR_DEFAULT;
39 static unsigned int special_cursor_delay = 0;
40 static unsigned int special_cursor_delay_value = 1000;
42 /* event filter especially needed for SDL event filtering due to
43 delay problems with lots of mouse motion events when mouse button
44 not pressed (X11 can handle this with 'PointerMotionHintMask') */
46 /* event filter addition for SDL2: as SDL2 does not have a function to enable
47 or disable keyboard auto-repeat, filter repeated keyboard events instead */
49 static int FilterEvents(const Event *event)
53 #if defined(TARGET_SDL2)
54 /* skip repeated key press events if keyboard auto-repeat is disabled */
55 if (event->type == EVENT_KEYPRESS &&
61 if (event->type == EVENT_BUTTONPRESS ||
62 event->type == EVENT_BUTTONRELEASE)
64 ((ButtonEvent *)event)->x -= video.screen_xoffset;
65 ((ButtonEvent *)event)->y -= video.screen_yoffset;
67 else if (event->type == EVENT_MOTIONNOTIFY)
69 ((MotionEvent *)event)->x -= video.screen_xoffset;
70 ((MotionEvent *)event)->y -= video.screen_yoffset;
73 /* non-motion events are directly passed to event handler functions */
74 if (event->type != EVENT_MOTIONNOTIFY)
77 motion = (MotionEvent *)event;
78 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
79 motion->y >= SY && motion->y < SY + SYSIZE);
81 /* do no reset mouse cursor before all pending events have been processed */
82 if (gfx.cursor_mode == cursor_mode_last &&
83 ((game_status == GAME_MODE_TITLE &&
84 gfx.cursor_mode == CURSOR_NONE) ||
85 (game_status == GAME_MODE_PLAYING &&
86 gfx.cursor_mode == CURSOR_PLAYFIELD)))
88 SetMouseCursor(CURSOR_DEFAULT);
90 DelayReached(&special_cursor_delay, 0);
92 cursor_mode_last = CURSOR_DEFAULT;
95 /* skip mouse motion events without pressed button outside level editor */
96 if (button_status == MB_RELEASED &&
97 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
103 /* to prevent delay problems, skip mouse motion events if the very next
104 event is also a mouse motion event (and therefore effectively only
105 handling the last of a row of mouse motion events in the event queue) */
107 static boolean SkipPressedMouseMotionEvent(const Event *event)
109 /* nothing to do if the current event is not a mouse motion event */
110 if (event->type != EVENT_MOTIONNOTIFY)
113 /* only skip motion events with pressed button outside the game */
114 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
121 PeekEvent(&next_event);
123 /* if next event is also a mouse motion event, skip the current one */
124 if (next_event.type == EVENT_MOTIONNOTIFY)
131 /* this is especially needed for event modifications for the Android target:
132 if mouse coordinates should be modified in the event filter function,
133 using a properly installed SDL event filter does not work, because in
134 the event filter, mouse coordinates in the event structure are still
135 physical pixel positions, not logical (scaled) screen positions, so this
136 has to be handled at a later stage in the event processing functions
137 (when device pixel positions are already converted to screen positions) */
139 boolean NextValidEvent(Event *event)
141 while (PendingEvent())
143 boolean handle_this_event = FALSE;
147 if (FilterEvents(event))
148 handle_this_event = TRUE;
150 if (SkipPressedMouseMotionEvent(event))
151 handle_this_event = FALSE;
153 if (handle_this_event)
163 unsigned int event_frame_delay = 0;
164 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
166 ResetDelayCounter(&event_frame_delay);
168 while (NextValidEvent(&event))
172 case EVENT_BUTTONPRESS:
173 case EVENT_BUTTONRELEASE:
174 HandleButtonEvent((ButtonEvent *) &event);
177 case EVENT_MOTIONNOTIFY:
178 HandleMotionEvent((MotionEvent *) &event);
181 #if defined(TARGET_SDL2)
182 case EVENT_WHEELMOTION:
183 HandleWheelEvent((WheelEvent *) &event);
186 case SDL_WINDOWEVENT:
187 HandleWindowEvent((WindowEvent *) &event);
190 case EVENT_FINGERPRESS:
191 case EVENT_FINGERRELEASE:
192 case EVENT_FINGERMOTION:
193 HandleFingerEvent((FingerEvent *) &event);
196 case EVENT_TEXTINPUT:
197 HandleTextEvent((TextEvent *) &event);
200 case SDL_APP_WILLENTERBACKGROUND:
201 case SDL_APP_DIDENTERBACKGROUND:
202 case SDL_APP_WILLENTERFOREGROUND:
203 case SDL_APP_DIDENTERFOREGROUND:
204 HandlePauseResumeEvent((PauseResumeEvent *) &event);
209 case EVENT_KEYRELEASE:
210 HandleKeyEvent((KeyEvent *) &event);
214 HandleOtherEvents(&event);
218 // do not handle events for longer than standard frame delay period
219 if (DelayReached(&event_frame_delay, event_frame_delay_value))
224 void HandleOtherEvents(Event *event)
229 HandleExposeEvent((ExposeEvent *) event);
232 case EVENT_UNMAPNOTIFY:
234 /* This causes the game to stop not only when iconified, but also
235 when on another virtual desktop, which might be not desired. */
236 SleepWhileUnmapped();
242 HandleFocusEvent((FocusChangeEvent *) event);
245 case EVENT_CLIENTMESSAGE:
246 HandleClientMessageEvent((ClientMessageEvent *) event);
249 #if defined(TARGET_SDL)
250 #if defined(TARGET_SDL2)
251 case SDL_CONTROLLERDEVICEADDED:
252 case SDL_CONTROLLERDEVICEREMOVED:
253 case SDL_CONTROLLERAXISMOTION:
254 case SDL_CONTROLLERBUTTONDOWN:
255 case SDL_CONTROLLERBUTTONUP:
257 case SDL_JOYAXISMOTION:
258 case SDL_JOYBUTTONDOWN:
259 case SDL_JOYBUTTONUP:
260 HandleJoystickEvent(event);
264 HandleWindowManagerEvent(event);
273 void HandleMouseCursor()
275 if (game_status == GAME_MODE_TITLE)
277 /* when showing title screens, hide mouse pointer (if not moved) */
279 if (gfx.cursor_mode != CURSOR_NONE &&
280 DelayReached(&special_cursor_delay, special_cursor_delay_value))
282 SetMouseCursor(CURSOR_NONE);
285 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
288 /* when playing, display a special mouse pointer inside the playfield */
290 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
291 cursor_inside_playfield &&
292 DelayReached(&special_cursor_delay, special_cursor_delay_value))
294 SetMouseCursor(CURSOR_PLAYFIELD);
297 else if (gfx.cursor_mode != CURSOR_DEFAULT)
299 SetMouseCursor(CURSOR_DEFAULT);
302 /* this is set after all pending events have been processed */
303 cursor_mode_last = gfx.cursor_mode;
315 /* also execute after pending events have been processed before */
318 /* don't use all CPU time when idle; the main loop while playing
319 has its own synchronization and is CPU friendly, too */
321 if (game_status == GAME_MODE_PLAYING)
324 /* always copy backbuffer to visible screen for every video frame */
327 /* reset video frame delay to default (may change again while playing) */
328 SetVideoFrameDelay(MenuFrameDelay);
330 if (game_status == GAME_MODE_QUIT)
335 void ClearEventQueue()
337 while (PendingEvent())
345 case EVENT_BUTTONRELEASE:
346 button_status = MB_RELEASED;
349 case EVENT_KEYRELEASE:
354 HandleOtherEvents(&event);
360 void ClearPlayerAction()
364 /* simulate key release events for still pressed keys */
365 key_joystick_mapping = 0;
366 for (i = 0; i < MAX_PLAYERS; i++)
367 stored_player[i].action = 0;
370 void SleepWhileUnmapped()
372 boolean window_unmapped = TRUE;
374 KeyboardAutoRepeatOn();
376 while (window_unmapped)
384 case EVENT_BUTTONRELEASE:
385 button_status = MB_RELEASED;
388 case EVENT_KEYRELEASE:
389 key_joystick_mapping = 0;
392 case EVENT_MAPNOTIFY:
393 window_unmapped = FALSE;
396 case EVENT_UNMAPNOTIFY:
397 /* this is only to surely prevent the 'should not happen' case
398 * of recursively looping between 'SleepWhileUnmapped()' and
399 * 'HandleOtherEvents()' which usually calls this funtion.
404 HandleOtherEvents(&event);
409 if (game_status == GAME_MODE_PLAYING)
410 KeyboardAutoRepeatOffUnlessAutoplay();
413 void HandleExposeEvent(ExposeEvent *event)
417 void HandleButtonEvent(ButtonEvent *event)
419 #if DEBUG_EVENTS_BUTTON
420 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
422 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
426 #if defined(HAS_SCREEN_KEYBOARD)
427 if (video.shifted_up)
428 event->y += video.shifted_up_pos;
431 motion_status = FALSE;
433 if (event->type == EVENT_BUTTONPRESS)
434 button_status = event->button;
436 button_status = MB_RELEASED;
438 HandleButton(event->x, event->y, button_status, event->button);
441 void HandleMotionEvent(MotionEvent *event)
443 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
446 motion_status = TRUE;
448 #if DEBUG_EVENTS_MOTION
449 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
450 button_status, event->x, event->y);
453 HandleButton(event->x, event->y, button_status, button_status);
456 #if defined(TARGET_SDL2)
458 void HandleWheelEvent(WheelEvent *event)
462 #if DEBUG_EVENTS_WHEEL
464 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
465 event->which, event->x, event->y);
467 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
468 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
469 event->which, event->x, event->y,
470 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
471 "SDL_MOUSEWHEEL_FLIPPED"));
475 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
476 event->x > 0 ? MB_WHEEL_RIGHT :
477 event->y < 0 ? MB_WHEEL_DOWN :
478 event->y > 0 ? MB_WHEEL_UP : 0);
480 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
481 // accelerated mouse wheel available on Mac and Windows
482 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
484 // no accelerated mouse wheel available on Unix/Linux
485 wheel_steps = DEFAULT_WHEEL_STEPS;
488 motion_status = FALSE;
490 button_status = button_nr;
491 HandleButton(0, 0, button_status, -button_nr);
493 button_status = MB_RELEASED;
494 HandleButton(0, 0, button_status, -button_nr);
497 void HandleWindowEvent(WindowEvent *event)
499 #if DEBUG_EVENTS_WINDOW
500 int subtype = event->event;
503 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
504 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
505 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
506 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
507 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
508 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
509 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
510 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
511 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
512 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
513 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
514 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
515 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
516 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
519 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
520 event_name, event->data1, event->data2);
524 // (not needed, as the screen gets redrawn every 20 ms anyway)
525 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
526 event->event == SDL_WINDOWEVENT_RESIZED ||
527 event->event == SDL_WINDOWEVENT_EXPOSED)
531 if (event->event == SDL_WINDOWEVENT_RESIZED)
533 if (!video.fullscreen_enabled)
535 int new_window_width = event->data1;
536 int new_window_height = event->data2;
538 // if window size has changed after resizing, calculate new scaling factor
539 if (new_window_width != video.window_width ||
540 new_window_height != video.window_height)
542 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
543 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
545 // (extreme window scaling allowed, but cannot be saved permanently)
546 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
547 setup.window_scaling_percent =
548 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
549 MAX_WINDOW_SCALING_PERCENT);
551 video.window_width = new_window_width;
552 video.window_height = new_window_height;
554 if (game_status == GAME_MODE_SETUP)
555 RedrawSetupScreenAfterFullscreenToggle();
560 #if defined(PLATFORM_ANDROID)
563 int new_display_width = event->data1;
564 int new_display_height = event->data2;
566 // if fullscreen display size has changed, device has been rotated
567 if (new_display_width != video.display_width ||
568 new_display_height != video.display_height)
570 video.display_width = new_display_width;
571 video.display_height = new_display_height;
573 SDLSetScreenProperties();
580 #define NUM_TOUCH_FINGERS 3
585 SDL_FingerID finger_id;
588 } touch_info[NUM_TOUCH_FINGERS];
590 void HandleFingerEvent(FingerEvent *event)
592 static Key motion_key_x = KSYM_UNDEFINED;
593 static Key motion_key_y = KSYM_UNDEFINED;
594 static Key button_key = KSYM_UNDEFINED;
595 static float motion_x1, motion_y1;
596 static float button_x1, button_y1;
597 static SDL_FingerID motion_id = -1;
598 static SDL_FingerID button_id = -1;
599 int move_trigger_distance_percent = setup.touch.move_distance;
600 int drop_trigger_distance_percent = setup.touch.drop_distance;
601 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
602 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
603 float event_x = event->x;
604 float event_y = event->y;
606 #if DEBUG_EVENTS_FINGER
607 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
608 event->type == EVENT_FINGERPRESS ? "pressed" :
609 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
613 event->dx, event->dy,
617 if (game_status != GAME_MODE_PLAYING)
620 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
623 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
625 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
627 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
629 event_y = (event_y - ypos) / (1 - ypos);
631 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
632 event_y > 2.0 / 3.0 && event_y < 1 ?
633 setup.input[0].key.snap :
634 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
635 event_y > 2.0 / 3.0 && event_y < 1 ?
636 setup.input[0].key.drop :
637 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
638 event_y > 0 && event_y < 1.0 / 3.0 ?
639 setup.input[0].key.up :
640 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
641 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
642 setup.input[0].key.left :
643 event_x > 8.0 / 9.0 && event_x < 1 &&
644 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
645 setup.input[0].key.right :
646 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
647 event_y > 2.0 / 3.0 && event_y < 1 ?
648 setup.input[0].key.down :
651 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
655 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
656 getKeyNameFromKey(key), key_status_name, event->fingerId);
658 // check if we already know this touch event's finger id
659 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
661 if (touch_info[i].touched &&
662 touch_info[i].finger_id == event->fingerId)
664 // Error(ERR_DEBUG, "MARK 1: %d", i);
670 if (i >= NUM_TOUCH_FINGERS)
672 if (key_status == KEY_PRESSED)
674 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
676 // unknown finger id -- get new, empty slot, if available
677 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
679 if (touch_info[i].counter < oldest_counter)
682 oldest_counter = touch_info[i].counter;
684 // Error(ERR_DEBUG, "MARK 2: %d", i);
687 if (!touch_info[i].touched)
689 // Error(ERR_DEBUG, "MARK 3: %d", i);
695 if (i >= NUM_TOUCH_FINGERS)
697 // all slots allocated -- use oldest slot
700 // Error(ERR_DEBUG, "MARK 4: %d", i);
705 // release of previously unknown key (should not happen)
707 if (key != KSYM_UNDEFINED)
709 HandleKey(key, KEY_RELEASED);
711 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
712 getKeyNameFromKey(key), "KEY_RELEASED", i);
717 if (i < NUM_TOUCH_FINGERS)
719 if (key_status == KEY_PRESSED)
721 if (touch_info[i].key != key)
723 if (touch_info[i].key != KSYM_UNDEFINED)
725 HandleKey(touch_info[i].key, KEY_RELEASED);
727 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
728 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
731 if (key != KSYM_UNDEFINED)
733 HandleKey(key, KEY_PRESSED);
735 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
736 getKeyNameFromKey(key), "KEY_PRESSED", i);
740 touch_info[i].touched = TRUE;
741 touch_info[i].finger_id = event->fingerId;
742 touch_info[i].counter = Counter();
743 touch_info[i].key = key;
747 if (touch_info[i].key != KSYM_UNDEFINED)
749 HandleKey(touch_info[i].key, KEY_RELEASED);
751 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
752 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
755 touch_info[i].touched = FALSE;
756 touch_info[i].finger_id = 0;
757 touch_info[i].counter = 0;
758 touch_info[i].key = 0;
765 // use touch direction control
767 if (event->type == EVENT_FINGERPRESS)
769 if (event_x > 1.0 / 3.0)
773 motion_id = event->fingerId;
778 motion_key_x = KSYM_UNDEFINED;
779 motion_key_y = KSYM_UNDEFINED;
781 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
787 button_id = event->fingerId;
792 button_key = setup.input[0].key.snap;
794 HandleKey(button_key, KEY_PRESSED);
796 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
799 else if (event->type == EVENT_FINGERRELEASE)
801 if (event->fingerId == motion_id)
805 if (motion_key_x != KSYM_UNDEFINED)
806 HandleKey(motion_key_x, KEY_RELEASED);
807 if (motion_key_y != KSYM_UNDEFINED)
808 HandleKey(motion_key_y, KEY_RELEASED);
810 motion_key_x = KSYM_UNDEFINED;
811 motion_key_y = KSYM_UNDEFINED;
813 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
815 else if (event->fingerId == button_id)
819 if (button_key != KSYM_UNDEFINED)
820 HandleKey(button_key, KEY_RELEASED);
822 button_key = KSYM_UNDEFINED;
824 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
827 else if (event->type == EVENT_FINGERMOTION)
829 if (event->fingerId == motion_id)
831 float distance_x = ABS(event_x - motion_x1);
832 float distance_y = ABS(event_y - motion_y1);
833 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
834 event_x > motion_x1 ? setup.input[0].key.right :
836 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
837 event_y > motion_y1 ? setup.input[0].key.down :
840 if (distance_x < move_trigger_distance / 2 ||
841 distance_x < distance_y)
842 new_motion_key_x = KSYM_UNDEFINED;
844 if (distance_y < move_trigger_distance / 2 ||
845 distance_y < distance_x)
846 new_motion_key_y = KSYM_UNDEFINED;
848 if (distance_x > move_trigger_distance ||
849 distance_y > move_trigger_distance)
851 if (new_motion_key_x != motion_key_x)
853 if (motion_key_x != KSYM_UNDEFINED)
854 HandleKey(motion_key_x, KEY_RELEASED);
855 if (new_motion_key_x != KSYM_UNDEFINED)
856 HandleKey(new_motion_key_x, KEY_PRESSED);
859 if (new_motion_key_y != motion_key_y)
861 if (motion_key_y != KSYM_UNDEFINED)
862 HandleKey(motion_key_y, KEY_RELEASED);
863 if (new_motion_key_y != KSYM_UNDEFINED)
864 HandleKey(new_motion_key_y, KEY_PRESSED);
870 motion_key_x = new_motion_key_x;
871 motion_key_y = new_motion_key_y;
873 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
876 else if (event->fingerId == button_id)
878 float distance_x = ABS(event_x - button_x1);
879 float distance_y = ABS(event_y - button_y1);
881 if (distance_x < drop_trigger_distance / 2 &&
882 distance_y > drop_trigger_distance)
884 if (button_key == setup.input[0].key.snap)
885 HandleKey(button_key, KEY_RELEASED);
890 button_key = setup.input[0].key.drop;
892 HandleKey(button_key, KEY_PRESSED);
894 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
900 static void HandleFollowFinger(int mx, int my, int button)
902 static int old_mx = 0, old_my = 0;
903 static Key motion_key_x = KSYM_UNDEFINED;
904 static Key motion_key_y = KSYM_UNDEFINED;
905 static boolean started_on_player = FALSE;
906 static boolean player_is_dropping = FALSE;
907 static int player_drop_count = 0;
908 static int last_player_x = -1;
909 static int last_player_y = -1;
911 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
914 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
916 touch_info[0].touched = TRUE;
917 touch_info[0].key = 0;
924 started_on_player = FALSE;
925 player_is_dropping = FALSE;
926 player_drop_count = 0;
930 motion_key_x = KSYM_UNDEFINED;
931 motion_key_y = KSYM_UNDEFINED;
933 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
936 else if (button == MB_RELEASED && touch_info[0].touched)
938 touch_info[0].touched = FALSE;
939 touch_info[0].key = 0;
944 if (motion_key_x != KSYM_UNDEFINED)
945 HandleKey(motion_key_x, KEY_RELEASED);
946 if (motion_key_y != KSYM_UNDEFINED)
947 HandleKey(motion_key_y, KEY_RELEASED);
949 if (started_on_player)
951 if (player_is_dropping)
953 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
955 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
959 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
961 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
965 motion_key_x = KSYM_UNDEFINED;
966 motion_key_y = KSYM_UNDEFINED;
968 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
971 if (touch_info[0].touched)
973 int src_x = local_player->jx;
974 int src_y = local_player->jy;
975 int dst_x = getLevelFromScreenX(old_mx);
976 int dst_y = getLevelFromScreenY(old_my);
977 int dx = dst_x - src_x;
978 int dy = dst_y - src_y;
979 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
980 dx > 0 ? setup.input[0].key.right :
982 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
983 dy > 0 ? setup.input[0].key.down :
986 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
987 (last_player_x != local_player->jx ||
988 last_player_y != local_player->jy))
990 // in case of asymmetric diagonal movement, use "preferred" direction
992 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
994 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
995 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
997 local_player->last_move_dir = last_move_dir;
999 // (required to prevent accidentally forcing direction for next movement)
1000 last_player_x = local_player->jx;
1001 last_player_y = local_player->jy;
1004 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1006 started_on_player = TRUE;
1007 player_drop_count = getPlayerInventorySize(0);
1008 player_is_dropping = (player_drop_count > 0);
1010 if (player_is_dropping)
1012 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1014 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1018 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1020 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1023 else if (dx != 0 || dy != 0)
1025 if (player_is_dropping &&
1026 player_drop_count == getPlayerInventorySize(0))
1028 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1030 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1031 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1033 player_is_dropping = FALSE;
1037 if (new_motion_key_x != motion_key_x)
1039 Error(ERR_DEBUG, "---------- %s %s ----------",
1040 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1041 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1043 if (motion_key_x != KSYM_UNDEFINED)
1044 HandleKey(motion_key_x, KEY_RELEASED);
1045 if (new_motion_key_x != KSYM_UNDEFINED)
1046 HandleKey(new_motion_key_x, KEY_PRESSED);
1049 if (new_motion_key_y != motion_key_y)
1051 Error(ERR_DEBUG, "---------- %s %s ----------",
1052 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1053 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1055 if (motion_key_y != KSYM_UNDEFINED)
1056 HandleKey(motion_key_y, KEY_RELEASED);
1057 if (new_motion_key_y != KSYM_UNDEFINED)
1058 HandleKey(new_motion_key_y, KEY_PRESSED);
1061 motion_key_x = new_motion_key_x;
1062 motion_key_y = new_motion_key_y;
1066 static boolean checkTextInputKeyModState()
1068 // when playing, only handle raw key events and ignore text input
1069 if (game_status == GAME_MODE_PLAYING)
1072 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1075 void HandleTextEvent(TextEvent *event)
1077 char *text = event->text;
1078 Key key = getKeyFromKeyName(text);
1080 #if DEBUG_EVENTS_TEXT
1081 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1084 text[0], (int)(text[0]),
1086 getKeyNameFromKey(key),
1090 #if !defined(HAS_SCREEN_KEYBOARD)
1091 // non-mobile devices: only handle key input with modifier keys pressed here
1092 // (every other key input is handled directly as physical key input event)
1093 if (!checkTextInputKeyModState())
1097 // process text input as "classic" (with uppercase etc.) key input event
1098 HandleKey(key, KEY_PRESSED);
1099 HandleKey(key, KEY_RELEASED);
1102 void HandlePauseResumeEvent(PauseResumeEvent *event)
1104 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1108 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1116 void HandleKeyEvent(KeyEvent *event)
1118 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1119 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1120 Key key = GetEventKey(event, with_modifiers);
1121 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1123 #if DEBUG_EVENTS_KEY
1124 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1125 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1126 event->keysym.scancode,
1131 getKeyNameFromKey(key));
1134 #if defined(PLATFORM_ANDROID)
1135 // always map the "back" button to the "escape" key on Android devices
1136 if (key == KSYM_Back)
1140 HandleKeyModState(keymod, key_status);
1142 #if defined(TARGET_SDL2)
1143 // only handle raw key input without text modifier keys pressed
1144 if (!checkTextInputKeyModState())
1145 HandleKey(key, key_status);
1147 HandleKey(key, key_status);
1151 void HandleFocusEvent(FocusChangeEvent *event)
1153 static int old_joystick_status = -1;
1155 if (event->type == EVENT_FOCUSOUT)
1157 KeyboardAutoRepeatOn();
1158 old_joystick_status = joystick.status;
1159 joystick.status = JOYSTICK_NOT_AVAILABLE;
1161 ClearPlayerAction();
1163 else if (event->type == EVENT_FOCUSIN)
1165 /* When there are two Rocks'n'Diamonds windows which overlap and
1166 the player moves the pointer from one game window to the other,
1167 a 'FocusOut' event is generated for the window the pointer is
1168 leaving and a 'FocusIn' event is generated for the window the
1169 pointer is entering. In some cases, it can happen that the
1170 'FocusIn' event is handled by the one game process before the
1171 'FocusOut' event by the other game process. In this case the
1172 X11 environment would end up with activated keyboard auto repeat,
1173 because unfortunately this is a global setting and not (which
1174 would be far better) set for each X11 window individually.
1175 The effect would be keyboard auto repeat while playing the game
1176 (game_status == GAME_MODE_PLAYING), which is not desired.
1177 To avoid this special case, we just wait 1/10 second before
1178 processing the 'FocusIn' event.
1181 if (game_status == GAME_MODE_PLAYING)
1184 KeyboardAutoRepeatOffUnlessAutoplay();
1187 if (old_joystick_status != -1)
1188 joystick.status = old_joystick_status;
1192 void HandleClientMessageEvent(ClientMessageEvent *event)
1194 if (CheckCloseWindowEvent(event))
1198 void HandleWindowManagerEvent(Event *event)
1200 #if defined(TARGET_SDL)
1201 SDLHandleWindowManagerEvent(event);
1205 void HandleButton(int mx, int my, int button, int button_nr)
1207 static int old_mx = 0, old_my = 0;
1208 boolean button_hold = FALSE;
1214 button_nr = -button_nr;
1223 #if defined(PLATFORM_ANDROID)
1224 // when playing, only handle gadgets when using "follow finger" controls
1225 boolean handle_gadgets =
1226 (game_status != GAME_MODE_PLAYING ||
1227 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1229 if (handle_gadgets &&
1230 HandleGadgets(mx, my, button))
1232 /* do not handle this button event anymore */
1233 mx = my = -32; /* force mouse event to be outside screen tiles */
1236 if (HandleGadgets(mx, my, button))
1238 /* do not handle this button event anymore */
1239 mx = my = -32; /* force mouse event to be outside screen tiles */
1243 if (HandleGlobalAnimClicks(mx, my, button))
1245 /* do not handle this button event anymore */
1246 mx = my = -32; /* force mouse event to be outside screen tiles */
1249 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1252 /* do not use scroll wheel button events for anything other than gadgets */
1253 if (IS_WHEEL_BUTTON(button_nr))
1256 switch (game_status)
1258 case GAME_MODE_TITLE:
1259 HandleTitleScreen(mx, my, 0, 0, button);
1262 case GAME_MODE_MAIN:
1263 HandleMainMenu(mx, my, 0, 0, button);
1266 case GAME_MODE_PSEUDO_TYPENAME:
1267 HandleTypeName(0, KSYM_Return);
1270 case GAME_MODE_LEVELS:
1271 HandleChooseLevelSet(mx, my, 0, 0, button);
1274 case GAME_MODE_LEVELNR:
1275 HandleChooseLevelNr(mx, my, 0, 0, button);
1278 case GAME_MODE_SCORES:
1279 HandleHallOfFame(0, 0, 0, 0, button);
1282 case GAME_MODE_EDITOR:
1283 HandleLevelEditorIdle();
1286 case GAME_MODE_INFO:
1287 HandleInfoScreen(mx, my, 0, 0, button);
1290 case GAME_MODE_SETUP:
1291 HandleSetupScreen(mx, my, 0, 0, button);
1294 #if defined(TARGET_SDL2)
1295 case GAME_MODE_PLAYING:
1296 HandleFollowFinger(mx, my, button);
1300 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1301 GetKeyModState() & KMOD_Control)
1302 DumpTileFromScreen(mx, my);
1312 static boolean is_string_suffix(char *string, char *suffix)
1314 int string_len = strlen(string);
1315 int suffix_len = strlen(suffix);
1317 if (suffix_len > string_len)
1320 return (strEqual(&string[string_len - suffix_len], suffix));
1323 #define MAX_CHEAT_INPUT_LEN 32
1325 static void HandleKeysSpecial(Key key)
1327 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1328 char letter = getCharFromKey(key);
1329 int cheat_input_len = strlen(cheat_input);
1335 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1337 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1338 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1340 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1343 cheat_input[cheat_input_len++] = letter;
1344 cheat_input[cheat_input_len] = '\0';
1346 #if DEBUG_EVENTS_KEY
1347 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1350 if (game_status == GAME_MODE_MAIN)
1352 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1353 is_string_suffix(cheat_input, ":ist"))
1355 InsertSolutionTape();
1357 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1358 is_string_suffix(cheat_input, ":rg"))
1360 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1363 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1364 is_string_suffix(cheat_input, ":rs"))
1366 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1369 else if (is_string_suffix(cheat_input, ":reload-music") ||
1370 is_string_suffix(cheat_input, ":rm"))
1372 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1375 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1376 is_string_suffix(cheat_input, ":ra"))
1378 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1379 1 << ARTWORK_TYPE_SOUNDS |
1380 1 << ARTWORK_TYPE_MUSIC);
1383 else if (is_string_suffix(cheat_input, ":dump-level") ||
1384 is_string_suffix(cheat_input, ":dl"))
1388 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1389 is_string_suffix(cheat_input, ":dt"))
1393 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1394 is_string_suffix(cheat_input, ":ft"))
1396 /* fix single-player tapes that contain player input for more than one
1397 player (due to a bug in 3.3.1.2 and earlier versions), which results
1398 in playing levels with more than one player in multi-player mode,
1399 even though the tape was originally recorded in single-player mode */
1401 /* remove player input actions for all players but the first one */
1402 for (i = 1; i < MAX_PLAYERS; i++)
1403 tape.player_participates[i] = FALSE;
1405 tape.changed = TRUE;
1407 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1408 is_string_suffix(cheat_input, ":snl"))
1410 SaveNativeLevel(&level);
1413 else if (game_status == GAME_MODE_PLAYING)
1416 if (is_string_suffix(cheat_input, ".q"))
1417 DEBUG_SetMaximumDynamite();
1420 else if (game_status == GAME_MODE_EDITOR)
1422 if (is_string_suffix(cheat_input, ":dump-brush") ||
1423 is_string_suffix(cheat_input, ":DB"))
1427 else if (is_string_suffix(cheat_input, ":DDB"))
1434 void HandleKeysDebug(Key key)
1439 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1441 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1443 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1445 if (key == setup.debug.frame_delay_key[i] &&
1446 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1448 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1449 setup.debug.frame_delay[i] : setup.game_frame_delay);
1451 if (!setup.debug.frame_delay_game_only)
1452 MenuFrameDelay = GameFrameDelay;
1454 SetVideoFrameDelay(GameFrameDelay);
1456 if (GameFrameDelay > ONE_SECOND_DELAY)
1457 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1458 else if (GameFrameDelay != 0)
1459 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1460 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1461 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1463 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1470 if (game_status == GAME_MODE_PLAYING)
1474 options.debug = !options.debug;
1476 Error(ERR_DEBUG, "debug mode %s",
1477 (options.debug ? "enabled" : "disabled"));
1479 else if (key == KSYM_v)
1481 Error(ERR_DEBUG, "currently using game engine version %d",
1482 game.engine_version);
1488 void HandleKey(Key key, int key_status)
1490 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1491 static boolean ignore_repeated_key = FALSE;
1492 static struct SetupKeyboardInfo ski;
1493 static struct SetupShortcutInfo ssi;
1502 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1503 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1504 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1505 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1506 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1507 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1512 if (game_status == GAME_MODE_PLAYING)
1514 /* only needed for single-step tape recording mode */
1515 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1516 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1517 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1518 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1521 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1523 byte key_action = 0;
1525 if (setup.input[pnr].use_joystick)
1528 ski = setup.input[pnr].key;
1530 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1531 if (key == *key_info[i].key_custom)
1532 key_action |= key_info[i].action;
1534 /* use combined snap+direction keys for the first player only */
1537 ssi = setup.shortcut;
1539 for (i = 0; i < NUM_DIRECTIONS; i++)
1540 if (key == *key_info[i].key_snap)
1541 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1544 /* clear delayed snap and drop actions in single step mode (see below) */
1545 if (tape.single_step)
1547 if (clear_snap_button[pnr])
1549 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1550 clear_snap_button[pnr] = FALSE;
1553 if (clear_drop_button[pnr])
1555 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1556 clear_drop_button[pnr] = FALSE;
1560 if (key_status == KEY_PRESSED)
1561 stored_player[pnr].action |= key_action;
1563 stored_player[pnr].action &= ~key_action;
1565 if (tape.single_step && tape.recording && tape.pausing)
1567 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1569 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1571 /* if snap key already pressed, don't snap when releasing (below) */
1572 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1573 element_snapped[pnr] = TRUE;
1575 /* if drop key already pressed, don't drop when releasing (below) */
1576 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1577 element_dropped[pnr] = TRUE;
1579 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1581 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1582 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1585 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1586 getRedDiskReleaseFlag_SP() == 0)
1587 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1589 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1592 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1594 if (key_action & KEY_BUTTON_SNAP)
1596 /* if snap key was released without moving (see above), snap now */
1597 if (!element_snapped[pnr])
1599 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1601 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1603 /* clear delayed snap button on next event */
1604 clear_snap_button[pnr] = TRUE;
1607 element_snapped[pnr] = FALSE;
1610 if (key_action & KEY_BUTTON_DROP &&
1611 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1613 /* if drop key was released without moving (see above), drop now */
1614 if (!element_dropped[pnr])
1616 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1618 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1619 getRedDiskReleaseFlag_SP() != 0)
1620 stored_player[pnr].action |= KEY_BUTTON_DROP;
1622 /* clear delayed drop button on next event */
1623 clear_drop_button[pnr] = TRUE;
1626 element_dropped[pnr] = FALSE;
1630 else if (tape.recording && tape.pausing)
1632 /* prevent key release events from un-pausing a paused game */
1633 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1634 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1640 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1641 if (key == key_info[i].key_default)
1642 joy |= key_info[i].action;
1647 if (key_status == KEY_PRESSED)
1648 key_joystick_mapping |= joy;
1650 key_joystick_mapping &= ~joy;
1655 if (game_status != GAME_MODE_PLAYING)
1656 key_joystick_mapping = 0;
1658 if (key_status == KEY_RELEASED)
1660 // reset flag to ignore repeated "key pressed" events after key release
1661 ignore_repeated_key = FALSE;
1666 if ((key == KSYM_F11 ||
1667 ((key == KSYM_Return ||
1668 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1669 video.fullscreen_available &&
1670 !ignore_repeated_key)
1672 setup.fullscreen = !setup.fullscreen;
1674 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1676 if (game_status == GAME_MODE_SETUP)
1677 RedrawSetupScreenAfterFullscreenToggle();
1679 // set flag to ignore repeated "key pressed" events
1680 ignore_repeated_key = TRUE;
1685 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1686 key == KSYM_minus || key == KSYM_KP_Subtract ||
1687 key == KSYM_plus || key == KSYM_KP_Add ||
1688 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1689 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1690 video.window_scaling_available &&
1691 !video.fullscreen_enabled)
1693 if (key == KSYM_0 || key == KSYM_KP_0)
1694 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1695 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1696 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1698 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1700 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1701 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1702 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1703 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1705 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1707 if (game_status == GAME_MODE_SETUP)
1708 RedrawSetupScreenAfterFullscreenToggle();
1713 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1714 key == KSYM_Return ||
1715 key == KSYM_Escape)))
1717 /* do not handle this key event anymore */
1718 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1722 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1723 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1730 if (game_status == GAME_MODE_MAIN &&
1731 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1733 StartGameActions(options.network, setup.autorecord, level.random_seed);
1738 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1740 if (key == setup.shortcut.save_game)
1742 else if (key == setup.shortcut.load_game)
1744 else if (key == setup.shortcut.toggle_pause)
1745 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1747 HandleTapeButtonKeys(key);
1748 HandleSoundButtonKeys(key);
1751 if (game_status == GAME_MODE_PLAYING && !network_playing)
1753 int centered_player_nr_next = -999;
1755 if (key == setup.shortcut.focus_player_all)
1756 centered_player_nr_next = -1;
1758 for (i = 0; i < MAX_PLAYERS; i++)
1759 if (key == setup.shortcut.focus_player[i])
1760 centered_player_nr_next = i;
1762 if (centered_player_nr_next != -999)
1764 game.centered_player_nr_next = centered_player_nr_next;
1765 game.set_centered_player = TRUE;
1769 tape.centered_player_nr_next = game.centered_player_nr_next;
1770 tape.set_centered_player = TRUE;
1775 HandleKeysSpecial(key);
1777 if (HandleGadgetsKeyInput(key))
1779 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1780 key = KSYM_UNDEFINED;
1783 switch (game_status)
1785 case GAME_MODE_PSEUDO_TYPENAME:
1786 HandleTypeName(0, key);
1789 case GAME_MODE_TITLE:
1790 case GAME_MODE_MAIN:
1791 case GAME_MODE_LEVELS:
1792 case GAME_MODE_LEVELNR:
1793 case GAME_MODE_SETUP:
1794 case GAME_MODE_INFO:
1795 case GAME_MODE_SCORES:
1800 if (game_status == GAME_MODE_TITLE)
1801 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1802 else if (game_status == GAME_MODE_MAIN)
1803 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1804 else if (game_status == GAME_MODE_LEVELS)
1805 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1806 else if (game_status == GAME_MODE_LEVELNR)
1807 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1808 else if (game_status == GAME_MODE_SETUP)
1809 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1810 else if (game_status == GAME_MODE_INFO)
1811 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1812 else if (game_status == GAME_MODE_SCORES)
1813 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1817 if (game_status != GAME_MODE_MAIN)
1818 FadeSkipNextFadeIn();
1820 if (game_status == GAME_MODE_TITLE)
1821 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1822 else if (game_status == GAME_MODE_LEVELS)
1823 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1824 else if (game_status == GAME_MODE_LEVELNR)
1825 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1826 else if (game_status == GAME_MODE_SETUP)
1827 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1828 else if (game_status == GAME_MODE_INFO)
1829 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1830 else if (game_status == GAME_MODE_SCORES)
1831 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1835 if (game_status == GAME_MODE_LEVELS)
1836 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1837 else if (game_status == GAME_MODE_LEVELNR)
1838 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1839 else if (game_status == GAME_MODE_SETUP)
1840 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1841 else if (game_status == GAME_MODE_INFO)
1842 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1843 else if (game_status == GAME_MODE_SCORES)
1844 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1847 case KSYM_Page_Down:
1848 if (game_status == GAME_MODE_LEVELS)
1849 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1850 else if (game_status == GAME_MODE_LEVELNR)
1851 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1852 else if (game_status == GAME_MODE_SETUP)
1853 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1854 else if (game_status == GAME_MODE_INFO)
1855 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1856 else if (game_status == GAME_MODE_SCORES)
1857 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1865 case GAME_MODE_EDITOR:
1866 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1867 HandleLevelEditorKeyInput(key);
1870 case GAME_MODE_PLAYING:
1875 RequestQuitGame(setup.ask_on_escape);
1885 if (key == KSYM_Escape)
1887 SetGameStatus(GAME_MODE_MAIN);
1895 HandleKeysDebug(key);
1898 void HandleNoEvent()
1900 // if (button_status && game_status != GAME_MODE_PLAYING)
1901 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1903 HandleButton(0, 0, button_status, -button_status);
1910 #if defined(NETWORK_AVALIABLE)
1911 if (options.network)
1915 switch (game_status)
1917 case GAME_MODE_MAIN:
1918 DrawPreviewLevelAnimation();
1921 case GAME_MODE_EDITOR:
1922 HandleLevelEditorIdle();
1925 #if defined(TARGET_SDL2)
1926 case GAME_MODE_PLAYING:
1927 HandleFollowFinger(-1, -1, -1);
1936 static int HandleJoystickForAllPlayers()
1940 boolean no_joysticks_configured = TRUE;
1941 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1942 static byte joy_action_last[MAX_PLAYERS];
1944 for (i = 0; i < MAX_PLAYERS; i++)
1945 if (setup.input[i].use_joystick)
1946 no_joysticks_configured = FALSE;
1948 /* if no joysticks configured, map connected joysticks to players */
1949 if (no_joysticks_configured)
1950 use_as_joystick_nr = TRUE;
1952 for (i = 0; i < MAX_PLAYERS; i++)
1954 byte joy_action = 0;
1956 joy_action = JoystickExt(i, use_as_joystick_nr);
1957 result |= joy_action;
1959 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1960 joy_action != joy_action_last[i])
1961 stored_player[i].action = joy_action;
1963 joy_action_last[i] = joy_action;
1969 void HandleJoystick()
1971 int joystick = HandleJoystickForAllPlayers();
1972 int keyboard = key_joystick_mapping;
1973 int joy = (joystick | keyboard);
1974 int left = joy & JOY_LEFT;
1975 int right = joy & JOY_RIGHT;
1976 int up = joy & JOY_UP;
1977 int down = joy & JOY_DOWN;
1978 int button = joy & JOY_BUTTON;
1979 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1980 int dx = (left ? -1 : right ? 1 : 0);
1981 int dy = (up ? -1 : down ? 1 : 0);
1983 if (HandleGlobalAnimClicks(-1, -1, newbutton))
1985 /* do not handle this button event anymore */
1989 switch (game_status)
1991 case GAME_MODE_TITLE:
1992 case GAME_MODE_MAIN:
1993 case GAME_MODE_LEVELS:
1994 case GAME_MODE_LEVELNR:
1995 case GAME_MODE_SETUP:
1996 case GAME_MODE_INFO:
1998 static unsigned int joystickmove_delay = 0;
2000 if (joystick && !button &&
2001 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
2002 newbutton = dx = dy = 0;
2004 if (game_status == GAME_MODE_TITLE)
2005 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2006 else if (game_status == GAME_MODE_MAIN)
2007 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2008 else if (game_status == GAME_MODE_LEVELS)
2009 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2010 else if (game_status == GAME_MODE_LEVELNR)
2011 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2012 else if (game_status == GAME_MODE_SETUP)
2013 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2014 else if (game_status == GAME_MODE_INFO)
2015 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2019 case GAME_MODE_SCORES:
2020 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2023 case GAME_MODE_PLAYING:
2024 if (tape.playing || keyboard)
2025 newbutton = ((joy & JOY_BUTTON) != 0);
2027 if (newbutton && AllPlayersGone)