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 static boolean WaitValidEvent(Event *event)
135 if (!FilterEvents(event))
138 if (SkipPressedMouseMotionEvent(event))
144 /* this is especially needed for event modifications for the Android target:
145 if mouse coordinates should be modified in the event filter function,
146 using a properly installed SDL event filter does not work, because in
147 the event filter, mouse coordinates in the event structure are still
148 physical pixel positions, not logical (scaled) screen positions, so this
149 has to be handled at a later stage in the event processing functions
150 (when device pixel positions are already converted to screen positions) */
152 boolean NextValidEvent(Event *event)
154 while (PendingEvent())
155 if (WaitValidEvent(event))
164 unsigned int event_frame_delay = 0;
165 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
167 ResetDelayCounter(&event_frame_delay);
169 while (NextValidEvent(&event))
173 case EVENT_BUTTONPRESS:
174 case EVENT_BUTTONRELEASE:
175 HandleButtonEvent((ButtonEvent *) &event);
178 case EVENT_MOTIONNOTIFY:
179 HandleMotionEvent((MotionEvent *) &event);
182 #if defined(TARGET_SDL2)
183 case EVENT_WHEELMOTION:
184 HandleWheelEvent((WheelEvent *) &event);
187 case SDL_WINDOWEVENT:
188 HandleWindowEvent((WindowEvent *) &event);
191 case EVENT_FINGERPRESS:
192 case EVENT_FINGERRELEASE:
193 case EVENT_FINGERMOTION:
194 HandleFingerEvent((FingerEvent *) &event);
197 case EVENT_TEXTINPUT:
198 HandleTextEvent((TextEvent *) &event);
201 case SDL_APP_WILLENTERBACKGROUND:
202 case SDL_APP_DIDENTERBACKGROUND:
203 case SDL_APP_WILLENTERFOREGROUND:
204 case SDL_APP_DIDENTERFOREGROUND:
205 HandlePauseResumeEvent((PauseResumeEvent *) &event);
210 case EVENT_KEYRELEASE:
211 HandleKeyEvent((KeyEvent *) &event);
215 HandleOtherEvents(&event);
219 // do not handle events for longer than standard frame delay period
220 if (DelayReached(&event_frame_delay, event_frame_delay_value))
225 void HandleOtherEvents(Event *event)
230 HandleExposeEvent((ExposeEvent *) event);
233 case EVENT_UNMAPNOTIFY:
235 /* This causes the game to stop not only when iconified, but also
236 when on another virtual desktop, which might be not desired. */
237 SleepWhileUnmapped();
243 HandleFocusEvent((FocusChangeEvent *) event);
246 case EVENT_CLIENTMESSAGE:
247 HandleClientMessageEvent((ClientMessageEvent *) event);
250 #if defined(TARGET_SDL)
251 #if defined(TARGET_SDL2)
252 case SDL_CONTROLLERBUTTONDOWN:
253 case SDL_CONTROLLERBUTTONUP:
254 // for any game controller button event, disable overlay buttons
255 SetOverlayEnabled(FALSE);
257 HandleSpecialGameControllerButtons(event);
260 case SDL_CONTROLLERDEVICEADDED:
261 case SDL_CONTROLLERDEVICEREMOVED:
262 case SDL_CONTROLLERAXISMOTION:
264 case SDL_JOYAXISMOTION:
265 case SDL_JOYBUTTONDOWN:
266 case SDL_JOYBUTTONUP:
267 HandleJoystickEvent(event);
271 HandleWindowManagerEvent(event);
280 void HandleMouseCursor()
282 if (game_status == GAME_MODE_TITLE)
284 /* when showing title screens, hide mouse pointer (if not moved) */
286 if (gfx.cursor_mode != CURSOR_NONE &&
287 DelayReached(&special_cursor_delay, special_cursor_delay_value))
289 SetMouseCursor(CURSOR_NONE);
292 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
295 /* when playing, display a special mouse pointer inside the playfield */
297 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
298 cursor_inside_playfield &&
299 DelayReached(&special_cursor_delay, special_cursor_delay_value))
301 SetMouseCursor(CURSOR_PLAYFIELD);
304 else if (gfx.cursor_mode != CURSOR_DEFAULT)
306 SetMouseCursor(CURSOR_DEFAULT);
309 /* this is set after all pending events have been processed */
310 cursor_mode_last = gfx.cursor_mode;
322 /* also execute after pending events have been processed before */
325 /* don't use all CPU time when idle; the main loop while playing
326 has its own synchronization and is CPU friendly, too */
328 if (game_status == GAME_MODE_PLAYING)
331 /* always copy backbuffer to visible screen for every video frame */
334 /* reset video frame delay to default (may change again while playing) */
335 SetVideoFrameDelay(MenuFrameDelay);
337 if (game_status == GAME_MODE_QUIT)
342 void ClearEventQueue()
346 while (NextValidEvent(&event))
350 case EVENT_BUTTONRELEASE:
351 button_status = MB_RELEASED;
354 case EVENT_KEYRELEASE:
358 #if defined(TARGET_SDL2)
359 case SDL_CONTROLLERBUTTONUP:
360 HandleJoystickEvent(&event);
366 HandleOtherEvents(&event);
372 void ClearPlayerAction()
376 /* simulate key release events for still pressed keys */
377 key_joystick_mapping = 0;
378 for (i = 0; i < MAX_PLAYERS; i++)
379 stored_player[i].action = 0;
381 ClearJoystickState();
384 void SleepWhileUnmapped()
386 boolean window_unmapped = TRUE;
388 KeyboardAutoRepeatOn();
390 while (window_unmapped)
394 if (!WaitValidEvent(&event))
399 case EVENT_BUTTONRELEASE:
400 button_status = MB_RELEASED;
403 case EVENT_KEYRELEASE:
404 key_joystick_mapping = 0;
407 #if defined(TARGET_SDL2)
408 case SDL_CONTROLLERBUTTONUP:
409 HandleJoystickEvent(&event);
410 key_joystick_mapping = 0;
414 case EVENT_MAPNOTIFY:
415 window_unmapped = FALSE;
418 case EVENT_UNMAPNOTIFY:
419 /* this is only to surely prevent the 'should not happen' case
420 * of recursively looping between 'SleepWhileUnmapped()' and
421 * 'HandleOtherEvents()' which usually calls this funtion.
426 HandleOtherEvents(&event);
431 if (game_status == GAME_MODE_PLAYING)
432 KeyboardAutoRepeatOffUnlessAutoplay();
435 void HandleExposeEvent(ExposeEvent *event)
439 void HandleButtonEvent(ButtonEvent *event)
441 #if DEBUG_EVENTS_BUTTON
442 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
444 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
448 #if defined(HAS_SCREEN_KEYBOARD)
449 if (video.shifted_up)
450 event->y += video.shifted_up_pos;
453 motion_status = FALSE;
455 if (event->type == EVENT_BUTTONPRESS)
456 button_status = event->button;
458 button_status = MB_RELEASED;
460 HandleButton(event->x, event->y, button_status, event->button);
463 void HandleMotionEvent(MotionEvent *event)
465 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
468 motion_status = TRUE;
470 #if DEBUG_EVENTS_MOTION
471 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
472 button_status, event->x, event->y);
475 HandleButton(event->x, event->y, button_status, button_status);
478 #if defined(TARGET_SDL2)
480 void HandleWheelEvent(WheelEvent *event)
484 #if DEBUG_EVENTS_WHEEL
486 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
487 event->which, event->x, event->y);
489 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
490 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
491 event->which, event->x, event->y,
492 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
493 "SDL_MOUSEWHEEL_FLIPPED"));
497 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
498 event->x > 0 ? MB_WHEEL_RIGHT :
499 event->y < 0 ? MB_WHEEL_DOWN :
500 event->y > 0 ? MB_WHEEL_UP : 0);
502 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
503 // accelerated mouse wheel available on Mac and Windows
504 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
506 // no accelerated mouse wheel available on Unix/Linux
507 wheel_steps = DEFAULT_WHEEL_STEPS;
510 motion_status = FALSE;
512 button_status = button_nr;
513 HandleButton(0, 0, button_status, -button_nr);
515 button_status = MB_RELEASED;
516 HandleButton(0, 0, button_status, -button_nr);
519 void HandleWindowEvent(WindowEvent *event)
521 #if DEBUG_EVENTS_WINDOW
522 int subtype = event->event;
525 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
526 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
527 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
528 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
529 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
530 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
531 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
532 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
533 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
534 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
535 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
536 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
537 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
538 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
541 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
542 event_name, event->data1, event->data2);
546 // (not needed, as the screen gets redrawn every 20 ms anyway)
547 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
548 event->event == SDL_WINDOWEVENT_RESIZED ||
549 event->event == SDL_WINDOWEVENT_EXPOSED)
553 if (event->event == SDL_WINDOWEVENT_RESIZED)
555 if (!video.fullscreen_enabled)
557 int new_window_width = event->data1;
558 int new_window_height = event->data2;
560 // if window size has changed after resizing, calculate new scaling factor
561 if (new_window_width != video.window_width ||
562 new_window_height != video.window_height)
564 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
565 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
567 // (extreme window scaling allowed, but cannot be saved permanently)
568 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
569 setup.window_scaling_percent =
570 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
571 MAX_WINDOW_SCALING_PERCENT);
573 video.window_width = new_window_width;
574 video.window_height = new_window_height;
576 if (game_status == GAME_MODE_SETUP)
577 RedrawSetupScreenAfterFullscreenToggle();
582 #if defined(PLATFORM_ANDROID)
585 int new_display_width = event->data1;
586 int new_display_height = event->data2;
588 // if fullscreen display size has changed, device has been rotated
589 if (new_display_width != video.display_width ||
590 new_display_height != video.display_height)
592 video.display_width = new_display_width;
593 video.display_height = new_display_height;
595 SDLSetScreenProperties();
602 #define NUM_TOUCH_FINGERS 3
607 SDL_FingerID finger_id;
610 } touch_info[NUM_TOUCH_FINGERS];
612 void HandleFingerEvent(FingerEvent *event)
614 static Key motion_key_x = KSYM_UNDEFINED;
615 static Key motion_key_y = KSYM_UNDEFINED;
616 static Key button_key = KSYM_UNDEFINED;
617 static float motion_x1, motion_y1;
618 static float button_x1, button_y1;
619 static SDL_FingerID motion_id = -1;
620 static SDL_FingerID button_id = -1;
621 int move_trigger_distance_percent = setup.touch.move_distance;
622 int drop_trigger_distance_percent = setup.touch.drop_distance;
623 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
624 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
625 float event_x = event->x;
626 float event_y = event->y;
628 #if DEBUG_EVENTS_FINGER
629 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
630 event->type == EVENT_FINGERPRESS ? "pressed" :
631 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
635 event->dx, event->dy,
639 if (game_status != GAME_MODE_PLAYING)
642 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
645 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
647 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
649 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
651 event_y = (event_y - ypos) / (1 - ypos);
653 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
654 event_y > 2.0 / 3.0 && event_y < 1 ?
655 setup.input[0].key.snap :
656 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
657 event_y > 2.0 / 3.0 && event_y < 1 ?
658 setup.input[0].key.drop :
659 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
660 event_y > 0 && event_y < 1.0 / 3.0 ?
661 setup.input[0].key.up :
662 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
663 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
664 setup.input[0].key.left :
665 event_x > 8.0 / 9.0 && event_x < 1 &&
666 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
667 setup.input[0].key.right :
668 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
669 event_y > 2.0 / 3.0 && event_y < 1 ?
670 setup.input[0].key.down :
673 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
677 // for any touch input event, enable overlay buttons (if activated)
678 SetOverlayEnabled(TRUE);
680 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
681 getKeyNameFromKey(key), key_status_name, event->fingerId);
683 // check if we already know this touch event's finger id
684 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
686 if (touch_info[i].touched &&
687 touch_info[i].finger_id == event->fingerId)
689 // Error(ERR_DEBUG, "MARK 1: %d", i);
695 if (i >= NUM_TOUCH_FINGERS)
697 if (key_status == KEY_PRESSED)
699 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
701 // unknown finger id -- get new, empty slot, if available
702 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
704 if (touch_info[i].counter < oldest_counter)
707 oldest_counter = touch_info[i].counter;
709 // Error(ERR_DEBUG, "MARK 2: %d", i);
712 if (!touch_info[i].touched)
714 // Error(ERR_DEBUG, "MARK 3: %d", i);
720 if (i >= NUM_TOUCH_FINGERS)
722 // all slots allocated -- use oldest slot
725 // Error(ERR_DEBUG, "MARK 4: %d", i);
730 // release of previously unknown key (should not happen)
732 if (key != KSYM_UNDEFINED)
734 HandleKey(key, KEY_RELEASED);
736 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
737 getKeyNameFromKey(key), "KEY_RELEASED", i);
742 if (i < NUM_TOUCH_FINGERS)
744 if (key_status == KEY_PRESSED)
746 if (touch_info[i].key != key)
748 if (touch_info[i].key != KSYM_UNDEFINED)
750 HandleKey(touch_info[i].key, KEY_RELEASED);
752 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
753 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
756 if (key != KSYM_UNDEFINED)
758 HandleKey(key, KEY_PRESSED);
760 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
761 getKeyNameFromKey(key), "KEY_PRESSED", i);
765 touch_info[i].touched = TRUE;
766 touch_info[i].finger_id = event->fingerId;
767 touch_info[i].counter = Counter();
768 touch_info[i].key = key;
772 if (touch_info[i].key != KSYM_UNDEFINED)
774 HandleKey(touch_info[i].key, KEY_RELEASED);
776 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
777 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
780 touch_info[i].touched = FALSE;
781 touch_info[i].finger_id = 0;
782 touch_info[i].counter = 0;
783 touch_info[i].key = 0;
790 // use touch direction control
792 if (event->type == EVENT_FINGERPRESS)
794 if (event_x > 1.0 / 3.0)
798 motion_id = event->fingerId;
803 motion_key_x = KSYM_UNDEFINED;
804 motion_key_y = KSYM_UNDEFINED;
806 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
812 button_id = event->fingerId;
817 button_key = setup.input[0].key.snap;
819 HandleKey(button_key, KEY_PRESSED);
821 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
824 else if (event->type == EVENT_FINGERRELEASE)
826 if (event->fingerId == motion_id)
830 if (motion_key_x != KSYM_UNDEFINED)
831 HandleKey(motion_key_x, KEY_RELEASED);
832 if (motion_key_y != KSYM_UNDEFINED)
833 HandleKey(motion_key_y, KEY_RELEASED);
835 motion_key_x = KSYM_UNDEFINED;
836 motion_key_y = KSYM_UNDEFINED;
838 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
840 else if (event->fingerId == button_id)
844 if (button_key != KSYM_UNDEFINED)
845 HandleKey(button_key, KEY_RELEASED);
847 button_key = KSYM_UNDEFINED;
849 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
852 else if (event->type == EVENT_FINGERMOTION)
854 if (event->fingerId == motion_id)
856 float distance_x = ABS(event_x - motion_x1);
857 float distance_y = ABS(event_y - motion_y1);
858 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
859 event_x > motion_x1 ? setup.input[0].key.right :
861 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
862 event_y > motion_y1 ? setup.input[0].key.down :
865 if (distance_x < move_trigger_distance / 2 ||
866 distance_x < distance_y)
867 new_motion_key_x = KSYM_UNDEFINED;
869 if (distance_y < move_trigger_distance / 2 ||
870 distance_y < distance_x)
871 new_motion_key_y = KSYM_UNDEFINED;
873 if (distance_x > move_trigger_distance ||
874 distance_y > move_trigger_distance)
876 if (new_motion_key_x != motion_key_x)
878 if (motion_key_x != KSYM_UNDEFINED)
879 HandleKey(motion_key_x, KEY_RELEASED);
880 if (new_motion_key_x != KSYM_UNDEFINED)
881 HandleKey(new_motion_key_x, KEY_PRESSED);
884 if (new_motion_key_y != motion_key_y)
886 if (motion_key_y != KSYM_UNDEFINED)
887 HandleKey(motion_key_y, KEY_RELEASED);
888 if (new_motion_key_y != KSYM_UNDEFINED)
889 HandleKey(new_motion_key_y, KEY_PRESSED);
895 motion_key_x = new_motion_key_x;
896 motion_key_y = new_motion_key_y;
898 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
901 else if (event->fingerId == button_id)
903 float distance_x = ABS(event_x - button_x1);
904 float distance_y = ABS(event_y - button_y1);
906 if (distance_x < drop_trigger_distance / 2 &&
907 distance_y > drop_trigger_distance)
909 if (button_key == setup.input[0].key.snap)
910 HandleKey(button_key, KEY_RELEASED);
915 button_key = setup.input[0].key.drop;
917 HandleKey(button_key, KEY_PRESSED);
919 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
925 static void HandleFollowFinger(int mx, int my, int button)
927 static int old_mx = 0, old_my = 0;
928 static Key motion_key_x = KSYM_UNDEFINED;
929 static Key motion_key_y = KSYM_UNDEFINED;
930 static boolean started_on_player = FALSE;
931 static boolean player_is_dropping = FALSE;
932 static int player_drop_count = 0;
933 static int last_player_x = -1;
934 static int last_player_y = -1;
936 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
939 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
941 touch_info[0].touched = TRUE;
942 touch_info[0].key = 0;
949 started_on_player = FALSE;
950 player_is_dropping = FALSE;
951 player_drop_count = 0;
955 motion_key_x = KSYM_UNDEFINED;
956 motion_key_y = KSYM_UNDEFINED;
958 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
961 else if (button == MB_RELEASED && touch_info[0].touched)
963 touch_info[0].touched = FALSE;
964 touch_info[0].key = 0;
969 if (motion_key_x != KSYM_UNDEFINED)
970 HandleKey(motion_key_x, KEY_RELEASED);
971 if (motion_key_y != KSYM_UNDEFINED)
972 HandleKey(motion_key_y, KEY_RELEASED);
974 if (started_on_player)
976 if (player_is_dropping)
978 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
980 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
984 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
986 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
990 motion_key_x = KSYM_UNDEFINED;
991 motion_key_y = KSYM_UNDEFINED;
993 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
996 if (touch_info[0].touched)
998 int src_x = local_player->jx;
999 int src_y = local_player->jy;
1000 int dst_x = getLevelFromScreenX(old_mx);
1001 int dst_y = getLevelFromScreenY(old_my);
1002 int dx = dst_x - src_x;
1003 int dy = dst_y - src_y;
1004 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
1005 dx > 0 ? setup.input[0].key.right :
1007 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
1008 dy > 0 ? setup.input[0].key.down :
1011 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1012 (last_player_x != local_player->jx ||
1013 last_player_y != local_player->jy))
1015 // in case of asymmetric diagonal movement, use "preferred" direction
1017 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1019 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1020 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1022 local_player->last_move_dir = last_move_dir;
1024 // (required to prevent accidentally forcing direction for next movement)
1025 last_player_x = local_player->jx;
1026 last_player_y = local_player->jy;
1029 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1031 started_on_player = TRUE;
1032 player_drop_count = getPlayerInventorySize(0);
1033 player_is_dropping = (player_drop_count > 0);
1035 if (player_is_dropping)
1037 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1039 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1043 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1045 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1048 else if (dx != 0 || dy != 0)
1050 if (player_is_dropping &&
1051 player_drop_count == getPlayerInventorySize(0))
1053 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1055 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1056 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1058 player_is_dropping = FALSE;
1062 if (new_motion_key_x != motion_key_x)
1064 Error(ERR_DEBUG, "---------- %s %s ----------",
1065 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1066 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1068 if (motion_key_x != KSYM_UNDEFINED)
1069 HandleKey(motion_key_x, KEY_RELEASED);
1070 if (new_motion_key_x != KSYM_UNDEFINED)
1071 HandleKey(new_motion_key_x, KEY_PRESSED);
1074 if (new_motion_key_y != motion_key_y)
1076 Error(ERR_DEBUG, "---------- %s %s ----------",
1077 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1078 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1080 if (motion_key_y != KSYM_UNDEFINED)
1081 HandleKey(motion_key_y, KEY_RELEASED);
1082 if (new_motion_key_y != KSYM_UNDEFINED)
1083 HandleKey(new_motion_key_y, KEY_PRESSED);
1086 motion_key_x = new_motion_key_x;
1087 motion_key_y = new_motion_key_y;
1091 static boolean checkTextInputKeyModState()
1093 // when playing, only handle raw key events and ignore text input
1094 if (game_status == GAME_MODE_PLAYING)
1097 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1100 void HandleTextEvent(TextEvent *event)
1102 char *text = event->text;
1103 Key key = getKeyFromKeyName(text);
1105 #if DEBUG_EVENTS_TEXT
1106 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1109 text[0], (int)(text[0]),
1111 getKeyNameFromKey(key),
1115 #if !defined(HAS_SCREEN_KEYBOARD)
1116 // non-mobile devices: only handle key input with modifier keys pressed here
1117 // (every other key input is handled directly as physical key input event)
1118 if (!checkTextInputKeyModState())
1122 // process text input as "classic" (with uppercase etc.) key input event
1123 HandleKey(key, KEY_PRESSED);
1124 HandleKey(key, KEY_RELEASED);
1127 void HandlePauseResumeEvent(PauseResumeEvent *event)
1129 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1133 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1141 void HandleKeyEvent(KeyEvent *event)
1143 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1144 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1145 Key key = GetEventKey(event, with_modifiers);
1146 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1148 #if DEBUG_EVENTS_KEY
1149 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1150 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1151 event->keysym.scancode,
1156 getKeyNameFromKey(key));
1159 #if defined(PLATFORM_ANDROID)
1160 if (key == KSYM_Back)
1162 // always map the "back" button to the "escape" key on Android devices
1167 // for any key event other than "back" button, disable overlay buttons
1168 SetOverlayEnabled(FALSE);
1172 HandleKeyModState(keymod, key_status);
1174 #if defined(TARGET_SDL2)
1175 // only handle raw key input without text modifier keys pressed
1176 if (!checkTextInputKeyModState())
1177 HandleKey(key, key_status);
1179 HandleKey(key, key_status);
1183 void HandleFocusEvent(FocusChangeEvent *event)
1185 static int old_joystick_status = -1;
1187 if (event->type == EVENT_FOCUSOUT)
1189 KeyboardAutoRepeatOn();
1190 old_joystick_status = joystick.status;
1191 joystick.status = JOYSTICK_NOT_AVAILABLE;
1193 ClearPlayerAction();
1195 else if (event->type == EVENT_FOCUSIN)
1197 /* When there are two Rocks'n'Diamonds windows which overlap and
1198 the player moves the pointer from one game window to the other,
1199 a 'FocusOut' event is generated for the window the pointer is
1200 leaving and a 'FocusIn' event is generated for the window the
1201 pointer is entering. In some cases, it can happen that the
1202 'FocusIn' event is handled by the one game process before the
1203 'FocusOut' event by the other game process. In this case the
1204 X11 environment would end up with activated keyboard auto repeat,
1205 because unfortunately this is a global setting and not (which
1206 would be far better) set for each X11 window individually.
1207 The effect would be keyboard auto repeat while playing the game
1208 (game_status == GAME_MODE_PLAYING), which is not desired.
1209 To avoid this special case, we just wait 1/10 second before
1210 processing the 'FocusIn' event.
1213 if (game_status == GAME_MODE_PLAYING)
1216 KeyboardAutoRepeatOffUnlessAutoplay();
1219 if (old_joystick_status != -1)
1220 joystick.status = old_joystick_status;
1224 void HandleClientMessageEvent(ClientMessageEvent *event)
1226 if (CheckCloseWindowEvent(event))
1230 void HandleWindowManagerEvent(Event *event)
1232 #if defined(TARGET_SDL)
1233 SDLHandleWindowManagerEvent(event);
1237 void HandleButton(int mx, int my, int button, int button_nr)
1239 static int old_mx = 0, old_my = 0;
1240 boolean button_hold = FALSE;
1246 button_nr = -button_nr;
1255 #if defined(PLATFORM_ANDROID)
1256 // when playing, only handle gadgets when using "follow finger" controls
1257 boolean handle_gadgets =
1258 (game_status != GAME_MODE_PLAYING ||
1259 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1261 if (handle_gadgets &&
1262 HandleGadgets(mx, my, button))
1264 /* do not handle this button event anymore */
1265 mx = my = -32; /* force mouse event to be outside screen tiles */
1268 if (HandleGadgets(mx, my, button))
1270 /* do not handle this button event anymore */
1271 mx = my = -32; /* force mouse event to be outside screen tiles */
1275 if (HandleGlobalAnimClicks(mx, my, button))
1277 /* do not handle this button event anymore */
1278 return; /* force mouse event not to be handled at all */
1281 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1284 /* do not use scroll wheel button events for anything other than gadgets */
1285 if (IS_WHEEL_BUTTON(button_nr))
1288 switch (game_status)
1290 case GAME_MODE_TITLE:
1291 HandleTitleScreen(mx, my, 0, 0, button);
1294 case GAME_MODE_MAIN:
1295 HandleMainMenu(mx, my, 0, 0, button);
1298 case GAME_MODE_PSEUDO_TYPENAME:
1299 HandleTypeName(0, KSYM_Return);
1302 case GAME_MODE_LEVELS:
1303 HandleChooseLevelSet(mx, my, 0, 0, button);
1306 case GAME_MODE_LEVELNR:
1307 HandleChooseLevelNr(mx, my, 0, 0, button);
1310 case GAME_MODE_SCORES:
1311 HandleHallOfFame(0, 0, 0, 0, button);
1314 case GAME_MODE_EDITOR:
1315 HandleLevelEditorIdle();
1318 case GAME_MODE_INFO:
1319 HandleInfoScreen(mx, my, 0, 0, button);
1322 case GAME_MODE_SETUP:
1323 HandleSetupScreen(mx, my, 0, 0, button);
1326 case GAME_MODE_PLAYING:
1327 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1328 ClickElement(mx, my, button);
1329 #if defined(TARGET_SDL2)
1331 HandleFollowFinger(mx, my, button);
1335 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1336 GetKeyModState() & KMOD_Control)
1337 DumpTileFromScreen(mx, my);
1347 static boolean is_string_suffix(char *string, char *suffix)
1349 int string_len = strlen(string);
1350 int suffix_len = strlen(suffix);
1352 if (suffix_len > string_len)
1355 return (strEqual(&string[string_len - suffix_len], suffix));
1358 #define MAX_CHEAT_INPUT_LEN 32
1360 static void HandleKeysSpecial(Key key)
1362 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1363 char letter = getCharFromKey(key);
1364 int cheat_input_len = strlen(cheat_input);
1370 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1372 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1373 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1375 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1378 cheat_input[cheat_input_len++] = letter;
1379 cheat_input[cheat_input_len] = '\0';
1381 #if DEBUG_EVENTS_KEY
1382 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1385 if (game_status == GAME_MODE_MAIN)
1387 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1388 is_string_suffix(cheat_input, ":ist"))
1390 InsertSolutionTape();
1392 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1393 is_string_suffix(cheat_input, ":rg"))
1395 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1398 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1399 is_string_suffix(cheat_input, ":rs"))
1401 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1404 else if (is_string_suffix(cheat_input, ":reload-music") ||
1405 is_string_suffix(cheat_input, ":rm"))
1407 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1410 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1411 is_string_suffix(cheat_input, ":ra"))
1413 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1414 1 << ARTWORK_TYPE_SOUNDS |
1415 1 << ARTWORK_TYPE_MUSIC);
1418 else if (is_string_suffix(cheat_input, ":dump-level") ||
1419 is_string_suffix(cheat_input, ":dl"))
1423 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1424 is_string_suffix(cheat_input, ":dt"))
1428 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1429 is_string_suffix(cheat_input, ":ft"))
1431 /* fix single-player tapes that contain player input for more than one
1432 player (due to a bug in 3.3.1.2 and earlier versions), which results
1433 in playing levels with more than one player in multi-player mode,
1434 even though the tape was originally recorded in single-player mode */
1436 /* remove player input actions for all players but the first one */
1437 for (i = 1; i < MAX_PLAYERS; i++)
1438 tape.player_participates[i] = FALSE;
1440 tape.changed = TRUE;
1442 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1443 is_string_suffix(cheat_input, ":snl"))
1445 SaveNativeLevel(&level);
1447 else if (is_string_suffix(cheat_input, ":frames-per-second") ||
1448 is_string_suffix(cheat_input, ":fps"))
1450 global.show_frames_per_second = !global.show_frames_per_second;
1453 else if (game_status == GAME_MODE_PLAYING)
1456 if (is_string_suffix(cheat_input, ".q"))
1457 DEBUG_SetMaximumDynamite();
1460 else if (game_status == GAME_MODE_EDITOR)
1462 if (is_string_suffix(cheat_input, ":dump-brush") ||
1463 is_string_suffix(cheat_input, ":DB"))
1467 else if (is_string_suffix(cheat_input, ":DDB"))
1474 void HandleKeysDebug(Key key)
1479 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1481 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
1483 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1485 if (key == setup.debug.frame_delay_key[i] &&
1486 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1488 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1489 setup.debug.frame_delay[i] : setup.game_frame_delay);
1491 if (!setup.debug.frame_delay_game_only)
1492 MenuFrameDelay = GameFrameDelay;
1494 SetVideoFrameDelay(GameFrameDelay);
1496 if (GameFrameDelay > ONE_SECOND_DELAY)
1497 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1498 else if (GameFrameDelay != 0)
1499 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1500 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1501 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1503 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1510 if (game_status == GAME_MODE_PLAYING)
1514 options.debug = !options.debug;
1516 Error(ERR_DEBUG, "debug mode %s",
1517 (options.debug ? "enabled" : "disabled"));
1519 else if (key == KSYM_v)
1521 Error(ERR_DEBUG, "currently using game engine version %d",
1522 game.engine_version);
1528 void HandleKey(Key key, int key_status)
1530 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1531 static boolean ignore_repeated_key = FALSE;
1532 static struct SetupKeyboardInfo ski;
1533 static struct SetupShortcutInfo ssi;
1542 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1543 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1544 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1545 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1546 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1547 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1552 #if defined(TARGET_SDL2)
1553 /* map special keys (media keys / remote control buttons) to default keys */
1554 if (key == KSYM_PlayPause)
1556 else if (key == KSYM_Select)
1560 HandleSpecialGameControllerKeys(key, key_status);
1562 if (game_status == GAME_MODE_PLAYING)
1564 /* only needed for single-step tape recording mode */
1565 static boolean has_snapped[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE };
1568 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1570 byte key_action = 0;
1572 if (setup.input[pnr].use_joystick)
1575 ski = setup.input[pnr].key;
1577 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1578 if (key == *key_info[i].key_custom)
1579 key_action |= key_info[i].action;
1581 /* use combined snap+direction keys for the first player only */
1584 ssi = setup.shortcut;
1586 for (i = 0; i < NUM_DIRECTIONS; i++)
1587 if (key == *key_info[i].key_snap)
1588 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1591 if (key_status == KEY_PRESSED)
1592 stored_player[pnr].action |= key_action;
1594 stored_player[pnr].action &= ~key_action;
1596 if (tape.single_step && tape.recording && tape.pausing)
1598 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1600 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1602 /* if snap key already pressed, keep pause mode when releasing */
1603 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1604 has_snapped[pnr] = TRUE;
1606 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1608 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1610 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1611 getRedDiskReleaseFlag_SP() == 0)
1613 /* add a single inactive frame before dropping starts */
1614 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1615 stored_player[pnr].force_dropping = TRUE;
1618 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON_SNAP)
1620 /* if snap key was pressed without direction, leave pause mode */
1621 if (!has_snapped[pnr])
1622 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1624 has_snapped[pnr] = FALSE;
1627 else if (tape.recording && tape.pausing)
1629 /* prevent key release events from un-pausing a paused game */
1630 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1631 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1637 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1638 if (key == key_info[i].key_default)
1639 joy |= key_info[i].action;
1644 if (key_status == KEY_PRESSED)
1645 key_joystick_mapping |= joy;
1647 key_joystick_mapping &= ~joy;
1652 if (game_status != GAME_MODE_PLAYING)
1653 key_joystick_mapping = 0;
1655 if (key_status == KEY_RELEASED)
1657 // reset flag to ignore repeated "key pressed" events after key release
1658 ignore_repeated_key = FALSE;
1663 if ((key == KSYM_F11 ||
1664 ((key == KSYM_Return ||
1665 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1666 video.fullscreen_available &&
1667 !ignore_repeated_key)
1669 setup.fullscreen = !setup.fullscreen;
1671 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1673 if (game_status == GAME_MODE_SETUP)
1674 RedrawSetupScreenAfterFullscreenToggle();
1676 // set flag to ignore repeated "key pressed" events
1677 ignore_repeated_key = TRUE;
1682 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1683 key == KSYM_minus || key == KSYM_KP_Subtract ||
1684 key == KSYM_plus || key == KSYM_KP_Add ||
1685 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1686 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1687 video.window_scaling_available &&
1688 !video.fullscreen_enabled)
1690 if (key == KSYM_0 || key == KSYM_KP_0)
1691 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1692 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1693 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1695 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1697 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1698 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1699 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1700 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1702 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1704 if (game_status == GAME_MODE_SETUP)
1705 RedrawSetupScreenAfterFullscreenToggle();
1710 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1711 key == KSYM_Return ||
1712 key == KSYM_Escape)))
1714 /* do not handle this key event anymore */
1715 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1719 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1720 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1727 if (game_status == GAME_MODE_MAIN &&
1728 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1730 StartGameActions(options.network, setup.autorecord, level.random_seed);
1735 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1737 if (key == setup.shortcut.save_game)
1739 else if (key == setup.shortcut.load_game)
1741 else if (key == setup.shortcut.toggle_pause)
1742 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1744 HandleTapeButtonKeys(key);
1745 HandleSoundButtonKeys(key);
1748 if (game_status == GAME_MODE_PLAYING && !network_playing)
1750 int centered_player_nr_next = -999;
1752 if (key == setup.shortcut.focus_player_all)
1753 centered_player_nr_next = -1;
1755 for (i = 0; i < MAX_PLAYERS; i++)
1756 if (key == setup.shortcut.focus_player[i])
1757 centered_player_nr_next = i;
1759 if (centered_player_nr_next != -999)
1761 game.centered_player_nr_next = centered_player_nr_next;
1762 game.set_centered_player = TRUE;
1766 tape.centered_player_nr_next = game.centered_player_nr_next;
1767 tape.set_centered_player = TRUE;
1772 HandleKeysSpecial(key);
1774 if (HandleGadgetsKeyInput(key))
1776 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1777 key = KSYM_UNDEFINED;
1780 switch (game_status)
1782 case GAME_MODE_PSEUDO_TYPENAME:
1783 HandleTypeName(0, key);
1786 case GAME_MODE_TITLE:
1787 case GAME_MODE_MAIN:
1788 case GAME_MODE_LEVELS:
1789 case GAME_MODE_LEVELNR:
1790 case GAME_MODE_SETUP:
1791 case GAME_MODE_INFO:
1792 case GAME_MODE_SCORES:
1797 if (game_status == GAME_MODE_TITLE)
1798 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1799 else if (game_status == GAME_MODE_MAIN)
1800 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1801 else if (game_status == GAME_MODE_LEVELS)
1802 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1803 else if (game_status == GAME_MODE_LEVELNR)
1804 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1805 else if (game_status == GAME_MODE_SETUP)
1806 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1807 else if (game_status == GAME_MODE_INFO)
1808 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1809 else if (game_status == GAME_MODE_SCORES)
1810 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1814 if (game_status != GAME_MODE_MAIN)
1815 FadeSkipNextFadeIn();
1817 if (game_status == GAME_MODE_TITLE)
1818 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1819 else if (game_status == GAME_MODE_LEVELS)
1820 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1821 else if (game_status == GAME_MODE_LEVELNR)
1822 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1823 else if (game_status == GAME_MODE_SETUP)
1824 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1825 else if (game_status == GAME_MODE_INFO)
1826 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1827 else if (game_status == GAME_MODE_SCORES)
1828 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1832 if (game_status == GAME_MODE_LEVELS)
1833 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1834 else if (game_status == GAME_MODE_LEVELNR)
1835 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1836 else if (game_status == GAME_MODE_SETUP)
1837 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1838 else if (game_status == GAME_MODE_INFO)
1839 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1840 else if (game_status == GAME_MODE_SCORES)
1841 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1844 case KSYM_Page_Down:
1845 if (game_status == GAME_MODE_LEVELS)
1846 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1847 else if (game_status == GAME_MODE_LEVELNR)
1848 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1849 else if (game_status == GAME_MODE_SETUP)
1850 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1851 else if (game_status == GAME_MODE_INFO)
1852 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1853 else if (game_status == GAME_MODE_SCORES)
1854 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1862 case GAME_MODE_EDITOR:
1863 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1864 HandleLevelEditorKeyInput(key);
1867 case GAME_MODE_PLAYING:
1872 RequestQuitGame(setup.ask_on_escape);
1882 if (key == KSYM_Escape)
1884 SetGameStatus(GAME_MODE_MAIN);
1892 HandleKeysDebug(key);
1895 void HandleNoEvent()
1897 // if (button_status && game_status != GAME_MODE_PLAYING)
1898 if (button_status && (game_status != GAME_MODE_PLAYING ||
1900 level.game_engine_type == GAME_ENGINE_TYPE_MM))
1902 HandleButton(0, 0, button_status, -button_status);
1909 #if defined(NETWORK_AVALIABLE)
1910 if (options.network)
1914 switch (game_status)
1916 case GAME_MODE_MAIN:
1917 DrawPreviewLevelAnimation();
1920 case GAME_MODE_EDITOR:
1921 HandleLevelEditorIdle();
1924 #if defined(TARGET_SDL2)
1925 case GAME_MODE_PLAYING:
1926 HandleFollowFinger(-1, -1, -1);
1935 static int HandleJoystickForAllPlayers()
1939 boolean no_joysticks_configured = TRUE;
1940 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1941 static byte joy_action_last[MAX_PLAYERS];
1943 for (i = 0; i < MAX_PLAYERS; i++)
1944 if (setup.input[i].use_joystick)
1945 no_joysticks_configured = FALSE;
1947 /* if no joysticks configured, map connected joysticks to players */
1948 if (no_joysticks_configured)
1949 use_as_joystick_nr = TRUE;
1951 for (i = 0; i < MAX_PLAYERS; i++)
1953 byte joy_action = 0;
1955 joy_action = JoystickExt(i, use_as_joystick_nr);
1956 result |= joy_action;
1958 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1959 joy_action != joy_action_last[i])
1960 stored_player[i].action = joy_action;
1962 joy_action_last[i] = joy_action;
1968 void HandleJoystick()
1970 int joystick = HandleJoystickForAllPlayers();
1971 int keyboard = key_joystick_mapping;
1972 int joy = (joystick | keyboard);
1973 int left = joy & JOY_LEFT;
1974 int right = joy & JOY_RIGHT;
1975 int up = joy & JOY_UP;
1976 int down = joy & JOY_DOWN;
1977 int button = joy & JOY_BUTTON;
1978 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1979 int dx = (left ? -1 : right ? 1 : 0);
1980 int dy = (up ? -1 : down ? 1 : 0);
1982 if (HandleGlobalAnimClicks(-1, -1, newbutton))
1984 /* do not handle this button event anymore */
1988 switch (game_status)
1990 case GAME_MODE_TITLE:
1991 case GAME_MODE_MAIN:
1992 case GAME_MODE_LEVELS:
1993 case GAME_MODE_LEVELNR:
1994 case GAME_MODE_SETUP:
1995 case GAME_MODE_INFO:
1997 static unsigned int joystickmove_delay = 0;
1998 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
1999 static int joystick_last = 0;
2001 if (joystick && !button &&
2002 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2004 /* delay joystick actions if buttons/axes continually pressed */
2005 newbutton = dx = dy = 0;
2009 /* start with longer delay, then continue with shorter delay */
2010 if (joystick != joystick_last)
2011 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2013 joystickmove_delay_value = GADGET_FRAME_DELAY;
2016 if (game_status == GAME_MODE_TITLE)
2017 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2018 else if (game_status == GAME_MODE_MAIN)
2019 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2020 else if (game_status == GAME_MODE_LEVELS)
2021 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2022 else if (game_status == GAME_MODE_LEVELNR)
2023 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2024 else if (game_status == GAME_MODE_SETUP)
2025 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2026 else if (game_status == GAME_MODE_INFO)
2027 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2029 joystick_last = joystick;
2034 case GAME_MODE_SCORES:
2035 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2038 case GAME_MODE_PLAYING:
2039 if (tape.playing || keyboard)
2040 newbutton = ((joy & JOY_BUTTON) != 0);
2042 if (newbutton && AllPlayersGone)
2049 if (tape.recording && tape.pausing)
2051 if (joystick & JOY_ACTION)
2052 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2062 void HandleSpecialGameControllerButtons(Event *event)
2064 #if defined(TARGET_SDL2)
2065 switch (event->type)
2067 case SDL_CONTROLLERBUTTONDOWN:
2068 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2069 HandleKey(KSYM_space, KEY_PRESSED);
2070 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2071 HandleKey(KSYM_Escape, KEY_PRESSED);
2075 case SDL_CONTROLLERBUTTONUP:
2076 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2077 HandleKey(KSYM_space, KEY_RELEASED);
2078 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2079 HandleKey(KSYM_Escape, KEY_RELEASED);
2086 void HandleSpecialGameControllerKeys(Key key, int key_status)
2088 #if defined(TARGET_SDL2)
2089 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2090 int button = SDL_CONTROLLER_BUTTON_INVALID;
2092 /* map keys to joystick buttons (special hack for Amazon Fire TV remote) */
2093 if (key == KSYM_Rewind)
2094 button = SDL_CONTROLLER_BUTTON_A;
2095 else if (key == KSYM_FastForward || key == KSYM_Menu)
2096 button = SDL_CONTROLLER_BUTTON_B;
2098 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2102 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2103 SDL_CONTROLLERBUTTONUP);
2105 event.cbutton.which = 0; /* first joystick (Amazon Fire TV remote) */
2106 event.cbutton.button = button;
2107 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2110 HandleJoystickEvent(&event);