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 WaitEventFiltered(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 (WaitEventFiltered(event))
164 unsigned int event_frame_delay = 0;
165 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
167 ResetDelayCounter(&event_frame_delay);
169 while (NextValidEvent(&event))
173 case EVENT_BUTTONPRESS:
174 case EVENT_BUTTONRELEASE:
175 HandleButtonEvent((ButtonEvent *) &event);
178 case EVENT_MOTIONNOTIFY:
179 HandleMotionEvent((MotionEvent *) &event);
182 #if defined(TARGET_SDL2)
183 case EVENT_WHEELMOTION:
184 HandleWheelEvent((WheelEvent *) &event);
187 case SDL_WINDOWEVENT:
188 HandleWindowEvent((WindowEvent *) &event);
191 case EVENT_FINGERPRESS:
192 case EVENT_FINGERRELEASE:
193 case EVENT_FINGERMOTION:
194 HandleFingerEvent((FingerEvent *) &event);
197 case EVENT_TEXTINPUT:
198 HandleTextEvent((TextEvent *) &event);
201 case SDL_APP_WILLENTERBACKGROUND:
202 case SDL_APP_DIDENTERBACKGROUND:
203 case SDL_APP_WILLENTERFOREGROUND:
204 case SDL_APP_DIDENTERFOREGROUND:
205 HandlePauseResumeEvent((PauseResumeEvent *) &event);
210 case EVENT_KEYRELEASE:
211 HandleKeyEvent((KeyEvent *) &event);
215 HandleOtherEvents(&event);
219 // do not handle events for longer than standard frame delay period
220 if (DelayReached(&event_frame_delay, event_frame_delay_value))
225 void HandleOtherEvents(Event *event)
230 HandleExposeEvent((ExposeEvent *) event);
233 case EVENT_UNMAPNOTIFY:
235 /* This causes the game to stop not only when iconified, but also
236 when on another virtual desktop, which might be not desired. */
237 SleepWhileUnmapped();
243 HandleFocusEvent((FocusChangeEvent *) event);
246 case EVENT_CLIENTMESSAGE:
247 HandleClientMessageEvent((ClientMessageEvent *) event);
250 #if defined(TARGET_SDL)
251 #if defined(TARGET_SDL2)
252 case SDL_CONTROLLERBUTTONDOWN:
253 case SDL_CONTROLLERBUTTONUP:
254 // for any game controller button event, disable overlay buttons
255 SetOverlayEnabled(FALSE);
257 HandleSpecialGameControllerButtons(event);
260 case SDL_CONTROLLERDEVICEADDED:
261 case SDL_CONTROLLERDEVICEREMOVED:
262 case SDL_CONTROLLERAXISMOTION:
264 case SDL_JOYAXISMOTION:
265 case SDL_JOYBUTTONDOWN:
266 case SDL_JOYBUTTONUP:
267 HandleJoystickEvent(event);
271 HandleWindowManagerEvent(event);
280 void HandleMouseCursor()
282 if (game_status == GAME_MODE_TITLE)
284 /* when showing title screens, hide mouse pointer (if not moved) */
286 if (gfx.cursor_mode != CURSOR_NONE &&
287 DelayReached(&special_cursor_delay, special_cursor_delay_value))
289 SetMouseCursor(CURSOR_NONE);
292 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
295 /* when playing, display a special mouse pointer inside the playfield */
297 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
298 cursor_inside_playfield &&
299 DelayReached(&special_cursor_delay, special_cursor_delay_value))
301 SetMouseCursor(CURSOR_PLAYFIELD);
304 else if (gfx.cursor_mode != CURSOR_DEFAULT)
306 SetMouseCursor(CURSOR_DEFAULT);
309 /* this is set after all pending events have been processed */
310 cursor_mode_last = gfx.cursor_mode;
322 /* also execute after pending events have been processed before */
325 /* don't use all CPU time when idle; the main loop while playing
326 has its own synchronization and is CPU friendly, too */
328 if (game_status == GAME_MODE_PLAYING)
331 /* always copy backbuffer to visible screen for every video frame */
334 /* reset video frame delay to default (may change again while playing) */
335 SetVideoFrameDelay(MenuFrameDelay);
337 if (game_status == GAME_MODE_QUIT)
342 void ClearEventQueue()
344 while (PendingEvent())
352 case EVENT_BUTTONRELEASE:
353 button_status = MB_RELEASED;
356 case EVENT_KEYRELEASE:
360 #if defined(TARGET_SDL2)
361 case SDL_CONTROLLERBUTTONUP:
362 HandleJoystickEvent(&event);
368 HandleOtherEvents(&event);
374 void ClearPlayerAction()
378 /* simulate key release events for still pressed keys */
379 key_joystick_mapping = 0;
380 for (i = 0; i < MAX_PLAYERS; i++)
381 stored_player[i].action = 0;
383 ClearJoystickState();
386 void SleepWhileUnmapped()
388 boolean window_unmapped = TRUE;
390 KeyboardAutoRepeatOn();
392 while (window_unmapped)
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 #if defined(TARGET_SDL2)
1328 case GAME_MODE_PLAYING:
1329 HandleFollowFinger(mx, my, button);
1333 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1334 GetKeyModState() & KMOD_Control)
1335 DumpTileFromScreen(mx, my);
1345 static boolean is_string_suffix(char *string, char *suffix)
1347 int string_len = strlen(string);
1348 int suffix_len = strlen(suffix);
1350 if (suffix_len > string_len)
1353 return (strEqual(&string[string_len - suffix_len], suffix));
1356 #define MAX_CHEAT_INPUT_LEN 32
1358 static void HandleKeysSpecial(Key key)
1360 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1361 char letter = getCharFromKey(key);
1362 int cheat_input_len = strlen(cheat_input);
1368 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1370 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1371 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1373 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1376 cheat_input[cheat_input_len++] = letter;
1377 cheat_input[cheat_input_len] = '\0';
1379 #if DEBUG_EVENTS_KEY
1380 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1383 if (game_status == GAME_MODE_MAIN)
1385 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1386 is_string_suffix(cheat_input, ":ist"))
1388 InsertSolutionTape();
1390 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1391 is_string_suffix(cheat_input, ":rg"))
1393 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1396 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1397 is_string_suffix(cheat_input, ":rs"))
1399 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1402 else if (is_string_suffix(cheat_input, ":reload-music") ||
1403 is_string_suffix(cheat_input, ":rm"))
1405 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1408 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1409 is_string_suffix(cheat_input, ":ra"))
1411 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1412 1 << ARTWORK_TYPE_SOUNDS |
1413 1 << ARTWORK_TYPE_MUSIC);
1416 else if (is_string_suffix(cheat_input, ":dump-level") ||
1417 is_string_suffix(cheat_input, ":dl"))
1421 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1422 is_string_suffix(cheat_input, ":dt"))
1426 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1427 is_string_suffix(cheat_input, ":ft"))
1429 /* fix single-player tapes that contain player input for more than one
1430 player (due to a bug in 3.3.1.2 and earlier versions), which results
1431 in playing levels with more than one player in multi-player mode,
1432 even though the tape was originally recorded in single-player mode */
1434 /* remove player input actions for all players but the first one */
1435 for (i = 1; i < MAX_PLAYERS; i++)
1436 tape.player_participates[i] = FALSE;
1438 tape.changed = TRUE;
1440 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1441 is_string_suffix(cheat_input, ":snl"))
1443 SaveNativeLevel(&level);
1445 else if (is_string_suffix(cheat_input, ":frames-per-second") ||
1446 is_string_suffix(cheat_input, ":fps"))
1448 global.show_frames_per_second = !global.show_frames_per_second;
1451 else if (game_status == GAME_MODE_PLAYING)
1454 if (is_string_suffix(cheat_input, ".q"))
1455 DEBUG_SetMaximumDynamite();
1458 else if (game_status == GAME_MODE_EDITOR)
1460 if (is_string_suffix(cheat_input, ":dump-brush") ||
1461 is_string_suffix(cheat_input, ":DB"))
1465 else if (is_string_suffix(cheat_input, ":DDB"))
1472 void HandleKeysDebug(Key key)
1477 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1479 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
1481 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1483 if (key == setup.debug.frame_delay_key[i] &&
1484 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1486 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1487 setup.debug.frame_delay[i] : setup.game_frame_delay);
1489 if (!setup.debug.frame_delay_game_only)
1490 MenuFrameDelay = GameFrameDelay;
1492 SetVideoFrameDelay(GameFrameDelay);
1494 if (GameFrameDelay > ONE_SECOND_DELAY)
1495 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1496 else if (GameFrameDelay != 0)
1497 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1498 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1499 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1501 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1508 if (game_status == GAME_MODE_PLAYING)
1512 options.debug = !options.debug;
1514 Error(ERR_DEBUG, "debug mode %s",
1515 (options.debug ? "enabled" : "disabled"));
1517 else if (key == KSYM_v)
1519 Error(ERR_DEBUG, "currently using game engine version %d",
1520 game.engine_version);
1526 void HandleKey(Key key, int key_status)
1528 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1529 static boolean ignore_repeated_key = FALSE;
1530 static struct SetupKeyboardInfo ski;
1531 static struct SetupShortcutInfo ssi;
1540 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1541 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1542 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1543 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1544 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1545 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1550 #if defined(TARGET_SDL2)
1551 /* map special keys (media keys / remote control buttons) to default keys */
1552 if (key == KSYM_PlayPause)
1554 else if (key == KSYM_Select)
1558 HandleSpecialGameControllerKeys(key, key_status);
1560 if (game_status == GAME_MODE_PLAYING)
1562 /* only needed for single-step tape recording mode */
1563 static boolean has_snapped[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE };
1566 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1568 byte key_action = 0;
1570 if (setup.input[pnr].use_joystick)
1573 ski = setup.input[pnr].key;
1575 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1576 if (key == *key_info[i].key_custom)
1577 key_action |= key_info[i].action;
1579 /* use combined snap+direction keys for the first player only */
1582 ssi = setup.shortcut;
1584 for (i = 0; i < NUM_DIRECTIONS; i++)
1585 if (key == *key_info[i].key_snap)
1586 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1589 if (key_status == KEY_PRESSED)
1590 stored_player[pnr].action |= key_action;
1592 stored_player[pnr].action &= ~key_action;
1594 if (tape.single_step && tape.recording && tape.pausing)
1596 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1598 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1600 /* if snap key already pressed, keep pause mode when releasing */
1601 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1602 has_snapped[pnr] = TRUE;
1604 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1606 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1608 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1609 getRedDiskReleaseFlag_SP() == 0)
1611 /* add a single inactive frame before dropping starts */
1612 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1613 stored_player[pnr].force_dropping = TRUE;
1616 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON_SNAP)
1618 /* if snap key was pressed without direction, leave pause mode */
1619 if (!has_snapped[pnr])
1620 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1622 has_snapped[pnr] = FALSE;
1625 else if (tape.recording && tape.pausing)
1627 /* prevent key release events from un-pausing a paused game */
1628 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1629 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1635 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1636 if (key == key_info[i].key_default)
1637 joy |= key_info[i].action;
1642 if (key_status == KEY_PRESSED)
1643 key_joystick_mapping |= joy;
1645 key_joystick_mapping &= ~joy;
1650 if (game_status != GAME_MODE_PLAYING)
1651 key_joystick_mapping = 0;
1653 if (key_status == KEY_RELEASED)
1655 // reset flag to ignore repeated "key pressed" events after key release
1656 ignore_repeated_key = FALSE;
1661 if ((key == KSYM_F11 ||
1662 ((key == KSYM_Return ||
1663 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1664 video.fullscreen_available &&
1665 !ignore_repeated_key)
1667 setup.fullscreen = !setup.fullscreen;
1669 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1671 if (game_status == GAME_MODE_SETUP)
1672 RedrawSetupScreenAfterFullscreenToggle();
1674 // set flag to ignore repeated "key pressed" events
1675 ignore_repeated_key = TRUE;
1680 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1681 key == KSYM_minus || key == KSYM_KP_Subtract ||
1682 key == KSYM_plus || key == KSYM_KP_Add ||
1683 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1684 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1685 video.window_scaling_available &&
1686 !video.fullscreen_enabled)
1688 if (key == KSYM_0 || key == KSYM_KP_0)
1689 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1690 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1691 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1693 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1695 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1696 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1697 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1698 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1700 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1702 if (game_status == GAME_MODE_SETUP)
1703 RedrawSetupScreenAfterFullscreenToggle();
1708 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1709 key == KSYM_Return ||
1710 key == KSYM_Escape)))
1712 /* do not handle this key event anymore */
1713 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1717 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1718 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1725 if (game_status == GAME_MODE_MAIN &&
1726 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1728 StartGameActions(options.network, setup.autorecord, level.random_seed);
1733 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1735 if (key == setup.shortcut.save_game)
1737 else if (key == setup.shortcut.load_game)
1739 else if (key == setup.shortcut.toggle_pause)
1740 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1742 HandleTapeButtonKeys(key);
1743 HandleSoundButtonKeys(key);
1746 if (game_status == GAME_MODE_PLAYING && !network_playing)
1748 int centered_player_nr_next = -999;
1750 if (key == setup.shortcut.focus_player_all)
1751 centered_player_nr_next = -1;
1753 for (i = 0; i < MAX_PLAYERS; i++)
1754 if (key == setup.shortcut.focus_player[i])
1755 centered_player_nr_next = i;
1757 if (centered_player_nr_next != -999)
1759 game.centered_player_nr_next = centered_player_nr_next;
1760 game.set_centered_player = TRUE;
1764 tape.centered_player_nr_next = game.centered_player_nr_next;
1765 tape.set_centered_player = TRUE;
1770 HandleKeysSpecial(key);
1772 if (HandleGadgetsKeyInput(key))
1774 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1775 key = KSYM_UNDEFINED;
1778 switch (game_status)
1780 case GAME_MODE_PSEUDO_TYPENAME:
1781 HandleTypeName(0, key);
1784 case GAME_MODE_TITLE:
1785 case GAME_MODE_MAIN:
1786 case GAME_MODE_LEVELS:
1787 case GAME_MODE_LEVELNR:
1788 case GAME_MODE_SETUP:
1789 case GAME_MODE_INFO:
1790 case GAME_MODE_SCORES:
1795 if (game_status == GAME_MODE_TITLE)
1796 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1797 else if (game_status == GAME_MODE_MAIN)
1798 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1799 else if (game_status == GAME_MODE_LEVELS)
1800 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1801 else if (game_status == GAME_MODE_LEVELNR)
1802 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1803 else if (game_status == GAME_MODE_SETUP)
1804 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1805 else if (game_status == GAME_MODE_INFO)
1806 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1807 else if (game_status == GAME_MODE_SCORES)
1808 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1812 if (game_status != GAME_MODE_MAIN)
1813 FadeSkipNextFadeIn();
1815 if (game_status == GAME_MODE_TITLE)
1816 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1817 else if (game_status == GAME_MODE_LEVELS)
1818 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1819 else if (game_status == GAME_MODE_LEVELNR)
1820 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1821 else if (game_status == GAME_MODE_SETUP)
1822 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1823 else if (game_status == GAME_MODE_INFO)
1824 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1825 else if (game_status == GAME_MODE_SCORES)
1826 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1830 if (game_status == GAME_MODE_LEVELS)
1831 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1832 else if (game_status == GAME_MODE_LEVELNR)
1833 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1834 else if (game_status == GAME_MODE_SETUP)
1835 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1836 else if (game_status == GAME_MODE_INFO)
1837 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1838 else if (game_status == GAME_MODE_SCORES)
1839 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1842 case KSYM_Page_Down:
1843 if (game_status == GAME_MODE_LEVELS)
1844 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1845 else if (game_status == GAME_MODE_LEVELNR)
1846 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1847 else if (game_status == GAME_MODE_SETUP)
1848 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1849 else if (game_status == GAME_MODE_INFO)
1850 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1851 else if (game_status == GAME_MODE_SCORES)
1852 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1860 case GAME_MODE_EDITOR:
1861 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1862 HandleLevelEditorKeyInput(key);
1865 case GAME_MODE_PLAYING:
1870 RequestQuitGame(setup.ask_on_escape);
1880 if (key == KSYM_Escape)
1882 SetGameStatus(GAME_MODE_MAIN);
1890 HandleKeysDebug(key);
1893 void HandleNoEvent()
1895 // if (button_status && game_status != GAME_MODE_PLAYING)
1896 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1898 HandleButton(0, 0, button_status, -button_status);
1905 #if defined(NETWORK_AVALIABLE)
1906 if (options.network)
1910 switch (game_status)
1912 case GAME_MODE_MAIN:
1913 DrawPreviewLevelAnimation();
1916 case GAME_MODE_EDITOR:
1917 HandleLevelEditorIdle();
1920 #if defined(TARGET_SDL2)
1921 case GAME_MODE_PLAYING:
1922 HandleFollowFinger(-1, -1, -1);
1931 static int HandleJoystickForAllPlayers()
1935 boolean no_joysticks_configured = TRUE;
1936 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1937 static byte joy_action_last[MAX_PLAYERS];
1939 for (i = 0; i < MAX_PLAYERS; i++)
1940 if (setup.input[i].use_joystick)
1941 no_joysticks_configured = FALSE;
1943 /* if no joysticks configured, map connected joysticks to players */
1944 if (no_joysticks_configured)
1945 use_as_joystick_nr = TRUE;
1947 for (i = 0; i < MAX_PLAYERS; i++)
1949 byte joy_action = 0;
1951 joy_action = JoystickExt(i, use_as_joystick_nr);
1952 result |= joy_action;
1954 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1955 joy_action != joy_action_last[i])
1956 stored_player[i].action = joy_action;
1958 joy_action_last[i] = joy_action;
1964 void HandleJoystick()
1966 int joystick = HandleJoystickForAllPlayers();
1967 int keyboard = key_joystick_mapping;
1968 int joy = (joystick | keyboard);
1969 int left = joy & JOY_LEFT;
1970 int right = joy & JOY_RIGHT;
1971 int up = joy & JOY_UP;
1972 int down = joy & JOY_DOWN;
1973 int button = joy & JOY_BUTTON;
1974 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1975 int dx = (left ? -1 : right ? 1 : 0);
1976 int dy = (up ? -1 : down ? 1 : 0);
1978 if (HandleGlobalAnimClicks(-1, -1, newbutton))
1980 /* do not handle this button event anymore */
1984 switch (game_status)
1986 case GAME_MODE_TITLE:
1987 case GAME_MODE_MAIN:
1988 case GAME_MODE_LEVELS:
1989 case GAME_MODE_LEVELNR:
1990 case GAME_MODE_SETUP:
1991 case GAME_MODE_INFO:
1993 static unsigned int joystickmove_delay = 0;
1994 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
1995 static int joystick_last = 0;
1997 if (joystick && !button &&
1998 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2000 /* delay joystick actions if buttons/axes continually pressed */
2001 newbutton = dx = dy = 0;
2005 /* start with longer delay, then continue with shorter delay */
2006 if (joystick != joystick_last)
2007 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2009 joystickmove_delay_value = GADGET_FRAME_DELAY;
2012 if (game_status == GAME_MODE_TITLE)
2013 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2014 else if (game_status == GAME_MODE_MAIN)
2015 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2016 else if (game_status == GAME_MODE_LEVELS)
2017 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2018 else if (game_status == GAME_MODE_LEVELNR)
2019 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2020 else if (game_status == GAME_MODE_SETUP)
2021 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2022 else if (game_status == GAME_MODE_INFO)
2023 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2025 joystick_last = joystick;
2030 case GAME_MODE_SCORES:
2031 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2034 case GAME_MODE_PLAYING:
2035 if (tape.playing || keyboard)
2036 newbutton = ((joy & JOY_BUTTON) != 0);
2038 if (newbutton && AllPlayersGone)
2045 if (tape.recording && tape.pausing)
2047 if (joystick & JOY_ACTION)
2048 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2058 void HandleSpecialGameControllerButtons(Event *event)
2060 #if defined(TARGET_SDL2)
2061 switch (event->type)
2063 case SDL_CONTROLLERBUTTONDOWN:
2064 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2065 HandleKey(KSYM_space, KEY_PRESSED);
2066 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2067 HandleKey(KSYM_Escape, KEY_PRESSED);
2071 case SDL_CONTROLLERBUTTONUP:
2072 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2073 HandleKey(KSYM_space, KEY_RELEASED);
2074 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2075 HandleKey(KSYM_Escape, KEY_RELEASED);
2082 void HandleSpecialGameControllerKeys(Key key, int key_status)
2084 #if defined(TARGET_SDL2)
2085 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2086 int button = SDL_CONTROLLER_BUTTON_INVALID;
2088 /* map keys to joystick buttons (special hack for Amazon Fire TV remote) */
2089 if (key == KSYM_Rewind)
2090 button = SDL_CONTROLLER_BUTTON_A;
2091 else if (key == KSYM_FastForward || key == KSYM_Menu)
2092 button = SDL_CONTROLLER_BUTTON_B;
2094 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2098 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2099 SDL_CONTROLLERBUTTONUP);
2101 event.cbutton.which = 0; /* first joystick (Amazon Fire TV remote) */
2102 event.cbutton.button = button;
2103 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2106 HandleJoystickEvent(&event);