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 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
302 SetMouseCursor(CURSOR_PLAYFIELD);
305 else if (gfx.cursor_mode != CURSOR_DEFAULT)
307 SetMouseCursor(CURSOR_DEFAULT);
310 /* this is set after all pending events have been processed */
311 cursor_mode_last = gfx.cursor_mode;
323 /* also execute after pending events have been processed before */
326 /* don't use all CPU time when idle; the main loop while playing
327 has its own synchronization and is CPU friendly, too */
329 if (game_status == GAME_MODE_PLAYING)
332 /* always copy backbuffer to visible screen for every video frame */
335 /* reset video frame delay to default (may change again while playing) */
336 SetVideoFrameDelay(MenuFrameDelay);
338 if (game_status == GAME_MODE_QUIT)
343 void ClearEventQueue()
347 while (NextValidEvent(&event))
351 case EVENT_BUTTONRELEASE:
352 button_status = MB_RELEASED;
355 case EVENT_KEYRELEASE:
359 #if defined(TARGET_SDL2)
360 case SDL_CONTROLLERBUTTONUP:
361 HandleJoystickEvent(&event);
367 HandleOtherEvents(&event);
373 void ClearPlayerAction()
377 /* simulate key release events for still pressed keys */
378 key_joystick_mapping = 0;
379 for (i = 0; i < MAX_PLAYERS; i++)
380 stored_player[i].action = 0;
382 ClearJoystickState();
385 void SleepWhileUnmapped()
387 boolean window_unmapped = TRUE;
389 KeyboardAutoRepeatOn();
391 while (window_unmapped)
395 if (!WaitValidEvent(&event))
400 case EVENT_BUTTONRELEASE:
401 button_status = MB_RELEASED;
404 case EVENT_KEYRELEASE:
405 key_joystick_mapping = 0;
408 #if defined(TARGET_SDL2)
409 case SDL_CONTROLLERBUTTONUP:
410 HandleJoystickEvent(&event);
411 key_joystick_mapping = 0;
415 case EVENT_MAPNOTIFY:
416 window_unmapped = FALSE;
419 case EVENT_UNMAPNOTIFY:
420 /* this is only to surely prevent the 'should not happen' case
421 * of recursively looping between 'SleepWhileUnmapped()' and
422 * 'HandleOtherEvents()' which usually calls this funtion.
427 HandleOtherEvents(&event);
432 if (game_status == GAME_MODE_PLAYING)
433 KeyboardAutoRepeatOffUnlessAutoplay();
436 void HandleExposeEvent(ExposeEvent *event)
440 void HandleButtonEvent(ButtonEvent *event)
442 #if DEBUG_EVENTS_BUTTON
443 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
445 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
449 #if defined(HAS_SCREEN_KEYBOARD)
450 if (video.shifted_up)
451 event->y += video.shifted_up_pos;
454 motion_status = FALSE;
456 if (event->type == EVENT_BUTTONPRESS)
457 button_status = event->button;
459 button_status = MB_RELEASED;
461 HandleButton(event->x, event->y, button_status, event->button);
464 void HandleMotionEvent(MotionEvent *event)
466 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
469 motion_status = TRUE;
471 #if DEBUG_EVENTS_MOTION
472 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
473 button_status, event->x, event->y);
476 HandleButton(event->x, event->y, button_status, button_status);
479 #if defined(TARGET_SDL2)
481 void HandleWheelEvent(WheelEvent *event)
485 #if DEBUG_EVENTS_WHEEL
487 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
488 event->which, event->x, event->y);
490 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
491 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
492 event->which, event->x, event->y,
493 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
494 "SDL_MOUSEWHEEL_FLIPPED"));
498 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
499 event->x > 0 ? MB_WHEEL_RIGHT :
500 event->y < 0 ? MB_WHEEL_DOWN :
501 event->y > 0 ? MB_WHEEL_UP : 0);
503 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
504 // accelerated mouse wheel available on Mac and Windows
505 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
507 // no accelerated mouse wheel available on Unix/Linux
508 wheel_steps = DEFAULT_WHEEL_STEPS;
511 motion_status = FALSE;
513 button_status = button_nr;
514 HandleButton(0, 0, button_status, -button_nr);
516 button_status = MB_RELEASED;
517 HandleButton(0, 0, button_status, -button_nr);
520 void HandleWindowEvent(WindowEvent *event)
522 #if DEBUG_EVENTS_WINDOW
523 int subtype = event->event;
526 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
527 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
528 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
529 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
530 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
531 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
532 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
533 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
534 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
535 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
536 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
537 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
538 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
539 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
542 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
543 event_name, event->data1, event->data2);
547 // (not needed, as the screen gets redrawn every 20 ms anyway)
548 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
549 event->event == SDL_WINDOWEVENT_RESIZED ||
550 event->event == SDL_WINDOWEVENT_EXPOSED)
554 if (event->event == SDL_WINDOWEVENT_RESIZED)
556 if (!video.fullscreen_enabled)
558 int new_window_width = event->data1;
559 int new_window_height = event->data2;
561 // if window size has changed after resizing, calculate new scaling factor
562 if (new_window_width != video.window_width ||
563 new_window_height != video.window_height)
565 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
566 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
568 // (extreme window scaling allowed, but cannot be saved permanently)
569 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
570 setup.window_scaling_percent =
571 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
572 MAX_WINDOW_SCALING_PERCENT);
574 video.window_width = new_window_width;
575 video.window_height = new_window_height;
577 if (game_status == GAME_MODE_SETUP)
578 RedrawSetupScreenAfterFullscreenToggle();
583 #if defined(PLATFORM_ANDROID)
586 int new_display_width = event->data1;
587 int new_display_height = event->data2;
589 // if fullscreen display size has changed, device has been rotated
590 if (new_display_width != video.display_width ||
591 new_display_height != video.display_height)
593 video.display_width = new_display_width;
594 video.display_height = new_display_height;
596 SDLSetScreenProperties();
603 #define NUM_TOUCH_FINGERS 3
608 SDL_FingerID finger_id;
611 } touch_info[NUM_TOUCH_FINGERS];
613 void HandleFingerEvent(FingerEvent *event)
615 static Key motion_key_x = KSYM_UNDEFINED;
616 static Key motion_key_y = KSYM_UNDEFINED;
617 static Key button_key = KSYM_UNDEFINED;
618 static float motion_x1, motion_y1;
619 static float button_x1, button_y1;
620 static SDL_FingerID motion_id = -1;
621 static SDL_FingerID button_id = -1;
622 int move_trigger_distance_percent = setup.touch.move_distance;
623 int drop_trigger_distance_percent = setup.touch.drop_distance;
624 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
625 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
626 float event_x = event->x;
627 float event_y = event->y;
629 #if DEBUG_EVENTS_FINGER
630 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
631 event->type == EVENT_FINGERPRESS ? "pressed" :
632 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
636 event->dx, event->dy,
640 if (game_status != GAME_MODE_PLAYING)
643 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
646 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
648 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
650 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
652 event_y = (event_y - ypos) / (1 - ypos);
654 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
655 event_y > 2.0 / 3.0 && event_y < 1 ?
656 setup.input[0].key.snap :
657 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
658 event_y > 2.0 / 3.0 && event_y < 1 ?
659 setup.input[0].key.drop :
660 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
661 event_y > 0 && event_y < 1.0 / 3.0 ?
662 setup.input[0].key.up :
663 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
664 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
665 setup.input[0].key.left :
666 event_x > 8.0 / 9.0 && event_x < 1 &&
667 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
668 setup.input[0].key.right :
669 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
670 event_y > 2.0 / 3.0 && event_y < 1 ?
671 setup.input[0].key.down :
674 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
678 // for any touch input event, enable overlay buttons (if activated)
679 SetOverlayEnabled(TRUE);
681 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
682 getKeyNameFromKey(key), key_status_name, event->fingerId);
684 // check if we already know this touch event's finger id
685 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
687 if (touch_info[i].touched &&
688 touch_info[i].finger_id == event->fingerId)
690 // Error(ERR_DEBUG, "MARK 1: %d", i);
696 if (i >= NUM_TOUCH_FINGERS)
698 if (key_status == KEY_PRESSED)
700 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
702 // unknown finger id -- get new, empty slot, if available
703 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
705 if (touch_info[i].counter < oldest_counter)
708 oldest_counter = touch_info[i].counter;
710 // Error(ERR_DEBUG, "MARK 2: %d", i);
713 if (!touch_info[i].touched)
715 // Error(ERR_DEBUG, "MARK 3: %d", i);
721 if (i >= NUM_TOUCH_FINGERS)
723 // all slots allocated -- use oldest slot
726 // Error(ERR_DEBUG, "MARK 4: %d", i);
731 // release of previously unknown key (should not happen)
733 if (key != KSYM_UNDEFINED)
735 HandleKey(key, KEY_RELEASED);
737 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
738 getKeyNameFromKey(key), "KEY_RELEASED", i);
743 if (i < NUM_TOUCH_FINGERS)
745 if (key_status == KEY_PRESSED)
747 if (touch_info[i].key != key)
749 if (touch_info[i].key != KSYM_UNDEFINED)
751 HandleKey(touch_info[i].key, KEY_RELEASED);
753 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
754 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
757 if (key != KSYM_UNDEFINED)
759 HandleKey(key, KEY_PRESSED);
761 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
762 getKeyNameFromKey(key), "KEY_PRESSED", i);
766 touch_info[i].touched = TRUE;
767 touch_info[i].finger_id = event->fingerId;
768 touch_info[i].counter = Counter();
769 touch_info[i].key = key;
773 if (touch_info[i].key != KSYM_UNDEFINED)
775 HandleKey(touch_info[i].key, KEY_RELEASED);
777 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
778 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
781 touch_info[i].touched = FALSE;
782 touch_info[i].finger_id = 0;
783 touch_info[i].counter = 0;
784 touch_info[i].key = 0;
791 // use touch direction control
793 if (event->type == EVENT_FINGERPRESS)
795 if (event_x > 1.0 / 3.0)
799 motion_id = event->fingerId;
804 motion_key_x = KSYM_UNDEFINED;
805 motion_key_y = KSYM_UNDEFINED;
807 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
813 button_id = event->fingerId;
818 button_key = setup.input[0].key.snap;
820 HandleKey(button_key, KEY_PRESSED);
822 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
825 else if (event->type == EVENT_FINGERRELEASE)
827 if (event->fingerId == motion_id)
831 if (motion_key_x != KSYM_UNDEFINED)
832 HandleKey(motion_key_x, KEY_RELEASED);
833 if (motion_key_y != KSYM_UNDEFINED)
834 HandleKey(motion_key_y, KEY_RELEASED);
836 motion_key_x = KSYM_UNDEFINED;
837 motion_key_y = KSYM_UNDEFINED;
839 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
841 else if (event->fingerId == button_id)
845 if (button_key != KSYM_UNDEFINED)
846 HandleKey(button_key, KEY_RELEASED);
848 button_key = KSYM_UNDEFINED;
850 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
853 else if (event->type == EVENT_FINGERMOTION)
855 if (event->fingerId == motion_id)
857 float distance_x = ABS(event_x - motion_x1);
858 float distance_y = ABS(event_y - motion_y1);
859 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
860 event_x > motion_x1 ? setup.input[0].key.right :
862 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
863 event_y > motion_y1 ? setup.input[0].key.down :
866 if (distance_x < move_trigger_distance / 2 ||
867 distance_x < distance_y)
868 new_motion_key_x = KSYM_UNDEFINED;
870 if (distance_y < move_trigger_distance / 2 ||
871 distance_y < distance_x)
872 new_motion_key_y = KSYM_UNDEFINED;
874 if (distance_x > move_trigger_distance ||
875 distance_y > move_trigger_distance)
877 if (new_motion_key_x != motion_key_x)
879 if (motion_key_x != KSYM_UNDEFINED)
880 HandleKey(motion_key_x, KEY_RELEASED);
881 if (new_motion_key_x != KSYM_UNDEFINED)
882 HandleKey(new_motion_key_x, KEY_PRESSED);
885 if (new_motion_key_y != motion_key_y)
887 if (motion_key_y != KSYM_UNDEFINED)
888 HandleKey(motion_key_y, KEY_RELEASED);
889 if (new_motion_key_y != KSYM_UNDEFINED)
890 HandleKey(new_motion_key_y, KEY_PRESSED);
896 motion_key_x = new_motion_key_x;
897 motion_key_y = new_motion_key_y;
899 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
902 else if (event->fingerId == button_id)
904 float distance_x = ABS(event_x - button_x1);
905 float distance_y = ABS(event_y - button_y1);
907 if (distance_x < drop_trigger_distance / 2 &&
908 distance_y > drop_trigger_distance)
910 if (button_key == setup.input[0].key.snap)
911 HandleKey(button_key, KEY_RELEASED);
916 button_key = setup.input[0].key.drop;
918 HandleKey(button_key, KEY_PRESSED);
920 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
926 static void HandleFollowFinger(int mx, int my, int button)
928 static int old_mx = 0, old_my = 0;
929 static Key motion_key_x = KSYM_UNDEFINED;
930 static Key motion_key_y = KSYM_UNDEFINED;
931 static boolean started_on_player = FALSE;
932 static boolean player_is_dropping = FALSE;
933 static int player_drop_count = 0;
934 static int last_player_x = -1;
935 static int last_player_y = -1;
937 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
940 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
942 touch_info[0].touched = TRUE;
943 touch_info[0].key = 0;
950 started_on_player = FALSE;
951 player_is_dropping = FALSE;
952 player_drop_count = 0;
956 motion_key_x = KSYM_UNDEFINED;
957 motion_key_y = KSYM_UNDEFINED;
959 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
962 else if (button == MB_RELEASED && touch_info[0].touched)
964 touch_info[0].touched = FALSE;
965 touch_info[0].key = 0;
970 if (motion_key_x != KSYM_UNDEFINED)
971 HandleKey(motion_key_x, KEY_RELEASED);
972 if (motion_key_y != KSYM_UNDEFINED)
973 HandleKey(motion_key_y, KEY_RELEASED);
975 if (started_on_player)
977 if (player_is_dropping)
979 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
981 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
985 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
987 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
991 motion_key_x = KSYM_UNDEFINED;
992 motion_key_y = KSYM_UNDEFINED;
994 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
997 if (touch_info[0].touched)
999 int src_x = local_player->jx;
1000 int src_y = local_player->jy;
1001 int dst_x = getLevelFromScreenX(old_mx);
1002 int dst_y = getLevelFromScreenY(old_my);
1003 int dx = dst_x - src_x;
1004 int dy = dst_y - src_y;
1005 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
1006 dx > 0 ? setup.input[0].key.right :
1008 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
1009 dy > 0 ? setup.input[0].key.down :
1012 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1013 (last_player_x != local_player->jx ||
1014 last_player_y != local_player->jy))
1016 // in case of asymmetric diagonal movement, use "preferred" direction
1018 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1020 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1021 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1023 local_player->last_move_dir = last_move_dir;
1025 // (required to prevent accidentally forcing direction for next movement)
1026 last_player_x = local_player->jx;
1027 last_player_y = local_player->jy;
1030 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1032 started_on_player = TRUE;
1033 player_drop_count = getPlayerInventorySize(0);
1034 player_is_dropping = (player_drop_count > 0);
1036 if (player_is_dropping)
1038 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1040 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1044 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1046 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1049 else if (dx != 0 || dy != 0)
1051 if (player_is_dropping &&
1052 player_drop_count == getPlayerInventorySize(0))
1054 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1056 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1057 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1059 player_is_dropping = FALSE;
1063 if (new_motion_key_x != motion_key_x)
1065 Error(ERR_DEBUG, "---------- %s %s ----------",
1066 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1067 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1069 if (motion_key_x != KSYM_UNDEFINED)
1070 HandleKey(motion_key_x, KEY_RELEASED);
1071 if (new_motion_key_x != KSYM_UNDEFINED)
1072 HandleKey(new_motion_key_x, KEY_PRESSED);
1075 if (new_motion_key_y != motion_key_y)
1077 Error(ERR_DEBUG, "---------- %s %s ----------",
1078 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1079 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1081 if (motion_key_y != KSYM_UNDEFINED)
1082 HandleKey(motion_key_y, KEY_RELEASED);
1083 if (new_motion_key_y != KSYM_UNDEFINED)
1084 HandleKey(new_motion_key_y, KEY_PRESSED);
1087 motion_key_x = new_motion_key_x;
1088 motion_key_y = new_motion_key_y;
1092 static boolean checkTextInputKeyModState()
1094 // when playing, only handle raw key events and ignore text input
1095 if (game_status == GAME_MODE_PLAYING)
1098 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1101 void HandleTextEvent(TextEvent *event)
1103 char *text = event->text;
1104 Key key = getKeyFromKeyName(text);
1106 #if DEBUG_EVENTS_TEXT
1107 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1110 text[0], (int)(text[0]),
1112 getKeyNameFromKey(key),
1116 #if !defined(HAS_SCREEN_KEYBOARD)
1117 // non-mobile devices: only handle key input with modifier keys pressed here
1118 // (every other key input is handled directly as physical key input event)
1119 if (!checkTextInputKeyModState())
1123 // process text input as "classic" (with uppercase etc.) key input event
1124 HandleKey(key, KEY_PRESSED);
1125 HandleKey(key, KEY_RELEASED);
1128 void HandlePauseResumeEvent(PauseResumeEvent *event)
1130 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1134 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1142 void HandleKeyEvent(KeyEvent *event)
1144 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1145 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1146 Key key = GetEventKey(event, with_modifiers);
1147 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1149 #if DEBUG_EVENTS_KEY
1150 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1151 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1152 event->keysym.scancode,
1157 getKeyNameFromKey(key));
1160 #if defined(PLATFORM_ANDROID)
1161 if (key == KSYM_Back)
1163 // always map the "back" button to the "escape" key on Android devices
1168 // for any key event other than "back" button, disable overlay buttons
1169 SetOverlayEnabled(FALSE);
1173 HandleKeyModState(keymod, key_status);
1175 #if defined(TARGET_SDL2)
1176 // only handle raw key input without text modifier keys pressed
1177 if (!checkTextInputKeyModState())
1178 HandleKey(key, key_status);
1180 HandleKey(key, key_status);
1184 void HandleFocusEvent(FocusChangeEvent *event)
1186 static int old_joystick_status = -1;
1188 if (event->type == EVENT_FOCUSOUT)
1190 KeyboardAutoRepeatOn();
1191 old_joystick_status = joystick.status;
1192 joystick.status = JOYSTICK_NOT_AVAILABLE;
1194 ClearPlayerAction();
1196 else if (event->type == EVENT_FOCUSIN)
1198 /* When there are two Rocks'n'Diamonds windows which overlap and
1199 the player moves the pointer from one game window to the other,
1200 a 'FocusOut' event is generated for the window the pointer is
1201 leaving and a 'FocusIn' event is generated for the window the
1202 pointer is entering. In some cases, it can happen that the
1203 'FocusIn' event is handled by the one game process before the
1204 'FocusOut' event by the other game process. In this case the
1205 X11 environment would end up with activated keyboard auto repeat,
1206 because unfortunately this is a global setting and not (which
1207 would be far better) set for each X11 window individually.
1208 The effect would be keyboard auto repeat while playing the game
1209 (game_status == GAME_MODE_PLAYING), which is not desired.
1210 To avoid this special case, we just wait 1/10 second before
1211 processing the 'FocusIn' event.
1214 if (game_status == GAME_MODE_PLAYING)
1217 KeyboardAutoRepeatOffUnlessAutoplay();
1220 if (old_joystick_status != -1)
1221 joystick.status = old_joystick_status;
1225 void HandleClientMessageEvent(ClientMessageEvent *event)
1227 if (CheckCloseWindowEvent(event))
1231 void HandleWindowManagerEvent(Event *event)
1233 #if defined(TARGET_SDL)
1234 SDLHandleWindowManagerEvent(event);
1238 void HandleButton(int mx, int my, int button, int button_nr)
1240 static int old_mx = 0, old_my = 0;
1241 boolean button_hold = FALSE;
1247 button_nr = -button_nr;
1256 #if defined(PLATFORM_ANDROID)
1257 // when playing, only handle gadgets when using "follow finger" controls
1258 boolean handle_gadgets =
1259 (game_status != GAME_MODE_PLAYING ||
1260 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1262 if (handle_gadgets &&
1263 HandleGadgets(mx, my, button))
1265 /* do not handle this button event anymore */
1266 mx = my = -32; /* force mouse event to be outside screen tiles */
1269 if (HandleGadgets(mx, my, button))
1271 /* do not handle this button event anymore */
1272 mx = my = -32; /* force mouse event to be outside screen tiles */
1276 if (HandleGlobalAnimClicks(mx, my, button))
1278 /* do not handle this button event anymore */
1279 return; /* force mouse event not to be handled at all */
1282 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1285 /* do not use scroll wheel button events for anything other than gadgets */
1286 if (IS_WHEEL_BUTTON(button_nr))
1289 switch (game_status)
1291 case GAME_MODE_TITLE:
1292 HandleTitleScreen(mx, my, 0, 0, button);
1295 case GAME_MODE_MAIN:
1296 HandleMainMenu(mx, my, 0, 0, button);
1299 case GAME_MODE_PSEUDO_TYPENAME:
1300 HandleTypeName(0, KSYM_Return);
1303 case GAME_MODE_LEVELS:
1304 HandleChooseLevelSet(mx, my, 0, 0, button);
1307 case GAME_MODE_LEVELNR:
1308 HandleChooseLevelNr(mx, my, 0, 0, button);
1311 case GAME_MODE_SCORES:
1312 HandleHallOfFame(0, 0, 0, 0, button);
1315 case GAME_MODE_EDITOR:
1316 HandleLevelEditorIdle();
1319 case GAME_MODE_INFO:
1320 HandleInfoScreen(mx, my, 0, 0, button);
1323 case GAME_MODE_SETUP:
1324 HandleSetupScreen(mx, my, 0, 0, button);
1327 case GAME_MODE_PLAYING:
1328 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1329 ClickElement(mx, my, button);
1330 #if defined(TARGET_SDL2)
1332 HandleFollowFinger(mx, my, button);
1336 if (button == MB_PRESSED && !motion_status && !button_hold &&
1337 IN_GFX_FIELD_PLAY(mx, my) && GetKeyModState() & KMOD_Control)
1338 DumpTileFromScreen(mx, my);
1348 static boolean is_string_suffix(char *string, char *suffix)
1350 int string_len = strlen(string);
1351 int suffix_len = strlen(suffix);
1353 if (suffix_len > string_len)
1356 return (strEqual(&string[string_len - suffix_len], suffix));
1359 #define MAX_CHEAT_INPUT_LEN 32
1361 static void HandleKeysSpecial(Key key)
1363 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1364 char letter = getCharFromKey(key);
1365 int cheat_input_len = strlen(cheat_input);
1371 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1373 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1374 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1376 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1379 cheat_input[cheat_input_len++] = letter;
1380 cheat_input[cheat_input_len] = '\0';
1382 #if DEBUG_EVENTS_KEY
1383 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1386 if (game_status == GAME_MODE_MAIN)
1388 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1389 is_string_suffix(cheat_input, ":ist"))
1391 InsertSolutionTape();
1393 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1394 is_string_suffix(cheat_input, ":rg"))
1396 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1399 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1400 is_string_suffix(cheat_input, ":rs"))
1402 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1405 else if (is_string_suffix(cheat_input, ":reload-music") ||
1406 is_string_suffix(cheat_input, ":rm"))
1408 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1411 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1412 is_string_suffix(cheat_input, ":ra"))
1414 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1415 1 << ARTWORK_TYPE_SOUNDS |
1416 1 << ARTWORK_TYPE_MUSIC);
1419 else if (is_string_suffix(cheat_input, ":dump-level") ||
1420 is_string_suffix(cheat_input, ":dl"))
1424 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1425 is_string_suffix(cheat_input, ":dt"))
1429 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1430 is_string_suffix(cheat_input, ":ft"))
1432 /* fix single-player tapes that contain player input for more than one
1433 player (due to a bug in 3.3.1.2 and earlier versions), which results
1434 in playing levels with more than one player in multi-player mode,
1435 even though the tape was originally recorded in single-player mode */
1437 /* remove player input actions for all players but the first one */
1438 for (i = 1; i < MAX_PLAYERS; i++)
1439 tape.player_participates[i] = FALSE;
1441 tape.changed = TRUE;
1443 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1444 is_string_suffix(cheat_input, ":snl"))
1446 SaveNativeLevel(&level);
1448 else if (is_string_suffix(cheat_input, ":frames-per-second") ||
1449 is_string_suffix(cheat_input, ":fps"))
1451 global.show_frames_per_second = !global.show_frames_per_second;
1454 else if (game_status == GAME_MODE_PLAYING)
1457 if (is_string_suffix(cheat_input, ".q"))
1458 DEBUG_SetMaximumDynamite();
1461 else if (game_status == GAME_MODE_EDITOR)
1463 if (is_string_suffix(cheat_input, ":dump-brush") ||
1464 is_string_suffix(cheat_input, ":DB"))
1468 else if (is_string_suffix(cheat_input, ":DDB"))
1475 void HandleKeysDebug(Key key)
1480 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1482 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
1484 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1486 if (key == setup.debug.frame_delay_key[i] &&
1487 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1489 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1490 setup.debug.frame_delay[i] : setup.game_frame_delay);
1492 if (!setup.debug.frame_delay_game_only)
1493 MenuFrameDelay = GameFrameDelay;
1495 SetVideoFrameDelay(GameFrameDelay);
1497 if (GameFrameDelay > ONE_SECOND_DELAY)
1498 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1499 else if (GameFrameDelay != 0)
1500 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1501 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1502 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1504 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1511 if (game_status == GAME_MODE_PLAYING)
1515 options.debug = !options.debug;
1517 Error(ERR_DEBUG, "debug mode %s",
1518 (options.debug ? "enabled" : "disabled"));
1520 else if (key == KSYM_v)
1522 Error(ERR_DEBUG, "currently using game engine version %d",
1523 game.engine_version);
1529 void HandleKey(Key key, int key_status)
1531 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1532 static boolean ignore_repeated_key = FALSE;
1533 static struct SetupKeyboardInfo ski;
1534 static struct SetupShortcutInfo ssi;
1543 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1544 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1545 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1546 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1547 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1548 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1553 #if defined(TARGET_SDL2)
1554 /* map special keys (media keys / remote control buttons) to default keys */
1555 if (key == KSYM_PlayPause)
1557 else if (key == KSYM_Select)
1561 HandleSpecialGameControllerKeys(key, key_status);
1563 if (game_status == GAME_MODE_PLAYING)
1565 /* only needed for single-step tape recording mode */
1566 static boolean has_snapped[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE };
1569 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1571 byte key_action = 0;
1573 if (setup.input[pnr].use_joystick)
1576 ski = setup.input[pnr].key;
1578 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1579 if (key == *key_info[i].key_custom)
1580 key_action |= key_info[i].action;
1582 /* use combined snap+direction keys for the first player only */
1585 ssi = setup.shortcut;
1587 for (i = 0; i < NUM_DIRECTIONS; i++)
1588 if (key == *key_info[i].key_snap)
1589 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1592 if (key_status == KEY_PRESSED)
1593 stored_player[pnr].action |= key_action;
1595 stored_player[pnr].action &= ~key_action;
1597 if (tape.single_step && tape.recording && tape.pausing)
1599 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1601 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1603 /* if snap key already pressed, keep pause mode when releasing */
1604 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1605 has_snapped[pnr] = TRUE;
1607 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1609 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1611 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1612 getRedDiskReleaseFlag_SP() == 0)
1614 /* add a single inactive frame before dropping starts */
1615 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1616 stored_player[pnr].force_dropping = TRUE;
1619 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON_SNAP)
1621 /* if snap key was pressed without direction, leave pause mode */
1622 if (!has_snapped[pnr])
1623 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1625 has_snapped[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 ||
1901 level.game_engine_type == GAME_ENGINE_TYPE_MM))
1903 HandleButton(0, 0, button_status, -button_status);
1910 #if defined(NETWORK_AVALIABLE)
1911 if (options.network)
1915 switch (game_status)
1917 case GAME_MODE_MAIN:
1918 DrawPreviewLevelAnimation();
1921 case GAME_MODE_EDITOR:
1922 HandleLevelEditorIdle();
1925 #if defined(TARGET_SDL2)
1926 case GAME_MODE_PLAYING:
1927 HandleFollowFinger(-1, -1, -1);
1936 static int HandleJoystickForAllPlayers()
1940 boolean no_joysticks_configured = TRUE;
1941 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1942 static byte joy_action_last[MAX_PLAYERS];
1944 for (i = 0; i < MAX_PLAYERS; i++)
1945 if (setup.input[i].use_joystick)
1946 no_joysticks_configured = FALSE;
1948 /* if no joysticks configured, map connected joysticks to players */
1949 if (no_joysticks_configured)
1950 use_as_joystick_nr = TRUE;
1952 for (i = 0; i < MAX_PLAYERS; i++)
1954 byte joy_action = 0;
1956 joy_action = JoystickExt(i, use_as_joystick_nr);
1957 result |= joy_action;
1959 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1960 joy_action != joy_action_last[i])
1961 stored_player[i].action = joy_action;
1963 joy_action_last[i] = joy_action;
1969 void HandleJoystick()
1971 int joystick = HandleJoystickForAllPlayers();
1972 int keyboard = key_joystick_mapping;
1973 int joy = (joystick | keyboard);
1974 int left = joy & JOY_LEFT;
1975 int right = joy & JOY_RIGHT;
1976 int up = joy & JOY_UP;
1977 int down = joy & JOY_DOWN;
1978 int button = joy & JOY_BUTTON;
1979 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1980 int dx = (left ? -1 : right ? 1 : 0);
1981 int dy = (up ? -1 : down ? 1 : 0);
1983 if (HandleGlobalAnimClicks(-1, -1, newbutton))
1985 /* do not handle this button event anymore */
1989 switch (game_status)
1991 case GAME_MODE_TITLE:
1992 case GAME_MODE_MAIN:
1993 case GAME_MODE_LEVELS:
1994 case GAME_MODE_LEVELNR:
1995 case GAME_MODE_SETUP:
1996 case GAME_MODE_INFO:
1998 static unsigned int joystickmove_delay = 0;
1999 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
2000 static int joystick_last = 0;
2002 if (joystick && !button &&
2003 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2005 /* delay joystick actions if buttons/axes continually pressed */
2006 newbutton = dx = dy = 0;
2010 /* start with longer delay, then continue with shorter delay */
2011 if (joystick != joystick_last)
2012 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2014 joystickmove_delay_value = GADGET_FRAME_DELAY;
2017 if (game_status == GAME_MODE_TITLE)
2018 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2019 else if (game_status == GAME_MODE_MAIN)
2020 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2021 else if (game_status == GAME_MODE_LEVELS)
2022 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2023 else if (game_status == GAME_MODE_LEVELNR)
2024 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2025 else if (game_status == GAME_MODE_SETUP)
2026 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2027 else if (game_status == GAME_MODE_INFO)
2028 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2030 joystick_last = joystick;
2035 case GAME_MODE_SCORES:
2036 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2039 case GAME_MODE_PLAYING:
2040 if (tape.playing || keyboard)
2041 newbutton = ((joy & JOY_BUTTON) != 0);
2043 if (newbutton && AllPlayersGone)
2050 if (tape.recording && tape.pausing)
2052 if (joystick & JOY_ACTION)
2053 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2063 void HandleSpecialGameControllerButtons(Event *event)
2065 #if defined(TARGET_SDL2)
2066 switch (event->type)
2068 case SDL_CONTROLLERBUTTONDOWN:
2069 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2070 HandleKey(KSYM_space, KEY_PRESSED);
2071 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2072 HandleKey(KSYM_Escape, KEY_PRESSED);
2076 case SDL_CONTROLLERBUTTONUP:
2077 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2078 HandleKey(KSYM_space, KEY_RELEASED);
2079 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2080 HandleKey(KSYM_Escape, KEY_RELEASED);
2087 void HandleSpecialGameControllerKeys(Key key, int key_status)
2089 #if defined(TARGET_SDL2)
2090 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2091 int button = SDL_CONTROLLER_BUTTON_INVALID;
2093 /* map keys to joystick buttons (special hack for Amazon Fire TV remote) */
2094 if (key == KSYM_Rewind)
2095 button = SDL_CONTROLLER_BUTTON_A;
2096 else if (key == KSYM_FastForward || key == KSYM_Menu)
2097 button = SDL_CONTROLLER_BUTTON_B;
2099 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2103 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2104 SDL_CONTROLLERBUTTONUP);
2106 event.cbutton.which = 0; /* first joystick (Amazon Fire TV remote) */
2107 event.cbutton.button = button;
2108 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2111 HandleJoystickEvent(&event);