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 ClearPlayerMouseAction()
375 local_player->mouse_action.lx = 0;
376 local_player->mouse_action.ly = 0;
377 local_player->mouse_action.button = 0;
380 void ClearPlayerAction()
384 /* simulate key release events for still pressed keys */
385 key_joystick_mapping = 0;
386 for (i = 0; i < MAX_PLAYERS; i++)
387 stored_player[i].action = 0;
389 ClearJoystickState();
390 ClearPlayerMouseAction();
393 void SetPlayerMouseAction(int mx, int my, int button)
395 int lx = getLevelFromScreenX(mx);
396 int ly = getLevelFromScreenY(my);
398 ClearPlayerMouseAction();
400 if (!IN_GFX_FIELD_PLAY(mx, my) || !IN_LEV_FIELD(lx, ly))
403 local_player->mouse_action.lx = lx;
404 local_player->mouse_action.ly = ly;
405 local_player->mouse_action.button = button;
408 void SleepWhileUnmapped()
410 boolean window_unmapped = TRUE;
412 KeyboardAutoRepeatOn();
414 while (window_unmapped)
418 if (!WaitValidEvent(&event))
423 case EVENT_BUTTONRELEASE:
424 button_status = MB_RELEASED;
427 case EVENT_KEYRELEASE:
428 key_joystick_mapping = 0;
431 #if defined(TARGET_SDL2)
432 case SDL_CONTROLLERBUTTONUP:
433 HandleJoystickEvent(&event);
434 key_joystick_mapping = 0;
438 case EVENT_MAPNOTIFY:
439 window_unmapped = FALSE;
442 case EVENT_UNMAPNOTIFY:
443 /* this is only to surely prevent the 'should not happen' case
444 * of recursively looping between 'SleepWhileUnmapped()' and
445 * 'HandleOtherEvents()' which usually calls this funtion.
450 HandleOtherEvents(&event);
455 if (game_status == GAME_MODE_PLAYING)
456 KeyboardAutoRepeatOffUnlessAutoplay();
459 void HandleExposeEvent(ExposeEvent *event)
463 void HandleButtonEvent(ButtonEvent *event)
465 #if DEBUG_EVENTS_BUTTON
466 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
468 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
472 #if defined(HAS_SCREEN_KEYBOARD)
473 if (video.shifted_up)
474 event->y += video.shifted_up_pos;
477 motion_status = FALSE;
479 if (event->type == EVENT_BUTTONPRESS)
480 button_status = event->button;
482 button_status = MB_RELEASED;
484 HandleButton(event->x, event->y, button_status, event->button);
487 void HandleMotionEvent(MotionEvent *event)
489 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
492 motion_status = TRUE;
494 #if DEBUG_EVENTS_MOTION
495 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
496 button_status, event->x, event->y);
499 HandleButton(event->x, event->y, button_status, button_status);
502 #if defined(TARGET_SDL2)
504 void HandleWheelEvent(WheelEvent *event)
508 #if DEBUG_EVENTS_WHEEL
510 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
511 event->which, event->x, event->y);
513 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
514 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
515 event->which, event->x, event->y,
516 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
517 "SDL_MOUSEWHEEL_FLIPPED"));
521 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
522 event->x > 0 ? MB_WHEEL_RIGHT :
523 event->y < 0 ? MB_WHEEL_DOWN :
524 event->y > 0 ? MB_WHEEL_UP : 0);
526 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
527 // accelerated mouse wheel available on Mac and Windows
528 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
530 // no accelerated mouse wheel available on Unix/Linux
531 wheel_steps = DEFAULT_WHEEL_STEPS;
534 motion_status = FALSE;
536 button_status = button_nr;
537 HandleButton(0, 0, button_status, -button_nr);
539 button_status = MB_RELEASED;
540 HandleButton(0, 0, button_status, -button_nr);
543 void HandleWindowEvent(WindowEvent *event)
545 #if DEBUG_EVENTS_WINDOW
546 int subtype = event->event;
549 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
550 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
551 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
552 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
553 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
554 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
555 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
556 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
557 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
558 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
559 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
560 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
561 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
562 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
565 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
566 event_name, event->data1, event->data2);
570 // (not needed, as the screen gets redrawn every 20 ms anyway)
571 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
572 event->event == SDL_WINDOWEVENT_RESIZED ||
573 event->event == SDL_WINDOWEVENT_EXPOSED)
577 if (event->event == SDL_WINDOWEVENT_RESIZED)
579 if (!video.fullscreen_enabled)
581 int new_window_width = event->data1;
582 int new_window_height = event->data2;
584 // if window size has changed after resizing, calculate new scaling factor
585 if (new_window_width != video.window_width ||
586 new_window_height != video.window_height)
588 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
589 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
591 // (extreme window scaling allowed, but cannot be saved permanently)
592 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
593 setup.window_scaling_percent =
594 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
595 MAX_WINDOW_SCALING_PERCENT);
597 video.window_width = new_window_width;
598 video.window_height = new_window_height;
600 if (game_status == GAME_MODE_SETUP)
601 RedrawSetupScreenAfterFullscreenToggle();
606 #if defined(PLATFORM_ANDROID)
609 int new_display_width = event->data1;
610 int new_display_height = event->data2;
612 // if fullscreen display size has changed, device has been rotated
613 if (new_display_width != video.display_width ||
614 new_display_height != video.display_height)
616 video.display_width = new_display_width;
617 video.display_height = new_display_height;
619 SDLSetScreenProperties();
626 #define NUM_TOUCH_FINGERS 3
631 SDL_FingerID finger_id;
634 } touch_info[NUM_TOUCH_FINGERS];
636 void HandleFingerEvent(FingerEvent *event)
638 static Key motion_key_x = KSYM_UNDEFINED;
639 static Key motion_key_y = KSYM_UNDEFINED;
640 static Key button_key = KSYM_UNDEFINED;
641 static float motion_x1, motion_y1;
642 static float button_x1, button_y1;
643 static SDL_FingerID motion_id = -1;
644 static SDL_FingerID button_id = -1;
645 int move_trigger_distance_percent = setup.touch.move_distance;
646 int drop_trigger_distance_percent = setup.touch.drop_distance;
647 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
648 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
649 float event_x = event->x;
650 float event_y = event->y;
652 #if DEBUG_EVENTS_FINGER
653 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
654 event->type == EVENT_FINGERPRESS ? "pressed" :
655 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
659 event->dx, event->dy,
663 if (game_status != GAME_MODE_PLAYING)
666 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
669 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
671 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
673 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
675 event_y = (event_y - ypos) / (1 - ypos);
677 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
678 event_y > 2.0 / 3.0 && event_y < 1 ?
679 setup.input[0].key.snap :
680 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
681 event_y > 2.0 / 3.0 && event_y < 1 ?
682 setup.input[0].key.drop :
683 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
684 event_y > 0 && event_y < 1.0 / 3.0 ?
685 setup.input[0].key.up :
686 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
687 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
688 setup.input[0].key.left :
689 event_x > 8.0 / 9.0 && event_x < 1 &&
690 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
691 setup.input[0].key.right :
692 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
693 event_y > 2.0 / 3.0 && event_y < 1 ?
694 setup.input[0].key.down :
697 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
701 // for any touch input event, enable overlay buttons (if activated)
702 SetOverlayEnabled(TRUE);
704 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
705 getKeyNameFromKey(key), key_status_name, event->fingerId);
707 // check if we already know this touch event's finger id
708 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
710 if (touch_info[i].touched &&
711 touch_info[i].finger_id == event->fingerId)
713 // Error(ERR_DEBUG, "MARK 1: %d", i);
719 if (i >= NUM_TOUCH_FINGERS)
721 if (key_status == KEY_PRESSED)
723 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
725 // unknown finger id -- get new, empty slot, if available
726 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
728 if (touch_info[i].counter < oldest_counter)
731 oldest_counter = touch_info[i].counter;
733 // Error(ERR_DEBUG, "MARK 2: %d", i);
736 if (!touch_info[i].touched)
738 // Error(ERR_DEBUG, "MARK 3: %d", i);
744 if (i >= NUM_TOUCH_FINGERS)
746 // all slots allocated -- use oldest slot
749 // Error(ERR_DEBUG, "MARK 4: %d", i);
754 // release of previously unknown key (should not happen)
756 if (key != KSYM_UNDEFINED)
758 HandleKey(key, KEY_RELEASED);
760 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
761 getKeyNameFromKey(key), "KEY_RELEASED", i);
766 if (i < NUM_TOUCH_FINGERS)
768 if (key_status == KEY_PRESSED)
770 if (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] [2]",
777 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
780 if (key != KSYM_UNDEFINED)
782 HandleKey(key, KEY_PRESSED);
784 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
785 getKeyNameFromKey(key), "KEY_PRESSED", i);
789 touch_info[i].touched = TRUE;
790 touch_info[i].finger_id = event->fingerId;
791 touch_info[i].counter = Counter();
792 touch_info[i].key = key;
796 if (touch_info[i].key != KSYM_UNDEFINED)
798 HandleKey(touch_info[i].key, KEY_RELEASED);
800 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
801 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
804 touch_info[i].touched = FALSE;
805 touch_info[i].finger_id = 0;
806 touch_info[i].counter = 0;
807 touch_info[i].key = 0;
814 // use touch direction control
816 if (event->type == EVENT_FINGERPRESS)
818 if (event_x > 1.0 / 3.0)
822 motion_id = event->fingerId;
827 motion_key_x = KSYM_UNDEFINED;
828 motion_key_y = KSYM_UNDEFINED;
830 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
836 button_id = event->fingerId;
841 button_key = setup.input[0].key.snap;
843 HandleKey(button_key, KEY_PRESSED);
845 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
848 else if (event->type == EVENT_FINGERRELEASE)
850 if (event->fingerId == motion_id)
854 if (motion_key_x != KSYM_UNDEFINED)
855 HandleKey(motion_key_x, KEY_RELEASED);
856 if (motion_key_y != KSYM_UNDEFINED)
857 HandleKey(motion_key_y, KEY_RELEASED);
859 motion_key_x = KSYM_UNDEFINED;
860 motion_key_y = KSYM_UNDEFINED;
862 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
864 else if (event->fingerId == button_id)
868 if (button_key != KSYM_UNDEFINED)
869 HandleKey(button_key, KEY_RELEASED);
871 button_key = KSYM_UNDEFINED;
873 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
876 else if (event->type == EVENT_FINGERMOTION)
878 if (event->fingerId == motion_id)
880 float distance_x = ABS(event_x - motion_x1);
881 float distance_y = ABS(event_y - motion_y1);
882 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
883 event_x > motion_x1 ? setup.input[0].key.right :
885 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
886 event_y > motion_y1 ? setup.input[0].key.down :
889 if (distance_x < move_trigger_distance / 2 ||
890 distance_x < distance_y)
891 new_motion_key_x = KSYM_UNDEFINED;
893 if (distance_y < move_trigger_distance / 2 ||
894 distance_y < distance_x)
895 new_motion_key_y = KSYM_UNDEFINED;
897 if (distance_x > move_trigger_distance ||
898 distance_y > move_trigger_distance)
900 if (new_motion_key_x != motion_key_x)
902 if (motion_key_x != KSYM_UNDEFINED)
903 HandleKey(motion_key_x, KEY_RELEASED);
904 if (new_motion_key_x != KSYM_UNDEFINED)
905 HandleKey(new_motion_key_x, KEY_PRESSED);
908 if (new_motion_key_y != motion_key_y)
910 if (motion_key_y != KSYM_UNDEFINED)
911 HandleKey(motion_key_y, KEY_RELEASED);
912 if (new_motion_key_y != KSYM_UNDEFINED)
913 HandleKey(new_motion_key_y, KEY_PRESSED);
919 motion_key_x = new_motion_key_x;
920 motion_key_y = new_motion_key_y;
922 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
925 else if (event->fingerId == button_id)
927 float distance_x = ABS(event_x - button_x1);
928 float distance_y = ABS(event_y - button_y1);
930 if (distance_x < drop_trigger_distance / 2 &&
931 distance_y > drop_trigger_distance)
933 if (button_key == setup.input[0].key.snap)
934 HandleKey(button_key, KEY_RELEASED);
939 button_key = setup.input[0].key.drop;
941 HandleKey(button_key, KEY_PRESSED);
943 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
949 static void HandleFollowFinger(int mx, int my, int button)
951 static int old_mx = 0, old_my = 0;
952 static Key motion_key_x = KSYM_UNDEFINED;
953 static Key motion_key_y = KSYM_UNDEFINED;
954 static boolean started_on_player = FALSE;
955 static boolean player_is_dropping = FALSE;
956 static int player_drop_count = 0;
957 static int last_player_x = -1;
958 static int last_player_y = -1;
960 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
963 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
965 touch_info[0].touched = TRUE;
966 touch_info[0].key = 0;
973 started_on_player = FALSE;
974 player_is_dropping = FALSE;
975 player_drop_count = 0;
979 motion_key_x = KSYM_UNDEFINED;
980 motion_key_y = KSYM_UNDEFINED;
982 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
985 else if (button == MB_RELEASED && touch_info[0].touched)
987 touch_info[0].touched = FALSE;
988 touch_info[0].key = 0;
993 if (motion_key_x != KSYM_UNDEFINED)
994 HandleKey(motion_key_x, KEY_RELEASED);
995 if (motion_key_y != KSYM_UNDEFINED)
996 HandleKey(motion_key_y, KEY_RELEASED);
998 if (started_on_player)
1000 if (player_is_dropping)
1002 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
1004 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1008 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
1010 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
1014 motion_key_x = KSYM_UNDEFINED;
1015 motion_key_y = KSYM_UNDEFINED;
1017 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
1020 if (touch_info[0].touched)
1022 int src_x = local_player->jx;
1023 int src_y = local_player->jy;
1024 int dst_x = getLevelFromScreenX(old_mx);
1025 int dst_y = getLevelFromScreenY(old_my);
1026 int dx = dst_x - src_x;
1027 int dy = dst_y - src_y;
1028 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
1029 dx > 0 ? setup.input[0].key.right :
1031 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
1032 dy > 0 ? setup.input[0].key.down :
1035 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1036 (last_player_x != local_player->jx ||
1037 last_player_y != local_player->jy))
1039 // in case of asymmetric diagonal movement, use "preferred" direction
1041 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1043 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1044 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1046 local_player->last_move_dir = last_move_dir;
1048 // (required to prevent accidentally forcing direction for next movement)
1049 last_player_x = local_player->jx;
1050 last_player_y = local_player->jy;
1053 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1055 started_on_player = TRUE;
1056 player_drop_count = getPlayerInventorySize(0);
1057 player_is_dropping = (player_drop_count > 0);
1059 if (player_is_dropping)
1061 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1063 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1067 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1069 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1072 else if (dx != 0 || dy != 0)
1074 if (player_is_dropping &&
1075 player_drop_count == getPlayerInventorySize(0))
1077 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1079 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1080 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1082 player_is_dropping = FALSE;
1086 if (new_motion_key_x != motion_key_x)
1088 Error(ERR_DEBUG, "---------- %s %s ----------",
1089 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1090 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1092 if (motion_key_x != KSYM_UNDEFINED)
1093 HandleKey(motion_key_x, KEY_RELEASED);
1094 if (new_motion_key_x != KSYM_UNDEFINED)
1095 HandleKey(new_motion_key_x, KEY_PRESSED);
1098 if (new_motion_key_y != motion_key_y)
1100 Error(ERR_DEBUG, "---------- %s %s ----------",
1101 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1102 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1104 if (motion_key_y != KSYM_UNDEFINED)
1105 HandleKey(motion_key_y, KEY_RELEASED);
1106 if (new_motion_key_y != KSYM_UNDEFINED)
1107 HandleKey(new_motion_key_y, KEY_PRESSED);
1110 motion_key_x = new_motion_key_x;
1111 motion_key_y = new_motion_key_y;
1115 static boolean checkTextInputKeyModState()
1117 // when playing, only handle raw key events and ignore text input
1118 if (game_status == GAME_MODE_PLAYING)
1121 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1124 void HandleTextEvent(TextEvent *event)
1126 char *text = event->text;
1127 Key key = getKeyFromKeyName(text);
1129 #if DEBUG_EVENTS_TEXT
1130 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1133 text[0], (int)(text[0]),
1135 getKeyNameFromKey(key),
1139 #if !defined(HAS_SCREEN_KEYBOARD)
1140 // non-mobile devices: only handle key input with modifier keys pressed here
1141 // (every other key input is handled directly as physical key input event)
1142 if (!checkTextInputKeyModState())
1146 // process text input as "classic" (with uppercase etc.) key input event
1147 HandleKey(key, KEY_PRESSED);
1148 HandleKey(key, KEY_RELEASED);
1151 void HandlePauseResumeEvent(PauseResumeEvent *event)
1153 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1157 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1165 void HandleKeyEvent(KeyEvent *event)
1167 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1168 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1169 Key key = GetEventKey(event, with_modifiers);
1170 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1172 #if DEBUG_EVENTS_KEY
1173 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1174 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1175 event->keysym.scancode,
1180 getKeyNameFromKey(key));
1183 #if defined(PLATFORM_ANDROID)
1184 if (key == KSYM_Back)
1186 // always map the "back" button to the "escape" key on Android devices
1191 // for any key event other than "back" button, disable overlay buttons
1192 SetOverlayEnabled(FALSE);
1196 HandleKeyModState(keymod, key_status);
1198 #if defined(TARGET_SDL2)
1199 // only handle raw key input without text modifier keys pressed
1200 if (!checkTextInputKeyModState())
1201 HandleKey(key, key_status);
1203 HandleKey(key, key_status);
1207 void HandleFocusEvent(FocusChangeEvent *event)
1209 static int old_joystick_status = -1;
1211 if (event->type == EVENT_FOCUSOUT)
1213 KeyboardAutoRepeatOn();
1214 old_joystick_status = joystick.status;
1215 joystick.status = JOYSTICK_NOT_AVAILABLE;
1217 ClearPlayerAction();
1219 else if (event->type == EVENT_FOCUSIN)
1221 /* When there are two Rocks'n'Diamonds windows which overlap and
1222 the player moves the pointer from one game window to the other,
1223 a 'FocusOut' event is generated for the window the pointer is
1224 leaving and a 'FocusIn' event is generated for the window the
1225 pointer is entering. In some cases, it can happen that the
1226 'FocusIn' event is handled by the one game process before the
1227 'FocusOut' event by the other game process. In this case the
1228 X11 environment would end up with activated keyboard auto repeat,
1229 because unfortunately this is a global setting and not (which
1230 would be far better) set for each X11 window individually.
1231 The effect would be keyboard auto repeat while playing the game
1232 (game_status == GAME_MODE_PLAYING), which is not desired.
1233 To avoid this special case, we just wait 1/10 second before
1234 processing the 'FocusIn' event.
1237 if (game_status == GAME_MODE_PLAYING)
1240 KeyboardAutoRepeatOffUnlessAutoplay();
1243 if (old_joystick_status != -1)
1244 joystick.status = old_joystick_status;
1248 void HandleClientMessageEvent(ClientMessageEvent *event)
1250 if (CheckCloseWindowEvent(event))
1254 void HandleWindowManagerEvent(Event *event)
1256 #if defined(TARGET_SDL)
1257 SDLHandleWindowManagerEvent(event);
1261 void HandleButton(int mx, int my, int button, int button_nr)
1263 static int old_mx = 0, old_my = 0;
1264 boolean button_hold = FALSE;
1270 button_nr = -button_nr;
1279 #if defined(PLATFORM_ANDROID)
1280 // when playing, only handle gadgets when using "follow finger" controls
1281 boolean handle_gadgets =
1282 (game_status != GAME_MODE_PLAYING ||
1283 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1285 if (handle_gadgets &&
1286 HandleGadgets(mx, my, button))
1288 /* do not handle this button event anymore */
1289 mx = my = -32; /* force mouse event to be outside screen tiles */
1292 if (HandleGadgets(mx, my, button))
1294 /* do not handle this button event anymore */
1295 mx = my = -32; /* force mouse event to be outside screen tiles */
1299 if (HandleGlobalAnimClicks(mx, my, button))
1301 /* do not handle this button event anymore */
1302 return; /* force mouse event not to be handled at all */
1305 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1308 /* do not use scroll wheel button events for anything other than gadgets */
1309 if (IS_WHEEL_BUTTON(button_nr))
1312 switch (game_status)
1314 case GAME_MODE_TITLE:
1315 HandleTitleScreen(mx, my, 0, 0, button);
1318 case GAME_MODE_MAIN:
1319 HandleMainMenu(mx, my, 0, 0, button);
1322 case GAME_MODE_PSEUDO_TYPENAME:
1323 HandleTypeName(0, KSYM_Return);
1326 case GAME_MODE_LEVELS:
1327 HandleChooseLevelSet(mx, my, 0, 0, button);
1330 case GAME_MODE_LEVELNR:
1331 HandleChooseLevelNr(mx, my, 0, 0, button);
1334 case GAME_MODE_SCORES:
1335 HandleHallOfFame(0, 0, 0, 0, button);
1338 case GAME_MODE_EDITOR:
1339 HandleLevelEditorIdle();
1342 case GAME_MODE_INFO:
1343 HandleInfoScreen(mx, my, 0, 0, button);
1346 case GAME_MODE_SETUP:
1347 HandleSetupScreen(mx, my, 0, 0, button);
1350 case GAME_MODE_PLAYING:
1351 SetPlayerMouseAction(mx, my, button);
1353 #if defined(TARGET_SDL2)
1354 HandleFollowFinger(mx, my, button);
1358 if (button == MB_PRESSED && !motion_status && !button_hold &&
1359 IN_GFX_FIELD_PLAY(mx, my) && GetKeyModState() & KMOD_Control)
1360 DumpTileFromScreen(mx, my);
1370 static boolean is_string_suffix(char *string, char *suffix)
1372 int string_len = strlen(string);
1373 int suffix_len = strlen(suffix);
1375 if (suffix_len > string_len)
1378 return (strEqual(&string[string_len - suffix_len], suffix));
1381 #define MAX_CHEAT_INPUT_LEN 32
1383 static void HandleKeysSpecial(Key key)
1385 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1386 char letter = getCharFromKey(key);
1387 int cheat_input_len = strlen(cheat_input);
1393 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1395 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1396 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1398 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1401 cheat_input[cheat_input_len++] = letter;
1402 cheat_input[cheat_input_len] = '\0';
1404 #if DEBUG_EVENTS_KEY
1405 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1408 if (game_status == GAME_MODE_MAIN)
1410 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1411 is_string_suffix(cheat_input, ":ist"))
1413 InsertSolutionTape();
1415 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1416 is_string_suffix(cheat_input, ":rg"))
1418 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1421 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1422 is_string_suffix(cheat_input, ":rs"))
1424 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1427 else if (is_string_suffix(cheat_input, ":reload-music") ||
1428 is_string_suffix(cheat_input, ":rm"))
1430 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1433 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1434 is_string_suffix(cheat_input, ":ra"))
1436 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1437 1 << ARTWORK_TYPE_SOUNDS |
1438 1 << ARTWORK_TYPE_MUSIC);
1441 else if (is_string_suffix(cheat_input, ":dump-level") ||
1442 is_string_suffix(cheat_input, ":dl"))
1446 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1447 is_string_suffix(cheat_input, ":dt"))
1451 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1452 is_string_suffix(cheat_input, ":ft"))
1454 /* fix single-player tapes that contain player input for more than one
1455 player (due to a bug in 3.3.1.2 and earlier versions), which results
1456 in playing levels with more than one player in multi-player mode,
1457 even though the tape was originally recorded in single-player mode */
1459 /* remove player input actions for all players but the first one */
1460 for (i = 1; i < MAX_PLAYERS; i++)
1461 tape.player_participates[i] = FALSE;
1463 tape.changed = TRUE;
1465 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1466 is_string_suffix(cheat_input, ":snl"))
1468 SaveNativeLevel(&level);
1470 else if (is_string_suffix(cheat_input, ":frames-per-second") ||
1471 is_string_suffix(cheat_input, ":fps"))
1473 global.show_frames_per_second = !global.show_frames_per_second;
1476 else if (game_status == GAME_MODE_PLAYING)
1479 if (is_string_suffix(cheat_input, ".q"))
1480 DEBUG_SetMaximumDynamite();
1483 else if (game_status == GAME_MODE_EDITOR)
1485 if (is_string_suffix(cheat_input, ":dump-brush") ||
1486 is_string_suffix(cheat_input, ":DB"))
1490 else if (is_string_suffix(cheat_input, ":DDB"))
1497 void HandleKeysDebug(Key key)
1502 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1504 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
1506 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1508 if (key == setup.debug.frame_delay_key[i] &&
1509 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1511 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1512 setup.debug.frame_delay[i] : setup.game_frame_delay);
1514 if (!setup.debug.frame_delay_game_only)
1515 MenuFrameDelay = GameFrameDelay;
1517 SetVideoFrameDelay(GameFrameDelay);
1519 if (GameFrameDelay > ONE_SECOND_DELAY)
1520 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1521 else if (GameFrameDelay != 0)
1522 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1523 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1524 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1526 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1533 if (game_status == GAME_MODE_PLAYING)
1537 options.debug = !options.debug;
1539 Error(ERR_DEBUG, "debug mode %s",
1540 (options.debug ? "enabled" : "disabled"));
1542 else if (key == KSYM_v)
1544 Error(ERR_DEBUG, "currently using game engine version %d",
1545 game.engine_version);
1551 void HandleKey(Key key, int key_status)
1553 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1554 static boolean ignore_repeated_key = FALSE;
1555 static struct SetupKeyboardInfo ski;
1556 static struct SetupShortcutInfo ssi;
1565 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1566 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1567 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1568 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1569 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1570 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1575 #if defined(TARGET_SDL2)
1576 /* map special keys (media keys / remote control buttons) to default keys */
1577 if (key == KSYM_PlayPause)
1579 else if (key == KSYM_Select)
1583 HandleSpecialGameControllerKeys(key, key_status);
1585 if (game_status == GAME_MODE_PLAYING)
1587 /* only needed for single-step tape recording mode */
1588 static boolean has_snapped[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE };
1591 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1593 byte key_action = 0;
1595 if (setup.input[pnr].use_joystick)
1598 ski = setup.input[pnr].key;
1600 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1601 if (key == *key_info[i].key_custom)
1602 key_action |= key_info[i].action;
1604 /* use combined snap+direction keys for the first player only */
1607 ssi = setup.shortcut;
1609 for (i = 0; i < NUM_DIRECTIONS; i++)
1610 if (key == *key_info[i].key_snap)
1611 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1614 if (key_status == KEY_PRESSED)
1615 stored_player[pnr].action |= key_action;
1617 stored_player[pnr].action &= ~key_action;
1619 if (tape.single_step && tape.recording && tape.pausing)
1621 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1623 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1625 /* if snap key already pressed, keep pause mode when releasing */
1626 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1627 has_snapped[pnr] = TRUE;
1629 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1631 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1633 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1634 getRedDiskReleaseFlag_SP() == 0)
1636 /* add a single inactive frame before dropping starts */
1637 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1638 stored_player[pnr].force_dropping = TRUE;
1641 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON_SNAP)
1643 /* if snap key was pressed without direction, leave pause mode */
1644 if (!has_snapped[pnr])
1645 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1647 has_snapped[pnr] = FALSE;
1650 else if (tape.recording && tape.pausing)
1652 /* prevent key release events from un-pausing a paused game */
1653 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1654 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1660 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1661 if (key == key_info[i].key_default)
1662 joy |= key_info[i].action;
1667 if (key_status == KEY_PRESSED)
1668 key_joystick_mapping |= joy;
1670 key_joystick_mapping &= ~joy;
1675 if (game_status != GAME_MODE_PLAYING)
1676 key_joystick_mapping = 0;
1678 if (key_status == KEY_RELEASED)
1680 // reset flag to ignore repeated "key pressed" events after key release
1681 ignore_repeated_key = FALSE;
1686 if ((key == KSYM_F11 ||
1687 ((key == KSYM_Return ||
1688 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1689 video.fullscreen_available &&
1690 !ignore_repeated_key)
1692 setup.fullscreen = !setup.fullscreen;
1694 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1696 if (game_status == GAME_MODE_SETUP)
1697 RedrawSetupScreenAfterFullscreenToggle();
1699 // set flag to ignore repeated "key pressed" events
1700 ignore_repeated_key = TRUE;
1705 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1706 key == KSYM_minus || key == KSYM_KP_Subtract ||
1707 key == KSYM_plus || key == KSYM_KP_Add ||
1708 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1709 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1710 video.window_scaling_available &&
1711 !video.fullscreen_enabled)
1713 if (key == KSYM_0 || key == KSYM_KP_0)
1714 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1715 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1716 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1718 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1720 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1721 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1722 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1723 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1725 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1727 if (game_status == GAME_MODE_SETUP)
1728 RedrawSetupScreenAfterFullscreenToggle();
1733 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1734 key == KSYM_Return ||
1735 key == KSYM_Escape)))
1737 /* do not handle this key event anymore */
1738 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1742 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1743 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1750 if (game_status == GAME_MODE_MAIN &&
1751 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1753 StartGameActions(options.network, setup.autorecord, level.random_seed);
1758 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1760 if (key == setup.shortcut.save_game)
1762 else if (key == setup.shortcut.load_game)
1764 else if (key == setup.shortcut.toggle_pause)
1765 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1767 HandleTapeButtonKeys(key);
1768 HandleSoundButtonKeys(key);
1771 if (game_status == GAME_MODE_PLAYING && !network_playing)
1773 int centered_player_nr_next = -999;
1775 if (key == setup.shortcut.focus_player_all)
1776 centered_player_nr_next = -1;
1778 for (i = 0; i < MAX_PLAYERS; i++)
1779 if (key == setup.shortcut.focus_player[i])
1780 centered_player_nr_next = i;
1782 if (centered_player_nr_next != -999)
1784 game.centered_player_nr_next = centered_player_nr_next;
1785 game.set_centered_player = TRUE;
1789 tape.centered_player_nr_next = game.centered_player_nr_next;
1790 tape.set_centered_player = TRUE;
1795 HandleKeysSpecial(key);
1797 if (HandleGadgetsKeyInput(key))
1799 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1800 key = KSYM_UNDEFINED;
1803 switch (game_status)
1805 case GAME_MODE_PSEUDO_TYPENAME:
1806 HandleTypeName(0, key);
1809 case GAME_MODE_TITLE:
1810 case GAME_MODE_MAIN:
1811 case GAME_MODE_LEVELS:
1812 case GAME_MODE_LEVELNR:
1813 case GAME_MODE_SETUP:
1814 case GAME_MODE_INFO:
1815 case GAME_MODE_SCORES:
1820 if (game_status == GAME_MODE_TITLE)
1821 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1822 else if (game_status == GAME_MODE_MAIN)
1823 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1824 else if (game_status == GAME_MODE_LEVELS)
1825 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1826 else if (game_status == GAME_MODE_LEVELNR)
1827 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1828 else if (game_status == GAME_MODE_SETUP)
1829 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1830 else if (game_status == GAME_MODE_INFO)
1831 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1832 else if (game_status == GAME_MODE_SCORES)
1833 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1837 if (game_status != GAME_MODE_MAIN)
1838 FadeSkipNextFadeIn();
1840 if (game_status == GAME_MODE_TITLE)
1841 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1842 else if (game_status == GAME_MODE_LEVELS)
1843 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1844 else if (game_status == GAME_MODE_LEVELNR)
1845 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1846 else if (game_status == GAME_MODE_SETUP)
1847 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1848 else if (game_status == GAME_MODE_INFO)
1849 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1850 else if (game_status == GAME_MODE_SCORES)
1851 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1855 if (game_status == GAME_MODE_LEVELS)
1856 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1857 else if (game_status == GAME_MODE_LEVELNR)
1858 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1859 else if (game_status == GAME_MODE_SETUP)
1860 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1861 else if (game_status == GAME_MODE_INFO)
1862 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1863 else if (game_status == GAME_MODE_SCORES)
1864 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1867 case KSYM_Page_Down:
1868 if (game_status == GAME_MODE_LEVELS)
1869 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1870 else if (game_status == GAME_MODE_LEVELNR)
1871 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1872 else if (game_status == GAME_MODE_SETUP)
1873 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1874 else if (game_status == GAME_MODE_INFO)
1875 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1876 else if (game_status == GAME_MODE_SCORES)
1877 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1885 case GAME_MODE_EDITOR:
1886 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1887 HandleLevelEditorKeyInput(key);
1890 case GAME_MODE_PLAYING:
1895 RequestQuitGame(setup.ask_on_escape);
1905 if (key == KSYM_Escape)
1907 SetGameStatus(GAME_MODE_MAIN);
1915 HandleKeysDebug(key);
1918 void HandleNoEvent()
1920 // if (button_status && game_status != GAME_MODE_PLAYING)
1921 if (button_status && (game_status != GAME_MODE_PLAYING ||
1923 level.game_engine_type == GAME_ENGINE_TYPE_MM))
1925 HandleButton(0, 0, button_status, -button_status);
1932 #if defined(NETWORK_AVALIABLE)
1933 if (options.network)
1937 switch (game_status)
1939 case GAME_MODE_MAIN:
1940 DrawPreviewLevelAnimation();
1943 case GAME_MODE_EDITOR:
1944 HandleLevelEditorIdle();
1947 #if defined(TARGET_SDL2)
1948 case GAME_MODE_PLAYING:
1949 HandleFollowFinger(-1, -1, -1);
1958 static int HandleJoystickForAllPlayers()
1962 boolean no_joysticks_configured = TRUE;
1963 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1964 static byte joy_action_last[MAX_PLAYERS];
1966 for (i = 0; i < MAX_PLAYERS; i++)
1967 if (setup.input[i].use_joystick)
1968 no_joysticks_configured = FALSE;
1970 /* if no joysticks configured, map connected joysticks to players */
1971 if (no_joysticks_configured)
1972 use_as_joystick_nr = TRUE;
1974 for (i = 0; i < MAX_PLAYERS; i++)
1976 byte joy_action = 0;
1978 joy_action = JoystickExt(i, use_as_joystick_nr);
1979 result |= joy_action;
1981 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1982 joy_action != joy_action_last[i])
1983 stored_player[i].action = joy_action;
1985 joy_action_last[i] = joy_action;
1991 void HandleJoystick()
1993 int joystick = HandleJoystickForAllPlayers();
1994 int keyboard = key_joystick_mapping;
1995 int joy = (joystick | keyboard);
1996 int left = joy & JOY_LEFT;
1997 int right = joy & JOY_RIGHT;
1998 int up = joy & JOY_UP;
1999 int down = joy & JOY_DOWN;
2000 int button = joy & JOY_BUTTON;
2001 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
2002 int dx = (left ? -1 : right ? 1 : 0);
2003 int dy = (up ? -1 : down ? 1 : 0);
2005 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2007 /* do not handle this button event anymore */
2011 switch (game_status)
2013 case GAME_MODE_TITLE:
2014 case GAME_MODE_MAIN:
2015 case GAME_MODE_LEVELS:
2016 case GAME_MODE_LEVELNR:
2017 case GAME_MODE_SETUP:
2018 case GAME_MODE_INFO:
2020 static unsigned int joystickmove_delay = 0;
2021 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
2022 static int joystick_last = 0;
2024 if (joystick && !button &&
2025 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2027 /* delay joystick actions if buttons/axes continually pressed */
2028 newbutton = dx = dy = 0;
2032 /* start with longer delay, then continue with shorter delay */
2033 if (joystick != joystick_last)
2034 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2036 joystickmove_delay_value = GADGET_FRAME_DELAY;
2039 if (game_status == GAME_MODE_TITLE)
2040 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2041 else if (game_status == GAME_MODE_MAIN)
2042 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2043 else if (game_status == GAME_MODE_LEVELS)
2044 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2045 else if (game_status == GAME_MODE_LEVELNR)
2046 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2047 else if (game_status == GAME_MODE_SETUP)
2048 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2049 else if (game_status == GAME_MODE_INFO)
2050 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2052 joystick_last = joystick;
2057 case GAME_MODE_SCORES:
2058 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2061 case GAME_MODE_PLAYING:
2062 if (tape.playing || keyboard)
2063 newbutton = ((joy & JOY_BUTTON) != 0);
2065 if (newbutton && AllPlayersGone)
2072 if (tape.recording && tape.pausing)
2074 if (joystick & JOY_ACTION)
2075 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2085 void HandleSpecialGameControllerButtons(Event *event)
2087 #if defined(TARGET_SDL2)
2088 switch (event->type)
2090 case SDL_CONTROLLERBUTTONDOWN:
2091 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2092 HandleKey(KSYM_space, KEY_PRESSED);
2093 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2094 HandleKey(KSYM_Escape, KEY_PRESSED);
2098 case SDL_CONTROLLERBUTTONUP:
2099 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2100 HandleKey(KSYM_space, KEY_RELEASED);
2101 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2102 HandleKey(KSYM_Escape, KEY_RELEASED);
2109 void HandleSpecialGameControllerKeys(Key key, int key_status)
2111 #if defined(TARGET_SDL2)
2112 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2113 int button = SDL_CONTROLLER_BUTTON_INVALID;
2115 /* map keys to joystick buttons (special hack for Amazon Fire TV remote) */
2116 if (key == KSYM_Rewind)
2117 button = SDL_CONTROLLER_BUTTON_A;
2118 else if (key == KSYM_FastForward || key == KSYM_Menu)
2119 button = SDL_CONTROLLER_BUTTON_B;
2121 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2125 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2126 SDL_CONTROLLERBUTTONUP);
2128 event.cbutton.which = 0; /* first joystick (Amazon Fire TV remote) */
2129 event.cbutton.button = button;
2130 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2133 HandleJoystickEvent(&event);