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_CONTROLLERAXISMOTION:
252 case SDL_CONTROLLERBUTTONDOWN:
253 case SDL_CONTROLLERBUTTONUP:
255 case SDL_JOYAXISMOTION:
256 case SDL_JOYBUTTONDOWN:
257 case SDL_JOYBUTTONUP:
258 HandleJoystickEvent(event);
262 HandleWindowManagerEvent(event);
271 void HandleMouseCursor()
273 if (game_status == GAME_MODE_TITLE)
275 /* when showing title screens, hide mouse pointer (if not moved) */
277 if (gfx.cursor_mode != CURSOR_NONE &&
278 DelayReached(&special_cursor_delay, special_cursor_delay_value))
280 SetMouseCursor(CURSOR_NONE);
283 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
286 /* when playing, display a special mouse pointer inside the playfield */
288 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
289 cursor_inside_playfield &&
290 DelayReached(&special_cursor_delay, special_cursor_delay_value))
292 SetMouseCursor(CURSOR_PLAYFIELD);
295 else if (gfx.cursor_mode != CURSOR_DEFAULT)
297 SetMouseCursor(CURSOR_DEFAULT);
300 /* this is set after all pending events have been processed */
301 cursor_mode_last = gfx.cursor_mode;
313 /* also execute after pending events have been processed before */
316 /* don't use all CPU time when idle; the main loop while playing
317 has its own synchronization and is CPU friendly, too */
319 if (game_status == GAME_MODE_PLAYING)
322 /* always copy backbuffer to visible screen for every video frame */
325 /* reset video frame delay to default (may change again while playing) */
326 SetVideoFrameDelay(MenuFrameDelay);
328 if (game_status == GAME_MODE_QUIT)
333 void ClearEventQueue()
335 while (PendingEvent())
343 case EVENT_BUTTONRELEASE:
344 button_status = MB_RELEASED;
347 case EVENT_KEYRELEASE:
352 HandleOtherEvents(&event);
358 void ClearPlayerAction()
362 /* simulate key release events for still pressed keys */
363 key_joystick_mapping = 0;
364 for (i = 0; i < MAX_PLAYERS; i++)
365 stored_player[i].action = 0;
368 void SleepWhileUnmapped()
370 boolean window_unmapped = TRUE;
372 KeyboardAutoRepeatOn();
374 while (window_unmapped)
382 case EVENT_BUTTONRELEASE:
383 button_status = MB_RELEASED;
386 case EVENT_KEYRELEASE:
387 key_joystick_mapping = 0;
390 case EVENT_MAPNOTIFY:
391 window_unmapped = FALSE;
394 case EVENT_UNMAPNOTIFY:
395 /* this is only to surely prevent the 'should not happen' case
396 * of recursively looping between 'SleepWhileUnmapped()' and
397 * 'HandleOtherEvents()' which usually calls this funtion.
402 HandleOtherEvents(&event);
407 if (game_status == GAME_MODE_PLAYING)
408 KeyboardAutoRepeatOffUnlessAutoplay();
411 void HandleExposeEvent(ExposeEvent *event)
415 void HandleButtonEvent(ButtonEvent *event)
417 #if DEBUG_EVENTS_BUTTON
418 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
420 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
424 #if defined(HAS_SCREEN_KEYBOARD)
425 if (video.shifted_up)
426 event->y += video.shifted_up_pos;
429 motion_status = FALSE;
431 if (event->type == EVENT_BUTTONPRESS)
432 button_status = event->button;
434 button_status = MB_RELEASED;
436 HandleButton(event->x, event->y, button_status, event->button);
439 void HandleMotionEvent(MotionEvent *event)
441 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
444 motion_status = TRUE;
446 #if DEBUG_EVENTS_MOTION
447 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
448 button_status, event->x, event->y);
451 HandleButton(event->x, event->y, button_status, button_status);
454 #if defined(TARGET_SDL2)
456 void HandleWheelEvent(WheelEvent *event)
460 #if DEBUG_EVENTS_WHEEL
462 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
463 event->which, event->x, event->y);
465 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
466 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
467 event->which, event->x, event->y,
468 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
469 "SDL_MOUSEWHEEL_FLIPPED"));
473 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
474 event->x > 0 ? MB_WHEEL_RIGHT :
475 event->y < 0 ? MB_WHEEL_DOWN :
476 event->y > 0 ? MB_WHEEL_UP : 0);
478 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
479 // accelerated mouse wheel available on Mac and Windows
480 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
482 // no accelerated mouse wheel available on Unix/Linux
483 wheel_steps = DEFAULT_WHEEL_STEPS;
486 motion_status = FALSE;
488 button_status = button_nr;
489 HandleButton(0, 0, button_status, -button_nr);
491 button_status = MB_RELEASED;
492 HandleButton(0, 0, button_status, -button_nr);
495 void HandleWindowEvent(WindowEvent *event)
497 #if DEBUG_EVENTS_WINDOW
498 int subtype = event->event;
501 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
502 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
503 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
504 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
505 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
506 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
507 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
508 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
509 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
510 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
511 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
512 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
513 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
514 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
517 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
518 event_name, event->data1, event->data2);
522 // (not needed, as the screen gets redrawn every 20 ms anyway)
523 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
524 event->event == SDL_WINDOWEVENT_RESIZED ||
525 event->event == SDL_WINDOWEVENT_EXPOSED)
529 if (event->event == SDL_WINDOWEVENT_RESIZED)
531 if (!video.fullscreen_enabled)
533 int new_window_width = event->data1;
534 int new_window_height = event->data2;
536 // if window size has changed after resizing, calculate new scaling factor
537 if (new_window_width != video.window_width ||
538 new_window_height != video.window_height)
540 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
541 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
543 // (extreme window scaling allowed, but cannot be saved permanently)
544 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
545 setup.window_scaling_percent =
546 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
547 MAX_WINDOW_SCALING_PERCENT);
549 video.window_width = new_window_width;
550 video.window_height = new_window_height;
552 if (game_status == GAME_MODE_SETUP)
553 RedrawSetupScreenAfterFullscreenToggle();
558 #if defined(PLATFORM_ANDROID)
561 int new_display_width = event->data1;
562 int new_display_height = event->data2;
564 // if fullscreen display size has changed, device has been rotated
565 if (new_display_width != video.display_width ||
566 new_display_height != video.display_height)
568 video.display_width = new_display_width;
569 video.display_height = new_display_height;
571 SDLSetScreenProperties();
578 #define NUM_TOUCH_FINGERS 3
583 SDL_FingerID finger_id;
586 } touch_info[NUM_TOUCH_FINGERS];
588 void HandleFingerEvent(FingerEvent *event)
590 static Key motion_key_x = KSYM_UNDEFINED;
591 static Key motion_key_y = KSYM_UNDEFINED;
592 static Key button_key = KSYM_UNDEFINED;
593 static float motion_x1, motion_y1;
594 static float button_x1, button_y1;
595 static SDL_FingerID motion_id = -1;
596 static SDL_FingerID button_id = -1;
597 int move_trigger_distance_percent = setup.touch.move_distance;
598 int drop_trigger_distance_percent = setup.touch.drop_distance;
599 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
600 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
601 float event_x = event->x;
602 float event_y = event->y;
604 #if DEBUG_EVENTS_FINGER
605 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
606 event->type == EVENT_FINGERPRESS ? "pressed" :
607 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
611 event->dx, event->dy,
615 if (game_status != GAME_MODE_PLAYING)
618 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
621 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
623 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
625 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
627 event_y = (event_y - ypos) / (1 - ypos);
629 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
630 event_y > 2.0 / 3.0 && event_y < 1 ?
631 setup.input[0].key.snap :
632 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
633 event_y > 2.0 / 3.0 && event_y < 1 ?
634 setup.input[0].key.drop :
635 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
636 event_y > 0 && event_y < 1.0 / 3.0 ?
637 setup.input[0].key.up :
638 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
639 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
640 setup.input[0].key.left :
641 event_x > 8.0 / 9.0 && event_x < 1 &&
642 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
643 setup.input[0].key.right :
644 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
645 event_y > 2.0 / 3.0 && event_y < 1 ?
646 setup.input[0].key.down :
649 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
653 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
654 getKeyNameFromKey(key), key_status_name, event->fingerId);
656 // check if we already know this touch event's finger id
657 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
659 if (touch_info[i].touched &&
660 touch_info[i].finger_id == event->fingerId)
662 // Error(ERR_DEBUG, "MARK 1: %d", i);
668 if (i >= NUM_TOUCH_FINGERS)
670 if (key_status == KEY_PRESSED)
672 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
674 // unknown finger id -- get new, empty slot, if available
675 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
677 if (touch_info[i].counter < oldest_counter)
680 oldest_counter = touch_info[i].counter;
682 // Error(ERR_DEBUG, "MARK 2: %d", i);
685 if (!touch_info[i].touched)
687 // Error(ERR_DEBUG, "MARK 3: %d", i);
693 if (i >= NUM_TOUCH_FINGERS)
695 // all slots allocated -- use oldest slot
698 // Error(ERR_DEBUG, "MARK 4: %d", i);
703 // release of previously unknown key (should not happen)
705 if (key != KSYM_UNDEFINED)
707 HandleKey(key, KEY_RELEASED);
709 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
710 getKeyNameFromKey(key), "KEY_RELEASED", i);
715 if (i < NUM_TOUCH_FINGERS)
717 if (key_status == KEY_PRESSED)
719 if (touch_info[i].key != key)
721 if (touch_info[i].key != KSYM_UNDEFINED)
723 HandleKey(touch_info[i].key, KEY_RELEASED);
725 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
726 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
729 if (key != KSYM_UNDEFINED)
731 HandleKey(key, KEY_PRESSED);
733 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
734 getKeyNameFromKey(key), "KEY_PRESSED", i);
738 touch_info[i].touched = TRUE;
739 touch_info[i].finger_id = event->fingerId;
740 touch_info[i].counter = Counter();
741 touch_info[i].key = key;
745 if (touch_info[i].key != KSYM_UNDEFINED)
747 HandleKey(touch_info[i].key, KEY_RELEASED);
749 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
750 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
753 touch_info[i].touched = FALSE;
754 touch_info[i].finger_id = 0;
755 touch_info[i].counter = 0;
756 touch_info[i].key = 0;
763 // use touch direction control
765 if (event->type == EVENT_FINGERPRESS)
767 if (event_x > 1.0 / 3.0)
771 motion_id = event->fingerId;
776 motion_key_x = KSYM_UNDEFINED;
777 motion_key_y = KSYM_UNDEFINED;
779 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
785 button_id = event->fingerId;
790 button_key = setup.input[0].key.snap;
792 HandleKey(button_key, KEY_PRESSED);
794 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
797 else if (event->type == EVENT_FINGERRELEASE)
799 if (event->fingerId == motion_id)
803 if (motion_key_x != KSYM_UNDEFINED)
804 HandleKey(motion_key_x, KEY_RELEASED);
805 if (motion_key_y != KSYM_UNDEFINED)
806 HandleKey(motion_key_y, KEY_RELEASED);
808 motion_key_x = KSYM_UNDEFINED;
809 motion_key_y = KSYM_UNDEFINED;
811 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
813 else if (event->fingerId == button_id)
817 if (button_key != KSYM_UNDEFINED)
818 HandleKey(button_key, KEY_RELEASED);
820 button_key = KSYM_UNDEFINED;
822 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
825 else if (event->type == EVENT_FINGERMOTION)
827 if (event->fingerId == motion_id)
829 float distance_x = ABS(event_x - motion_x1);
830 float distance_y = ABS(event_y - motion_y1);
831 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
832 event_x > motion_x1 ? setup.input[0].key.right :
834 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
835 event_y > motion_y1 ? setup.input[0].key.down :
838 if (distance_x < move_trigger_distance / 2 ||
839 distance_x < distance_y)
840 new_motion_key_x = KSYM_UNDEFINED;
842 if (distance_y < move_trigger_distance / 2 ||
843 distance_y < distance_x)
844 new_motion_key_y = KSYM_UNDEFINED;
846 if (distance_x > move_trigger_distance ||
847 distance_y > move_trigger_distance)
849 if (new_motion_key_x != motion_key_x)
851 if (motion_key_x != KSYM_UNDEFINED)
852 HandleKey(motion_key_x, KEY_RELEASED);
853 if (new_motion_key_x != KSYM_UNDEFINED)
854 HandleKey(new_motion_key_x, KEY_PRESSED);
857 if (new_motion_key_y != motion_key_y)
859 if (motion_key_y != KSYM_UNDEFINED)
860 HandleKey(motion_key_y, KEY_RELEASED);
861 if (new_motion_key_y != KSYM_UNDEFINED)
862 HandleKey(new_motion_key_y, KEY_PRESSED);
868 motion_key_x = new_motion_key_x;
869 motion_key_y = new_motion_key_y;
871 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
874 else if (event->fingerId == button_id)
876 float distance_x = ABS(event_x - button_x1);
877 float distance_y = ABS(event_y - button_y1);
879 if (distance_x < drop_trigger_distance / 2 &&
880 distance_y > drop_trigger_distance)
882 if (button_key == setup.input[0].key.snap)
883 HandleKey(button_key, KEY_RELEASED);
888 button_key = setup.input[0].key.drop;
890 HandleKey(button_key, KEY_PRESSED);
892 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
898 static void HandleFollowFinger(int mx, int my, int button)
900 static int old_mx = 0, old_my = 0;
901 static Key motion_key_x = KSYM_UNDEFINED;
902 static Key motion_key_y = KSYM_UNDEFINED;
903 static boolean started_on_player = FALSE;
904 static boolean player_is_dropping = FALSE;
905 static int player_drop_count = 0;
906 static int last_player_x = -1;
907 static int last_player_y = -1;
909 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
912 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
914 touch_info[0].touched = TRUE;
915 touch_info[0].key = 0;
922 started_on_player = FALSE;
923 player_is_dropping = FALSE;
924 player_drop_count = 0;
928 motion_key_x = KSYM_UNDEFINED;
929 motion_key_y = KSYM_UNDEFINED;
931 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
934 else if (button == MB_RELEASED && touch_info[0].touched)
936 touch_info[0].touched = FALSE;
937 touch_info[0].key = 0;
942 if (motion_key_x != KSYM_UNDEFINED)
943 HandleKey(motion_key_x, KEY_RELEASED);
944 if (motion_key_y != KSYM_UNDEFINED)
945 HandleKey(motion_key_y, KEY_RELEASED);
947 if (started_on_player)
949 if (player_is_dropping)
951 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
953 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
957 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
959 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
963 motion_key_x = KSYM_UNDEFINED;
964 motion_key_y = KSYM_UNDEFINED;
966 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
969 if (touch_info[0].touched)
971 int src_x = local_player->jx;
972 int src_y = local_player->jy;
973 int dst_x = getLevelFromScreenX(old_mx);
974 int dst_y = getLevelFromScreenY(old_my);
975 int dx = dst_x - src_x;
976 int dy = dst_y - src_y;
977 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
978 dx > 0 ? setup.input[0].key.right :
980 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
981 dy > 0 ? setup.input[0].key.down :
984 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
985 (last_player_x != local_player->jx ||
986 last_player_y != local_player->jy))
988 // in case of asymmetric diagonal movement, use "preferred" direction
990 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
992 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
993 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
995 local_player->last_move_dir = last_move_dir;
997 // (required to prevent accidentally forcing direction for next movement)
998 last_player_x = local_player->jx;
999 last_player_y = local_player->jy;
1002 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1004 started_on_player = TRUE;
1005 player_drop_count = getPlayerInventorySize(0);
1006 player_is_dropping = (player_drop_count > 0);
1008 if (player_is_dropping)
1010 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1012 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1016 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1018 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1021 else if (dx != 0 || dy != 0)
1023 if (player_is_dropping &&
1024 player_drop_count == getPlayerInventorySize(0))
1026 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1028 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1029 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1031 player_is_dropping = FALSE;
1035 if (new_motion_key_x != motion_key_x)
1037 Error(ERR_DEBUG, "---------- %s %s ----------",
1038 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1039 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1041 if (motion_key_x != KSYM_UNDEFINED)
1042 HandleKey(motion_key_x, KEY_RELEASED);
1043 if (new_motion_key_x != KSYM_UNDEFINED)
1044 HandleKey(new_motion_key_x, KEY_PRESSED);
1047 if (new_motion_key_y != motion_key_y)
1049 Error(ERR_DEBUG, "---------- %s %s ----------",
1050 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1051 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1053 if (motion_key_y != KSYM_UNDEFINED)
1054 HandleKey(motion_key_y, KEY_RELEASED);
1055 if (new_motion_key_y != KSYM_UNDEFINED)
1056 HandleKey(new_motion_key_y, KEY_PRESSED);
1059 motion_key_x = new_motion_key_x;
1060 motion_key_y = new_motion_key_y;
1064 static boolean checkTextInputKeyModState()
1066 // when playing, only handle raw key events and ignore text input
1067 if (game_status == GAME_MODE_PLAYING)
1070 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1073 void HandleTextEvent(TextEvent *event)
1075 char *text = event->text;
1076 Key key = getKeyFromKeyName(text);
1078 #if DEBUG_EVENTS_TEXT
1079 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1082 text[0], (int)(text[0]),
1084 getKeyNameFromKey(key),
1088 #if !defined(HAS_SCREEN_KEYBOARD)
1089 // non-mobile devices: only handle key input with modifier keys pressed here
1090 // (every other key input is handled directly as physical key input event)
1091 if (!checkTextInputKeyModState())
1095 // process text input as "classic" (with uppercase etc.) key input event
1096 HandleKey(key, KEY_PRESSED);
1097 HandleKey(key, KEY_RELEASED);
1100 void HandlePauseResumeEvent(PauseResumeEvent *event)
1102 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1106 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1114 void HandleKeyEvent(KeyEvent *event)
1116 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1117 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1118 Key key = GetEventKey(event, with_modifiers);
1119 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1121 #if DEBUG_EVENTS_KEY
1122 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1123 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1124 event->keysym.scancode,
1129 getKeyNameFromKey(key));
1132 #if defined(PLATFORM_ANDROID)
1133 // always map the "back" button to the "escape" key on Android devices
1134 if (key == KSYM_Back)
1138 HandleKeyModState(keymod, key_status);
1140 #if defined(TARGET_SDL2)
1141 // only handle raw key input without text modifier keys pressed
1142 if (!checkTextInputKeyModState())
1143 HandleKey(key, key_status);
1145 HandleKey(key, key_status);
1149 void HandleFocusEvent(FocusChangeEvent *event)
1151 static int old_joystick_status = -1;
1153 if (event->type == EVENT_FOCUSOUT)
1155 KeyboardAutoRepeatOn();
1156 old_joystick_status = joystick.status;
1157 joystick.status = JOYSTICK_NOT_AVAILABLE;
1159 ClearPlayerAction();
1161 else if (event->type == EVENT_FOCUSIN)
1163 /* When there are two Rocks'n'Diamonds windows which overlap and
1164 the player moves the pointer from one game window to the other,
1165 a 'FocusOut' event is generated for the window the pointer is
1166 leaving and a 'FocusIn' event is generated for the window the
1167 pointer is entering. In some cases, it can happen that the
1168 'FocusIn' event is handled by the one game process before the
1169 'FocusOut' event by the other game process. In this case the
1170 X11 environment would end up with activated keyboard auto repeat,
1171 because unfortunately this is a global setting and not (which
1172 would be far better) set for each X11 window individually.
1173 The effect would be keyboard auto repeat while playing the game
1174 (game_status == GAME_MODE_PLAYING), which is not desired.
1175 To avoid this special case, we just wait 1/10 second before
1176 processing the 'FocusIn' event.
1179 if (game_status == GAME_MODE_PLAYING)
1182 KeyboardAutoRepeatOffUnlessAutoplay();
1185 if (old_joystick_status != -1)
1186 joystick.status = old_joystick_status;
1190 void HandleClientMessageEvent(ClientMessageEvent *event)
1192 if (CheckCloseWindowEvent(event))
1196 void HandleWindowManagerEvent(Event *event)
1198 #if defined(TARGET_SDL)
1199 SDLHandleWindowManagerEvent(event);
1203 void HandleButton(int mx, int my, int button, int button_nr)
1205 static int old_mx = 0, old_my = 0;
1206 boolean button_hold = FALSE;
1212 button_nr = -button_nr;
1221 #if defined(PLATFORM_ANDROID)
1222 // when playing, only handle gadgets when using "follow finger" controls
1223 boolean handle_gadgets =
1224 (game_status != GAME_MODE_PLAYING ||
1225 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1227 if (handle_gadgets &&
1228 HandleGadgets(mx, my, button))
1230 /* do not handle this button event anymore */
1231 mx = my = -32; /* force mouse event to be outside screen tiles */
1234 if (HandleGadgets(mx, my, button))
1236 /* do not handle this button event anymore */
1237 mx = my = -32; /* force mouse event to be outside screen tiles */
1241 if (HandleGlobalAnimClicks(mx, my, button))
1243 /* do not handle this button event anymore */
1244 mx = my = -32; /* force mouse event to be outside screen tiles */
1247 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1250 /* do not use scroll wheel button events for anything other than gadgets */
1251 if (IS_WHEEL_BUTTON(button_nr))
1254 switch (game_status)
1256 case GAME_MODE_TITLE:
1257 HandleTitleScreen(mx, my, 0, 0, button);
1260 case GAME_MODE_MAIN:
1261 HandleMainMenu(mx, my, 0, 0, button);
1264 case GAME_MODE_PSEUDO_TYPENAME:
1265 HandleTypeName(0, KSYM_Return);
1268 case GAME_MODE_LEVELS:
1269 HandleChooseLevelSet(mx, my, 0, 0, button);
1272 case GAME_MODE_LEVELNR:
1273 HandleChooseLevelNr(mx, my, 0, 0, button);
1276 case GAME_MODE_SCORES:
1277 HandleHallOfFame(0, 0, 0, 0, button);
1280 case GAME_MODE_EDITOR:
1281 HandleLevelEditorIdle();
1284 case GAME_MODE_INFO:
1285 HandleInfoScreen(mx, my, 0, 0, button);
1288 case GAME_MODE_SETUP:
1289 HandleSetupScreen(mx, my, 0, 0, button);
1292 #if defined(TARGET_SDL2)
1293 case GAME_MODE_PLAYING:
1294 HandleFollowFinger(mx, my, button);
1298 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1299 GetKeyModState() & KMOD_Control)
1300 DumpTileFromScreen(mx, my);
1310 static boolean is_string_suffix(char *string, char *suffix)
1312 int string_len = strlen(string);
1313 int suffix_len = strlen(suffix);
1315 if (suffix_len > string_len)
1318 return (strEqual(&string[string_len - suffix_len], suffix));
1321 #define MAX_CHEAT_INPUT_LEN 32
1323 static void HandleKeysSpecial(Key key)
1325 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1326 char letter = getCharFromKey(key);
1327 int cheat_input_len = strlen(cheat_input);
1333 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1335 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1336 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1338 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1341 cheat_input[cheat_input_len++] = letter;
1342 cheat_input[cheat_input_len] = '\0';
1344 #if DEBUG_EVENTS_KEY
1345 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1348 if (game_status == GAME_MODE_MAIN)
1350 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1351 is_string_suffix(cheat_input, ":ist"))
1353 InsertSolutionTape();
1355 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1356 is_string_suffix(cheat_input, ":rg"))
1358 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1361 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1362 is_string_suffix(cheat_input, ":rs"))
1364 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1367 else if (is_string_suffix(cheat_input, ":reload-music") ||
1368 is_string_suffix(cheat_input, ":rm"))
1370 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1373 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1374 is_string_suffix(cheat_input, ":ra"))
1376 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1377 1 << ARTWORK_TYPE_SOUNDS |
1378 1 << ARTWORK_TYPE_MUSIC);
1381 else if (is_string_suffix(cheat_input, ":dump-level") ||
1382 is_string_suffix(cheat_input, ":dl"))
1386 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1387 is_string_suffix(cheat_input, ":dt"))
1391 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1392 is_string_suffix(cheat_input, ":ft"))
1394 /* fix single-player tapes that contain player input for more than one
1395 player (due to a bug in 3.3.1.2 and earlier versions), which results
1396 in playing levels with more than one player in multi-player mode,
1397 even though the tape was originally recorded in single-player mode */
1399 /* remove player input actions for all players but the first one */
1400 for (i = 1; i < MAX_PLAYERS; i++)
1401 tape.player_participates[i] = FALSE;
1403 tape.changed = TRUE;
1405 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1406 is_string_suffix(cheat_input, ":snl"))
1408 SaveNativeLevel(&level);
1411 else if (game_status == GAME_MODE_PLAYING)
1414 if (is_string_suffix(cheat_input, ".q"))
1415 DEBUG_SetMaximumDynamite();
1418 else if (game_status == GAME_MODE_EDITOR)
1420 if (is_string_suffix(cheat_input, ":dump-brush") ||
1421 is_string_suffix(cheat_input, ":DB"))
1425 else if (is_string_suffix(cheat_input, ":DDB"))
1432 void HandleKeysDebug(Key key)
1437 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1439 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1441 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1443 if (key == setup.debug.frame_delay_key[i] &&
1444 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1446 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1447 setup.debug.frame_delay[i] : setup.game_frame_delay);
1449 if (!setup.debug.frame_delay_game_only)
1450 MenuFrameDelay = GameFrameDelay;
1452 SetVideoFrameDelay(GameFrameDelay);
1454 if (GameFrameDelay > ONE_SECOND_DELAY)
1455 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1456 else if (GameFrameDelay != 0)
1457 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1458 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1459 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1461 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1468 if (game_status == GAME_MODE_PLAYING)
1472 options.debug = !options.debug;
1474 Error(ERR_DEBUG, "debug mode %s",
1475 (options.debug ? "enabled" : "disabled"));
1477 else if (key == KSYM_v)
1479 Error(ERR_DEBUG, "currently using game engine version %d",
1480 game.engine_version);
1486 void HandleKey(Key key, int key_status)
1488 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1489 static boolean ignore_repeated_key = FALSE;
1490 static struct SetupKeyboardInfo ski;
1491 static struct SetupShortcutInfo ssi;
1500 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1501 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1502 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1503 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1504 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1505 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1510 if (game_status == GAME_MODE_PLAYING)
1512 /* only needed for single-step tape recording mode */
1513 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1514 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1515 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1516 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1519 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1521 byte key_action = 0;
1523 if (setup.input[pnr].use_joystick)
1526 ski = setup.input[pnr].key;
1528 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1529 if (key == *key_info[i].key_custom)
1530 key_action |= key_info[i].action;
1532 /* use combined snap+direction keys for the first player only */
1535 ssi = setup.shortcut;
1537 for (i = 0; i < NUM_DIRECTIONS; i++)
1538 if (key == *key_info[i].key_snap)
1539 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1542 /* clear delayed snap and drop actions in single step mode (see below) */
1543 if (tape.single_step)
1545 if (clear_snap_button[pnr])
1547 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1548 clear_snap_button[pnr] = FALSE;
1551 if (clear_drop_button[pnr])
1553 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1554 clear_drop_button[pnr] = FALSE;
1558 if (key_status == KEY_PRESSED)
1559 stored_player[pnr].action |= key_action;
1561 stored_player[pnr].action &= ~key_action;
1563 if (tape.single_step && tape.recording && tape.pausing)
1565 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1567 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1569 /* if snap key already pressed, don't snap when releasing (below) */
1570 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1571 element_snapped[pnr] = TRUE;
1573 /* if drop key already pressed, don't drop when releasing (below) */
1574 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1575 element_dropped[pnr] = TRUE;
1577 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1579 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1580 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1583 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1584 getRedDiskReleaseFlag_SP() == 0)
1585 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1587 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1590 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1592 if (key_action & KEY_BUTTON_SNAP)
1594 /* if snap key was released without moving (see above), snap now */
1595 if (!element_snapped[pnr])
1597 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1599 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1601 /* clear delayed snap button on next event */
1602 clear_snap_button[pnr] = TRUE;
1605 element_snapped[pnr] = FALSE;
1608 if (key_action & KEY_BUTTON_DROP &&
1609 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1611 /* if drop key was released without moving (see above), drop now */
1612 if (!element_dropped[pnr])
1614 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1616 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1617 getRedDiskReleaseFlag_SP() != 0)
1618 stored_player[pnr].action |= KEY_BUTTON_DROP;
1620 /* clear delayed drop button on next event */
1621 clear_drop_button[pnr] = TRUE;
1624 element_dropped[pnr] = FALSE;
1628 else if (tape.recording && tape.pausing)
1630 /* prevent key release events from un-pausing a paused game */
1631 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1632 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1638 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1639 if (key == key_info[i].key_default)
1640 joy |= key_info[i].action;
1645 if (key_status == KEY_PRESSED)
1646 key_joystick_mapping |= joy;
1648 key_joystick_mapping &= ~joy;
1653 if (game_status != GAME_MODE_PLAYING)
1654 key_joystick_mapping = 0;
1656 if (key_status == KEY_RELEASED)
1658 // reset flag to ignore repeated "key pressed" events after key release
1659 ignore_repeated_key = FALSE;
1664 if ((key == KSYM_F11 ||
1665 ((key == KSYM_Return ||
1666 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1667 video.fullscreen_available &&
1668 !ignore_repeated_key)
1670 setup.fullscreen = !setup.fullscreen;
1672 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1674 if (game_status == GAME_MODE_SETUP)
1675 RedrawSetupScreenAfterFullscreenToggle();
1677 // set flag to ignore repeated "key pressed" events
1678 ignore_repeated_key = TRUE;
1683 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1684 key == KSYM_minus || key == KSYM_KP_Subtract ||
1685 key == KSYM_plus || key == KSYM_KP_Add ||
1686 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1687 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1688 video.window_scaling_available &&
1689 !video.fullscreen_enabled)
1691 if (key == KSYM_0 || key == KSYM_KP_0)
1692 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1693 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1694 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1696 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1698 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1699 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1700 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1701 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1703 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1705 if (game_status == GAME_MODE_SETUP)
1706 RedrawSetupScreenAfterFullscreenToggle();
1711 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1712 key == KSYM_Return ||
1713 key == KSYM_Escape)))
1715 /* do not handle this key event anymore */
1716 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1720 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1721 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1728 if (game_status == GAME_MODE_MAIN &&
1729 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1731 StartGameActions(options.network, setup.autorecord, level.random_seed);
1736 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1738 if (key == setup.shortcut.save_game)
1740 else if (key == setup.shortcut.load_game)
1742 else if (key == setup.shortcut.toggle_pause)
1743 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1745 HandleTapeButtonKeys(key);
1746 HandleSoundButtonKeys(key);
1749 if (game_status == GAME_MODE_PLAYING && !network_playing)
1751 int centered_player_nr_next = -999;
1753 if (key == setup.shortcut.focus_player_all)
1754 centered_player_nr_next = -1;
1756 for (i = 0; i < MAX_PLAYERS; i++)
1757 if (key == setup.shortcut.focus_player[i])
1758 centered_player_nr_next = i;
1760 if (centered_player_nr_next != -999)
1762 game.centered_player_nr_next = centered_player_nr_next;
1763 game.set_centered_player = TRUE;
1767 tape.centered_player_nr_next = game.centered_player_nr_next;
1768 tape.set_centered_player = TRUE;
1773 HandleKeysSpecial(key);
1775 if (HandleGadgetsKeyInput(key))
1777 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1778 key = KSYM_UNDEFINED;
1781 switch (game_status)
1783 case GAME_MODE_PSEUDO_TYPENAME:
1784 HandleTypeName(0, key);
1787 case GAME_MODE_TITLE:
1788 case GAME_MODE_MAIN:
1789 case GAME_MODE_LEVELS:
1790 case GAME_MODE_LEVELNR:
1791 case GAME_MODE_SETUP:
1792 case GAME_MODE_INFO:
1793 case GAME_MODE_SCORES:
1798 if (game_status == GAME_MODE_TITLE)
1799 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1800 else if (game_status == GAME_MODE_MAIN)
1801 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1802 else if (game_status == GAME_MODE_LEVELS)
1803 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1804 else if (game_status == GAME_MODE_LEVELNR)
1805 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1806 else if (game_status == GAME_MODE_SETUP)
1807 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1808 else if (game_status == GAME_MODE_INFO)
1809 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1810 else if (game_status == GAME_MODE_SCORES)
1811 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1815 if (game_status != GAME_MODE_MAIN)
1816 FadeSkipNextFadeIn();
1818 if (game_status == GAME_MODE_TITLE)
1819 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1820 else if (game_status == GAME_MODE_LEVELS)
1821 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1822 else if (game_status == GAME_MODE_LEVELNR)
1823 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1824 else if (game_status == GAME_MODE_SETUP)
1825 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1826 else if (game_status == GAME_MODE_INFO)
1827 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1828 else if (game_status == GAME_MODE_SCORES)
1829 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1833 if (game_status == GAME_MODE_LEVELS)
1834 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1835 else if (game_status == GAME_MODE_LEVELNR)
1836 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1837 else if (game_status == GAME_MODE_SETUP)
1838 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1839 else if (game_status == GAME_MODE_INFO)
1840 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1841 else if (game_status == GAME_MODE_SCORES)
1842 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1845 case KSYM_Page_Down:
1846 if (game_status == GAME_MODE_LEVELS)
1847 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1848 else if (game_status == GAME_MODE_LEVELNR)
1849 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1850 else if (game_status == GAME_MODE_SETUP)
1851 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1852 else if (game_status == GAME_MODE_INFO)
1853 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1854 else if (game_status == GAME_MODE_SCORES)
1855 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1863 case GAME_MODE_EDITOR:
1864 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1865 HandleLevelEditorKeyInput(key);
1868 case GAME_MODE_PLAYING:
1873 RequestQuitGame(setup.ask_on_escape);
1883 if (key == KSYM_Escape)
1885 SetGameStatus(GAME_MODE_MAIN);
1893 HandleKeysDebug(key);
1896 void HandleNoEvent()
1898 // if (button_status && game_status != GAME_MODE_PLAYING)
1899 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1901 HandleButton(0, 0, button_status, -button_status);
1908 #if defined(NETWORK_AVALIABLE)
1909 if (options.network)
1913 switch (game_status)
1915 case GAME_MODE_MAIN:
1916 DrawPreviewLevelAnimation();
1919 case GAME_MODE_EDITOR:
1920 HandleLevelEditorIdle();
1923 #if defined(TARGET_SDL2)
1924 case GAME_MODE_PLAYING:
1925 HandleFollowFinger(-1, -1, -1);
1934 static int HandleJoystickForAllPlayers()
1938 boolean no_joysticks_configured = TRUE;
1939 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1940 static byte joy_action_last[MAX_PLAYERS];
1942 for (i = 0; i < MAX_PLAYERS; i++)
1943 if (setup.input[i].use_joystick)
1944 no_joysticks_configured = FALSE;
1946 /* if no joysticks configured, map connected joysticks to players */
1947 if (no_joysticks_configured)
1948 use_as_joystick_nr = TRUE;
1950 for (i = 0; i < MAX_PLAYERS; i++)
1952 byte joy_action = 0;
1954 joy_action = JoystickExt(i, use_as_joystick_nr);
1955 result |= joy_action;
1957 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1958 joy_action != joy_action_last[i])
1959 stored_player[i].action = joy_action;
1961 joy_action_last[i] = joy_action;
1967 void HandleJoystick()
1969 int joystick = HandleJoystickForAllPlayers();
1970 int keyboard = key_joystick_mapping;
1971 int joy = (joystick | keyboard);
1972 int left = joy & JOY_LEFT;
1973 int right = joy & JOY_RIGHT;
1974 int up = joy & JOY_UP;
1975 int down = joy & JOY_DOWN;
1976 int button = joy & JOY_BUTTON;
1977 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1978 int dx = (left ? -1 : right ? 1 : 0);
1979 int dy = (up ? -1 : down ? 1 : 0);
1981 if (HandleGlobalAnimClicks(-1, -1, newbutton))
1983 /* do not handle this button event anymore */
1987 switch (game_status)
1989 case GAME_MODE_TITLE:
1990 case GAME_MODE_MAIN:
1991 case GAME_MODE_LEVELS:
1992 case GAME_MODE_LEVELNR:
1993 case GAME_MODE_SETUP:
1994 case GAME_MODE_INFO:
1996 static unsigned int joystickmove_delay = 0;
1998 if (joystick && !button &&
1999 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
2000 newbutton = dx = dy = 0;
2002 if (game_status == GAME_MODE_TITLE)
2003 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2004 else if (game_status == GAME_MODE_MAIN)
2005 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2006 else if (game_status == GAME_MODE_LEVELS)
2007 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2008 else if (game_status == GAME_MODE_LEVELNR)
2009 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2010 else if (game_status == GAME_MODE_SETUP)
2011 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2012 else if (game_status == GAME_MODE_INFO)
2013 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2017 case GAME_MODE_SCORES:
2018 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2021 case GAME_MODE_PLAYING:
2022 if (tape.playing || keyboard)
2023 newbutton = ((joy & JOY_BUTTON) != 0);
2025 if (newbutton && AllPlayersGone)