1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_EVENTS 0
28 #define DEBUG_EVENTS_BUTTON (DEBUG_EVENTS * 0)
29 #define DEBUG_EVENTS_MOTION (DEBUG_EVENTS * 0)
30 #define DEBUG_EVENTS_WHEEL (DEBUG_EVENTS * 1)
31 #define DEBUG_EVENTS_WINDOW (DEBUG_EVENTS * 0)
32 #define DEBUG_EVENTS_FINGER (DEBUG_EVENTS * 0)
33 #define DEBUG_EVENTS_TEXT (DEBUG_EVENTS * 1)
34 #define DEBUG_EVENTS_KEY (DEBUG_EVENTS * 1)
37 static boolean cursor_inside_playfield = FALSE;
38 static int cursor_mode_last = CURSOR_DEFAULT;
39 static unsigned int special_cursor_delay = 0;
40 static unsigned int special_cursor_delay_value = 1000;
42 /* event filter especially needed for SDL event filtering due to
43 delay problems with lots of mouse motion events when mouse button
44 not pressed (X11 can handle this with 'PointerMotionHintMask') */
46 /* event filter addition for SDL2: as SDL2 does not have a function to enable
47 or disable keyboard auto-repeat, filter repeated keyboard events instead */
49 static int FilterEvents(const Event *event)
53 #if defined(TARGET_SDL2)
54 /* skip repeated key press events if keyboard auto-repeat is disabled */
55 if (event->type == EVENT_KEYPRESS &&
61 if (event->type == EVENT_BUTTONPRESS ||
62 event->type == EVENT_BUTTONRELEASE)
64 ((ButtonEvent *)event)->x -= video.screen_xoffset;
65 ((ButtonEvent *)event)->y -= video.screen_yoffset;
67 else if (event->type == EVENT_MOTIONNOTIFY)
69 ((MotionEvent *)event)->x -= video.screen_xoffset;
70 ((MotionEvent *)event)->y -= video.screen_yoffset;
73 /* non-motion events are directly passed to event handler functions */
74 if (event->type != EVENT_MOTIONNOTIFY)
77 motion = (MotionEvent *)event;
78 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
79 motion->y >= SY && motion->y < SY + SYSIZE);
81 /* do no reset mouse cursor before all pending events have been processed */
82 if (gfx.cursor_mode == cursor_mode_last &&
83 ((game_status == GAME_MODE_TITLE &&
84 gfx.cursor_mode == CURSOR_NONE) ||
85 (game_status == GAME_MODE_PLAYING &&
86 gfx.cursor_mode == CURSOR_PLAYFIELD)))
88 SetMouseCursor(CURSOR_DEFAULT);
90 DelayReached(&special_cursor_delay, 0);
92 cursor_mode_last = CURSOR_DEFAULT;
95 /* skip mouse motion events without pressed button outside level editor */
96 if (button_status == MB_RELEASED &&
97 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
103 /* to prevent delay problems, skip mouse motion events if the very next
104 event is also a mouse motion event (and therefore effectively only
105 handling the last of a row of mouse motion events in the event queue) */
107 static boolean SkipPressedMouseMotionEvent(const Event *event)
109 /* nothing to do if the current event is not a mouse motion event */
110 if (event->type != EVENT_MOTIONNOTIFY)
113 /* only skip motion events with pressed button outside the game */
114 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
121 PeekEvent(&next_event);
123 /* if next event is also a mouse motion event, skip the current one */
124 if (next_event.type == EVENT_MOTIONNOTIFY)
131 /* this is especially needed for event modifications for the Android target:
132 if mouse coordinates should be modified in the event filter function,
133 using a properly installed SDL event filter does not work, because in
134 the event filter, mouse coordinates in the event structure are still
135 physical pixel positions, not logical (scaled) screen positions, so this
136 has to be handled at a later stage in the event processing functions
137 (when device pixel positions are already converted to screen positions) */
139 boolean NextValidEvent(Event *event)
141 while (PendingEvent())
143 boolean handle_this_event = FALSE;
147 if (FilterEvents(event))
148 handle_this_event = TRUE;
150 if (SkipPressedMouseMotionEvent(event))
151 handle_this_event = FALSE;
153 if (handle_this_event)
163 unsigned int event_frame_delay = 0;
164 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
166 ResetDelayCounter(&event_frame_delay);
168 while (NextValidEvent(&event))
172 case EVENT_BUTTONPRESS:
173 case EVENT_BUTTONRELEASE:
174 HandleButtonEvent((ButtonEvent *) &event);
177 case EVENT_MOTIONNOTIFY:
178 HandleMotionEvent((MotionEvent *) &event);
181 #if defined(TARGET_SDL2)
182 case EVENT_WHEELMOTION:
183 HandleWheelEvent((WheelEvent *) &event);
186 case SDL_WINDOWEVENT:
187 HandleWindowEvent((WindowEvent *) &event);
190 case EVENT_FINGERPRESS:
191 case EVENT_FINGERRELEASE:
192 case EVENT_FINGERMOTION:
193 HandleFingerEvent((FingerEvent *) &event);
196 case EVENT_TEXTINPUT:
197 HandleTextEvent((TextEvent *) &event);
200 case SDL_APP_WILLENTERBACKGROUND:
201 case SDL_APP_DIDENTERBACKGROUND:
202 case SDL_APP_WILLENTERFOREGROUND:
203 case SDL_APP_DIDENTERFOREGROUND:
204 HandlePauseResumeEvent((PauseResumeEvent *) &event);
209 case EVENT_KEYRELEASE:
210 HandleKeyEvent((KeyEvent *) &event);
214 HandleOtherEvents(&event);
218 // do not handle events for longer than standard frame delay period
219 if (DelayReached(&event_frame_delay, event_frame_delay_value))
224 void HandleOtherEvents(Event *event)
229 HandleExposeEvent((ExposeEvent *) event);
232 case EVENT_UNMAPNOTIFY:
234 /* This causes the game to stop not only when iconified, but also
235 when on another virtual desktop, which might be not desired. */
236 SleepWhileUnmapped();
242 HandleFocusEvent((FocusChangeEvent *) event);
245 case EVENT_CLIENTMESSAGE:
246 HandleClientMessageEvent((ClientMessageEvent *) event);
249 #if defined(TARGET_SDL)
250 case SDL_JOYAXISMOTION:
251 case SDL_JOYBUTTONDOWN:
252 case SDL_JOYBUTTONUP:
253 HandleJoystickEvent(event);
257 HandleWindowManagerEvent(event);
266 void HandleMouseCursor()
268 if (game_status == GAME_MODE_TITLE)
270 /* when showing title screens, hide mouse pointer (if not moved) */
272 if (gfx.cursor_mode != CURSOR_NONE &&
273 DelayReached(&special_cursor_delay, special_cursor_delay_value))
275 SetMouseCursor(CURSOR_NONE);
278 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
281 /* when playing, display a special mouse pointer inside the playfield */
283 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
284 cursor_inside_playfield &&
285 DelayReached(&special_cursor_delay, special_cursor_delay_value))
287 SetMouseCursor(CURSOR_PLAYFIELD);
290 else if (gfx.cursor_mode != CURSOR_DEFAULT)
292 SetMouseCursor(CURSOR_DEFAULT);
295 /* this is set after all pending events have been processed */
296 cursor_mode_last = gfx.cursor_mode;
308 /* also execute after pending events have been processed before */
311 /* don't use all CPU time when idle; the main loop while playing
312 has its own synchronization and is CPU friendly, too */
314 if (game_status == GAME_MODE_PLAYING)
317 /* always copy backbuffer to visible screen for every video frame */
320 /* reset video frame delay to default (may change again while playing) */
321 SetVideoFrameDelay(MenuFrameDelay);
323 if (game_status == GAME_MODE_QUIT)
328 void ClearEventQueue()
330 while (PendingEvent())
338 case EVENT_BUTTONRELEASE:
339 button_status = MB_RELEASED;
342 case EVENT_KEYRELEASE:
347 HandleOtherEvents(&event);
353 void ClearPlayerAction()
357 /* simulate key release events for still pressed keys */
358 key_joystick_mapping = 0;
359 for (i = 0; i < MAX_PLAYERS; i++)
360 stored_player[i].action = 0;
363 void SleepWhileUnmapped()
365 boolean window_unmapped = TRUE;
367 KeyboardAutoRepeatOn();
369 while (window_unmapped)
377 case EVENT_BUTTONRELEASE:
378 button_status = MB_RELEASED;
381 case EVENT_KEYRELEASE:
382 key_joystick_mapping = 0;
385 case EVENT_MAPNOTIFY:
386 window_unmapped = FALSE;
389 case EVENT_UNMAPNOTIFY:
390 /* this is only to surely prevent the 'should not happen' case
391 * of recursively looping between 'SleepWhileUnmapped()' and
392 * 'HandleOtherEvents()' which usually calls this funtion.
397 HandleOtherEvents(&event);
402 if (game_status == GAME_MODE_PLAYING)
403 KeyboardAutoRepeatOffUnlessAutoplay();
406 void HandleExposeEvent(ExposeEvent *event)
410 void HandleButtonEvent(ButtonEvent *event)
412 #if DEBUG_EVENTS_BUTTON
413 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
415 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
419 #if defined(HAS_SCREEN_KEYBOARD)
420 if (video.shifted_up)
421 event->y += video.shifted_up_pos;
424 motion_status = FALSE;
426 if (event->type == EVENT_BUTTONPRESS)
427 button_status = event->button;
429 button_status = MB_RELEASED;
431 HandleButton(event->x, event->y, button_status, event->button);
434 void HandleMotionEvent(MotionEvent *event)
436 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
439 motion_status = TRUE;
441 #if DEBUG_EVENTS_MOTION
442 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
443 button_status, event->x, event->y);
446 HandleButton(event->x, event->y, button_status, button_status);
449 #if defined(TARGET_SDL2)
451 void HandleWheelEvent(WheelEvent *event)
455 #if DEBUG_EVENTS_WHEEL
457 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
458 event->which, event->x, event->y);
460 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
461 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
462 event->which, event->x, event->y,
463 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
464 "SDL_MOUSEWHEEL_FLIPPED"));
468 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
469 event->x > 0 ? MB_WHEEL_RIGHT :
470 event->y < 0 ? MB_WHEEL_DOWN :
471 event->y > 0 ? MB_WHEEL_UP : 0);
473 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
474 // accelerated mouse wheel available on Mac and Windows
475 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
477 // no accelerated mouse wheel available on Unix/Linux
478 wheel_steps = DEFAULT_WHEEL_STEPS;
481 motion_status = FALSE;
483 button_status = button_nr;
484 HandleButton(0, 0, button_status, -button_nr);
486 button_status = MB_RELEASED;
487 HandleButton(0, 0, button_status, -button_nr);
490 void HandleWindowEvent(WindowEvent *event)
492 #if DEBUG_EVENTS_WINDOW
493 int subtype = event->event;
496 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
497 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
498 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
499 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
500 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
501 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
502 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
503 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
504 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
505 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
506 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
507 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
508 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
509 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
512 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
513 event_name, event->data1, event->data2);
517 // (not needed, as the screen gets redrawn every 20 ms anyway)
518 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
519 event->event == SDL_WINDOWEVENT_RESIZED ||
520 event->event == SDL_WINDOWEVENT_EXPOSED)
524 if (event->event == SDL_WINDOWEVENT_RESIZED)
526 if (!video.fullscreen_enabled)
528 int new_window_width = event->data1;
529 int new_window_height = event->data2;
531 // if window size has changed after resizing, calculate new scaling factor
532 if (new_window_width != video.window_width ||
533 new_window_height != video.window_height)
535 int new_xpercent = (100 * new_window_width / video.screen_width);
536 int new_ypercent = (100 * new_window_height / video.screen_height);
538 // (extreme window scaling allowed, but cannot be saved permanently)
539 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
540 setup.window_scaling_percent =
541 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
542 MAX_WINDOW_SCALING_PERCENT);
544 video.window_width = new_window_width;
545 video.window_height = new_window_height;
547 if (game_status == GAME_MODE_SETUP)
548 RedrawSetupScreenAfterFullscreenToggle();
553 #if defined(PLATFORM_ANDROID)
556 int new_display_width = event->data1;
557 int new_display_height = event->data2;
559 // if fullscreen display size has changed, device has been rotated
560 if (new_display_width != video.display_width ||
561 new_display_height != video.display_height)
563 video.display_width = new_display_width;
564 video.display_height = new_display_height;
566 SDLSetScreenProperties();
573 #define NUM_TOUCH_FINGERS 3
578 SDL_FingerID finger_id;
581 } touch_info[NUM_TOUCH_FINGERS];
583 void HandleFingerEvent(FingerEvent *event)
585 static Key motion_key_x = KSYM_UNDEFINED;
586 static Key motion_key_y = KSYM_UNDEFINED;
587 static Key button_key = KSYM_UNDEFINED;
588 static float motion_x1, motion_y1;
589 static float button_x1, button_y1;
590 static SDL_FingerID motion_id = -1;
591 static SDL_FingerID button_id = -1;
592 int move_trigger_distance_percent = setup.touch.move_distance;
593 int drop_trigger_distance_percent = setup.touch.drop_distance;
594 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
595 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
596 float event_x = event->x;
597 float event_y = event->y;
599 #if DEBUG_EVENTS_FINGER
600 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
601 event->type == EVENT_FINGERPRESS ? "pressed" :
602 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
606 event->dx, event->dy,
610 if (game_status != GAME_MODE_PLAYING)
613 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
616 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
618 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
620 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
622 event_y = (event_y - ypos) / (1 - ypos);
624 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
625 event_y > 2.0 / 3.0 && event_y < 1 ?
626 setup.input[0].key.snap :
627 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
628 event_y > 2.0 / 3.0 && event_y < 1 ?
629 setup.input[0].key.drop :
630 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
631 event_y > 0 && event_y < 1.0 / 3.0 ?
632 setup.input[0].key.up :
633 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
634 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
635 setup.input[0].key.left :
636 event_x > 8.0 / 9.0 && event_x < 1 &&
637 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
638 setup.input[0].key.right :
639 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
640 event_y > 2.0 / 3.0 && event_y < 1 ?
641 setup.input[0].key.down :
644 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
648 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
649 getKeyNameFromKey(key), key_status_name, event->fingerId);
651 // check if we already know this touch event's finger id
652 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
654 if (touch_info[i].touched &&
655 touch_info[i].finger_id == event->fingerId)
657 // Error(ERR_DEBUG, "MARK 1: %d", i);
663 if (i >= NUM_TOUCH_FINGERS)
665 if (key_status == KEY_PRESSED)
667 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
669 // unknown finger id -- get new, empty slot, if available
670 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
672 if (touch_info[i].counter < oldest_counter)
675 oldest_counter = touch_info[i].counter;
677 // Error(ERR_DEBUG, "MARK 2: %d", i);
680 if (!touch_info[i].touched)
682 // Error(ERR_DEBUG, "MARK 3: %d", i);
688 if (i >= NUM_TOUCH_FINGERS)
690 // all slots allocated -- use oldest slot
693 // Error(ERR_DEBUG, "MARK 4: %d", i);
698 // release of previously unknown key (should not happen)
700 if (key != KSYM_UNDEFINED)
702 HandleKey(key, KEY_RELEASED);
704 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
705 getKeyNameFromKey(key), "KEY_RELEASED", i);
710 if (i < NUM_TOUCH_FINGERS)
712 if (key_status == KEY_PRESSED)
714 if (touch_info[i].key != key)
716 if (touch_info[i].key != KSYM_UNDEFINED)
718 HandleKey(touch_info[i].key, KEY_RELEASED);
720 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
721 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
724 if (key != KSYM_UNDEFINED)
726 HandleKey(key, KEY_PRESSED);
728 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
729 getKeyNameFromKey(key), "KEY_PRESSED", i);
733 touch_info[i].touched = TRUE;
734 touch_info[i].finger_id = event->fingerId;
735 touch_info[i].counter = Counter();
736 touch_info[i].key = key;
740 if (touch_info[i].key != KSYM_UNDEFINED)
742 HandleKey(touch_info[i].key, KEY_RELEASED);
744 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
745 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
748 touch_info[i].touched = FALSE;
749 touch_info[i].finger_id = 0;
750 touch_info[i].counter = 0;
751 touch_info[i].key = 0;
758 // use touch direction control
760 if (event->type == EVENT_FINGERPRESS)
762 if (event_x > 1.0 / 3.0)
766 motion_id = event->fingerId;
771 motion_key_x = KSYM_UNDEFINED;
772 motion_key_y = KSYM_UNDEFINED;
774 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
780 button_id = event->fingerId;
785 button_key = setup.input[0].key.snap;
787 HandleKey(button_key, KEY_PRESSED);
789 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
792 else if (event->type == EVENT_FINGERRELEASE)
794 if (event->fingerId == motion_id)
798 if (motion_key_x != KSYM_UNDEFINED)
799 HandleKey(motion_key_x, KEY_RELEASED);
800 if (motion_key_y != KSYM_UNDEFINED)
801 HandleKey(motion_key_y, KEY_RELEASED);
803 motion_key_x = KSYM_UNDEFINED;
804 motion_key_y = KSYM_UNDEFINED;
806 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
808 else if (event->fingerId == button_id)
812 if (button_key != KSYM_UNDEFINED)
813 HandleKey(button_key, KEY_RELEASED);
815 button_key = KSYM_UNDEFINED;
817 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
820 else if (event->type == EVENT_FINGERMOTION)
822 if (event->fingerId == motion_id)
824 float distance_x = ABS(event_x - motion_x1);
825 float distance_y = ABS(event_y - motion_y1);
826 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
827 event_x > motion_x1 ? setup.input[0].key.right :
829 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
830 event_y > motion_y1 ? setup.input[0].key.down :
833 if (distance_x < move_trigger_distance / 2 ||
834 distance_x < distance_y)
835 new_motion_key_x = KSYM_UNDEFINED;
837 if (distance_y < move_trigger_distance / 2 ||
838 distance_y < distance_x)
839 new_motion_key_y = KSYM_UNDEFINED;
841 if (distance_x > move_trigger_distance ||
842 distance_y > move_trigger_distance)
844 if (new_motion_key_x != motion_key_x)
846 if (motion_key_x != KSYM_UNDEFINED)
847 HandleKey(motion_key_x, KEY_RELEASED);
848 if (new_motion_key_x != KSYM_UNDEFINED)
849 HandleKey(new_motion_key_x, KEY_PRESSED);
852 if (new_motion_key_y != motion_key_y)
854 if (motion_key_y != KSYM_UNDEFINED)
855 HandleKey(motion_key_y, KEY_RELEASED);
856 if (new_motion_key_y != KSYM_UNDEFINED)
857 HandleKey(new_motion_key_y, KEY_PRESSED);
863 motion_key_x = new_motion_key_x;
864 motion_key_y = new_motion_key_y;
866 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
869 else if (event->fingerId == button_id)
871 float distance_x = ABS(event_x - button_x1);
872 float distance_y = ABS(event_y - button_y1);
874 if (distance_x < drop_trigger_distance / 2 &&
875 distance_y > drop_trigger_distance)
877 if (button_key == setup.input[0].key.snap)
878 HandleKey(button_key, KEY_RELEASED);
883 button_key = setup.input[0].key.drop;
885 HandleKey(button_key, KEY_PRESSED);
887 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
893 static void HandleFollowFinger(int mx, int my, int button)
895 static int old_mx = 0, old_my = 0;
896 static Key motion_key_x = KSYM_UNDEFINED;
897 static Key motion_key_y = KSYM_UNDEFINED;
898 static boolean started_on_player = FALSE;
899 static boolean player_is_dropping = FALSE;
900 static int player_drop_count = 0;
901 static int last_player_x = -1;
902 static int last_player_y = -1;
904 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
907 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
909 touch_info[0].touched = TRUE;
910 touch_info[0].key = 0;
917 started_on_player = FALSE;
918 player_is_dropping = FALSE;
919 player_drop_count = 0;
923 motion_key_x = KSYM_UNDEFINED;
924 motion_key_y = KSYM_UNDEFINED;
926 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
929 else if (button == MB_RELEASED && touch_info[0].touched)
931 touch_info[0].touched = FALSE;
932 touch_info[0].key = 0;
937 if (motion_key_x != KSYM_UNDEFINED)
938 HandleKey(motion_key_x, KEY_RELEASED);
939 if (motion_key_y != KSYM_UNDEFINED)
940 HandleKey(motion_key_y, KEY_RELEASED);
942 if (started_on_player)
944 if (player_is_dropping)
946 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
948 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
952 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
954 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
958 motion_key_x = KSYM_UNDEFINED;
959 motion_key_y = KSYM_UNDEFINED;
961 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
964 if (touch_info[0].touched)
966 int src_x = local_player->jx;
967 int src_y = local_player->jy;
968 int dst_x = getLevelFromScreenX(old_mx);
969 int dst_y = getLevelFromScreenY(old_my);
970 int dx = dst_x - src_x;
971 int dy = dst_y - src_y;
972 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
973 dx > 0 ? setup.input[0].key.right :
975 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
976 dy > 0 ? setup.input[0].key.down :
979 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
980 (last_player_x != local_player->jx ||
981 last_player_y != local_player->jy))
983 // in case of asymmetric diagonal movement, use "preferred" direction
985 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
987 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
988 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
990 local_player->last_move_dir = last_move_dir;
992 // (required to prevent accidentally forcing direction for next movement)
993 last_player_x = local_player->jx;
994 last_player_y = local_player->jy;
997 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
999 started_on_player = TRUE;
1000 player_drop_count = getPlayerInventorySize(0);
1001 player_is_dropping = (player_drop_count > 0);
1003 if (player_is_dropping)
1005 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1007 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1011 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1013 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1016 else if (dx != 0 || dy != 0)
1018 if (player_is_dropping &&
1019 player_drop_count == getPlayerInventorySize(0))
1021 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1023 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1024 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1026 player_is_dropping = FALSE;
1030 if (new_motion_key_x != motion_key_x)
1032 Error(ERR_DEBUG, "---------- %s %s ----------",
1033 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1034 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1036 if (motion_key_x != KSYM_UNDEFINED)
1037 HandleKey(motion_key_x, KEY_RELEASED);
1038 if (new_motion_key_x != KSYM_UNDEFINED)
1039 HandleKey(new_motion_key_x, KEY_PRESSED);
1042 if (new_motion_key_y != motion_key_y)
1044 Error(ERR_DEBUG, "---------- %s %s ----------",
1045 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1046 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1048 if (motion_key_y != KSYM_UNDEFINED)
1049 HandleKey(motion_key_y, KEY_RELEASED);
1050 if (new_motion_key_y != KSYM_UNDEFINED)
1051 HandleKey(new_motion_key_y, KEY_PRESSED);
1054 motion_key_x = new_motion_key_x;
1055 motion_key_y = new_motion_key_y;
1059 static boolean checkTextInputKeyModState()
1061 // when playing, only handle raw key events and ignore text input
1062 if (game_status == GAME_MODE_PLAYING)
1065 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1068 void HandleTextEvent(TextEvent *event)
1070 char *text = event->text;
1071 Key key = getKeyFromKeyName(text);
1073 #if DEBUG_EVENTS_TEXT
1074 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1077 text[0], (int)(text[0]),
1079 getKeyNameFromKey(key),
1083 #if !defined(HAS_SCREEN_KEYBOARD)
1084 // non-mobile devices: only handle key input with modifier keys pressed here
1085 // (every other key input is handled directly as physical key input event)
1086 if (!checkTextInputKeyModState())
1090 // process text input as "classic" (with uppercase etc.) key input event
1091 HandleKey(key, KEY_PRESSED);
1092 HandleKey(key, KEY_RELEASED);
1095 void HandlePauseResumeEvent(PauseResumeEvent *event)
1097 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1101 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1109 void HandleKeyEvent(KeyEvent *event)
1111 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1112 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1113 Key key = GetEventKey(event, with_modifiers);
1114 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1116 #if DEBUG_EVENTS_KEY
1117 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1118 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1119 event->keysym.scancode,
1124 getKeyNameFromKey(key));
1127 #if defined(PLATFORM_ANDROID)
1128 // always map the "back" button to the "escape" key on Android devices
1129 if (key == KSYM_Back)
1133 HandleKeyModState(keymod, key_status);
1135 #if defined(TARGET_SDL2)
1136 // only handle raw key input without text modifier keys pressed
1137 if (!checkTextInputKeyModState())
1138 HandleKey(key, key_status);
1140 HandleKey(key, key_status);
1144 void HandleFocusEvent(FocusChangeEvent *event)
1146 static int old_joystick_status = -1;
1148 if (event->type == EVENT_FOCUSOUT)
1150 KeyboardAutoRepeatOn();
1151 old_joystick_status = joystick.status;
1152 joystick.status = JOYSTICK_NOT_AVAILABLE;
1154 ClearPlayerAction();
1156 else if (event->type == EVENT_FOCUSIN)
1158 /* When there are two Rocks'n'Diamonds windows which overlap and
1159 the player moves the pointer from one game window to the other,
1160 a 'FocusOut' event is generated for the window the pointer is
1161 leaving and a 'FocusIn' event is generated for the window the
1162 pointer is entering. In some cases, it can happen that the
1163 'FocusIn' event is handled by the one game process before the
1164 'FocusOut' event by the other game process. In this case the
1165 X11 environment would end up with activated keyboard auto repeat,
1166 because unfortunately this is a global setting and not (which
1167 would be far better) set for each X11 window individually.
1168 The effect would be keyboard auto repeat while playing the game
1169 (game_status == GAME_MODE_PLAYING), which is not desired.
1170 To avoid this special case, we just wait 1/10 second before
1171 processing the 'FocusIn' event.
1174 if (game_status == GAME_MODE_PLAYING)
1177 KeyboardAutoRepeatOffUnlessAutoplay();
1180 if (old_joystick_status != -1)
1181 joystick.status = old_joystick_status;
1185 void HandleClientMessageEvent(ClientMessageEvent *event)
1187 if (CheckCloseWindowEvent(event))
1191 void HandleWindowManagerEvent(Event *event)
1193 #if defined(TARGET_SDL)
1194 SDLHandleWindowManagerEvent(event);
1198 void HandleButton(int mx, int my, int button, int button_nr)
1200 static int old_mx = 0, old_my = 0;
1201 boolean button_hold = FALSE;
1207 button_nr = -button_nr;
1216 #if defined(PLATFORM_ANDROID)
1217 // when playing, only handle gadgets when using "follow finger" controls
1218 boolean handle_gadgets =
1219 (game_status != GAME_MODE_PLAYING ||
1220 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1222 if (handle_gadgets &&
1223 HandleGadgets(mx, my, button))
1225 /* do not handle this button event anymore */
1226 mx = my = -32; /* force mouse event to be outside screen tiles */
1229 if (HandleGadgets(mx, my, button))
1231 /* do not handle this button event anymore */
1232 mx = my = -32; /* force mouse event to be outside screen tiles */
1236 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1239 /* do not use scroll wheel button events for anything other than gadgets */
1240 if (IS_WHEEL_BUTTON(button_nr))
1243 switch (game_status)
1245 case GAME_MODE_TITLE:
1246 HandleTitleScreen(mx, my, 0, 0, button);
1249 case GAME_MODE_MAIN:
1250 HandleMainMenu(mx, my, 0, 0, button);
1253 case GAME_MODE_PSEUDO_TYPENAME:
1254 HandleTypeName(0, KSYM_Return);
1257 case GAME_MODE_LEVELS:
1258 HandleChooseLevelSet(mx, my, 0, 0, button);
1261 case GAME_MODE_LEVELNR:
1262 HandleChooseLevelNr(mx, my, 0, 0, button);
1265 case GAME_MODE_SCORES:
1266 HandleHallOfFame(0, 0, 0, 0, button);
1269 case GAME_MODE_EDITOR:
1270 HandleLevelEditorIdle();
1273 case GAME_MODE_INFO:
1274 HandleInfoScreen(mx, my, 0, 0, button);
1277 case GAME_MODE_SETUP:
1278 HandleSetupScreen(mx, my, 0, 0, button);
1281 #if defined(TARGET_SDL2)
1282 case GAME_MODE_PLAYING:
1283 HandleFollowFinger(mx, my, button);
1287 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1288 GetKeyModState() & KMOD_Control)
1289 DumpTileFromScreen(mx, my);
1299 static boolean is_string_suffix(char *string, char *suffix)
1301 int string_len = strlen(string);
1302 int suffix_len = strlen(suffix);
1304 if (suffix_len > string_len)
1307 return (strEqual(&string[string_len - suffix_len], suffix));
1310 #define MAX_CHEAT_INPUT_LEN 32
1312 static void HandleKeysSpecial(Key key)
1314 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1315 char letter = getCharFromKey(key);
1316 int cheat_input_len = strlen(cheat_input);
1322 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1324 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1325 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1327 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1330 cheat_input[cheat_input_len++] = letter;
1331 cheat_input[cheat_input_len] = '\0';
1333 #if DEBUG_EVENTS_KEY
1334 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1337 if (game_status == GAME_MODE_MAIN)
1339 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1340 is_string_suffix(cheat_input, ":ist"))
1342 InsertSolutionTape();
1344 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1345 is_string_suffix(cheat_input, ":rg"))
1347 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1350 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1351 is_string_suffix(cheat_input, ":rs"))
1353 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1356 else if (is_string_suffix(cheat_input, ":reload-music") ||
1357 is_string_suffix(cheat_input, ":rm"))
1359 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1362 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1363 is_string_suffix(cheat_input, ":ra"))
1365 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1366 1 << ARTWORK_TYPE_SOUNDS |
1367 1 << ARTWORK_TYPE_MUSIC);
1370 else if (is_string_suffix(cheat_input, ":dump-level") ||
1371 is_string_suffix(cheat_input, ":dl"))
1375 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1376 is_string_suffix(cheat_input, ":dt"))
1380 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1381 is_string_suffix(cheat_input, ":ft"))
1383 /* fix single-player tapes that contain player input for more than one
1384 player (due to a bug in 3.3.1.2 and earlier versions), which results
1385 in playing levels with more than one player in multi-player mode,
1386 even though the tape was originally recorded in single-player mode */
1388 /* remove player input actions for all players but the first one */
1389 for (i = 1; i < MAX_PLAYERS; i++)
1390 tape.player_participates[i] = FALSE;
1392 tape.changed = TRUE;
1394 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1395 is_string_suffix(cheat_input, ":snl"))
1397 SaveNativeLevel(&level);
1400 else if (game_status == GAME_MODE_PLAYING)
1403 if (is_string_suffix(cheat_input, ".q"))
1404 DEBUG_SetMaximumDynamite();
1407 else if (game_status == GAME_MODE_EDITOR)
1409 if (is_string_suffix(cheat_input, ":dump-brush") ||
1410 is_string_suffix(cheat_input, ":DB"))
1414 else if (is_string_suffix(cheat_input, ":DDB"))
1421 void HandleKeysDebug(Key key)
1426 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1428 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1430 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1432 if (key == setup.debug.frame_delay_key[i] &&
1433 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1435 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1436 setup.debug.frame_delay[i] : setup.game_frame_delay);
1438 if (!setup.debug.frame_delay_game_only)
1439 MenuFrameDelay = GameFrameDelay;
1441 SetVideoFrameDelay(GameFrameDelay);
1443 if (GameFrameDelay > ONE_SECOND_DELAY)
1444 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1445 else if (GameFrameDelay != 0)
1446 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1447 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1448 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1450 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1457 if (game_status == GAME_MODE_PLAYING)
1461 options.debug = !options.debug;
1463 Error(ERR_DEBUG, "debug mode %s",
1464 (options.debug ? "enabled" : "disabled"));
1466 else if (key == KSYM_v)
1468 Error(ERR_DEBUG, "currently using game engine version %d",
1469 game.engine_version);
1475 void HandleKey(Key key, int key_status)
1477 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1478 static boolean ignore_repeated_key = FALSE;
1479 static struct SetupKeyboardInfo ski;
1480 static struct SetupShortcutInfo ssi;
1489 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1490 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1491 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1492 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1493 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1494 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1499 if (game_status == GAME_MODE_PLAYING)
1501 /* only needed for single-step tape recording mode */
1502 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1503 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1504 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1505 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1508 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1510 byte key_action = 0;
1512 if (setup.input[pnr].use_joystick)
1515 ski = setup.input[pnr].key;
1517 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1518 if (key == *key_info[i].key_custom)
1519 key_action |= key_info[i].action;
1521 /* use combined snap+direction keys for the first player only */
1524 ssi = setup.shortcut;
1526 for (i = 0; i < NUM_DIRECTIONS; i++)
1527 if (key == *key_info[i].key_snap)
1528 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1531 /* clear delayed snap and drop actions in single step mode (see below) */
1532 if (tape.single_step)
1534 if (clear_snap_button[pnr])
1536 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1537 clear_snap_button[pnr] = FALSE;
1540 if (clear_drop_button[pnr])
1542 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1543 clear_drop_button[pnr] = FALSE;
1547 if (key_status == KEY_PRESSED)
1548 stored_player[pnr].action |= key_action;
1550 stored_player[pnr].action &= ~key_action;
1552 if (tape.single_step && tape.recording && tape.pausing)
1554 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1556 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1558 /* if snap key already pressed, don't snap when releasing (below) */
1559 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1560 element_snapped[pnr] = TRUE;
1562 /* if drop key already pressed, don't drop when releasing (below) */
1563 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1564 element_dropped[pnr] = TRUE;
1566 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1568 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1569 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1572 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1573 getRedDiskReleaseFlag_SP() == 0)
1574 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1576 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1579 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1581 if (key_action & KEY_BUTTON_SNAP)
1583 /* if snap key was released without moving (see above), snap now */
1584 if (!element_snapped[pnr])
1586 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1588 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1590 /* clear delayed snap button on next event */
1591 clear_snap_button[pnr] = TRUE;
1594 element_snapped[pnr] = FALSE;
1597 if (key_action & KEY_BUTTON_DROP &&
1598 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1600 /* if drop key was released without moving (see above), drop now */
1601 if (!element_dropped[pnr])
1603 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1605 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1606 getRedDiskReleaseFlag_SP() != 0)
1607 stored_player[pnr].action |= KEY_BUTTON_DROP;
1609 /* clear delayed drop button on next event */
1610 clear_drop_button[pnr] = TRUE;
1613 element_dropped[pnr] = FALSE;
1617 else if (tape.recording && tape.pausing)
1619 /* prevent key release events from un-pausing a paused game */
1620 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1621 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1627 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1628 if (key == key_info[i].key_default)
1629 joy |= key_info[i].action;
1634 if (key_status == KEY_PRESSED)
1635 key_joystick_mapping |= joy;
1637 key_joystick_mapping &= ~joy;
1642 if (game_status != GAME_MODE_PLAYING)
1643 key_joystick_mapping = 0;
1645 if (key_status == KEY_RELEASED)
1647 // reset flag to ignore repeated "key pressed" events after key release
1648 ignore_repeated_key = FALSE;
1653 if ((key == KSYM_F11 ||
1654 ((key == KSYM_Return ||
1655 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1656 video.fullscreen_available &&
1657 !ignore_repeated_key)
1659 setup.fullscreen = !setup.fullscreen;
1661 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1663 if (game_status == GAME_MODE_SETUP)
1664 RedrawSetupScreenAfterFullscreenToggle();
1666 // set flag to ignore repeated "key pressed" events
1667 ignore_repeated_key = TRUE;
1672 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1673 key == KSYM_minus || key == KSYM_KP_Subtract ||
1674 key == KSYM_plus || key == KSYM_KP_Add ||
1675 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1676 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1677 video.window_scaling_available &&
1678 !video.fullscreen_enabled)
1680 if (key == KSYM_0 || key == KSYM_KP_0)
1681 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1682 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1683 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1685 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1687 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1688 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1689 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1690 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1692 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1694 if (game_status == GAME_MODE_SETUP)
1695 RedrawSetupScreenAfterFullscreenToggle();
1700 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1701 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1708 if (game_status == GAME_MODE_MAIN &&
1709 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1711 StartGameActions(options.network, setup.autorecord, level.random_seed);
1716 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1718 if (key == setup.shortcut.save_game)
1720 else if (key == setup.shortcut.load_game)
1722 else if (key == setup.shortcut.toggle_pause)
1723 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1725 HandleTapeButtonKeys(key);
1726 HandleSoundButtonKeys(key);
1729 if (game_status == GAME_MODE_PLAYING && !network_playing)
1731 int centered_player_nr_next = -999;
1733 if (key == setup.shortcut.focus_player_all)
1734 centered_player_nr_next = -1;
1736 for (i = 0; i < MAX_PLAYERS; i++)
1737 if (key == setup.shortcut.focus_player[i])
1738 centered_player_nr_next = i;
1740 if (centered_player_nr_next != -999)
1742 game.centered_player_nr_next = centered_player_nr_next;
1743 game.set_centered_player = TRUE;
1747 tape.centered_player_nr_next = game.centered_player_nr_next;
1748 tape.set_centered_player = TRUE;
1753 HandleKeysSpecial(key);
1755 if (HandleGadgetsKeyInput(key))
1757 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1758 key = KSYM_UNDEFINED;
1761 switch (game_status)
1763 case GAME_MODE_PSEUDO_TYPENAME:
1764 HandleTypeName(0, key);
1767 case GAME_MODE_TITLE:
1768 case GAME_MODE_MAIN:
1769 case GAME_MODE_LEVELS:
1770 case GAME_MODE_LEVELNR:
1771 case GAME_MODE_SETUP:
1772 case GAME_MODE_INFO:
1773 case GAME_MODE_SCORES:
1778 if (game_status == GAME_MODE_TITLE)
1779 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1780 else if (game_status == GAME_MODE_MAIN)
1781 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1782 else if (game_status == GAME_MODE_LEVELS)
1783 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1784 else if (game_status == GAME_MODE_LEVELNR)
1785 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1786 else if (game_status == GAME_MODE_SETUP)
1787 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1788 else if (game_status == GAME_MODE_INFO)
1789 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1790 else if (game_status == GAME_MODE_SCORES)
1791 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1795 if (game_status != GAME_MODE_MAIN)
1796 FadeSkipNextFadeIn();
1798 if (game_status == GAME_MODE_TITLE)
1799 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1800 else if (game_status == GAME_MODE_LEVELS)
1801 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1802 else if (game_status == GAME_MODE_LEVELNR)
1803 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1804 else if (game_status == GAME_MODE_SETUP)
1805 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1806 else if (game_status == GAME_MODE_INFO)
1807 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1808 else if (game_status == GAME_MODE_SCORES)
1809 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1813 if (game_status == GAME_MODE_LEVELS)
1814 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1815 else if (game_status == GAME_MODE_LEVELNR)
1816 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1817 else if (game_status == GAME_MODE_SETUP)
1818 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1819 else if (game_status == GAME_MODE_INFO)
1820 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1821 else if (game_status == GAME_MODE_SCORES)
1822 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1825 case KSYM_Page_Down:
1826 if (game_status == GAME_MODE_LEVELS)
1827 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1828 else if (game_status == GAME_MODE_LEVELNR)
1829 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1830 else if (game_status == GAME_MODE_SETUP)
1831 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1832 else if (game_status == GAME_MODE_INFO)
1833 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1834 else if (game_status == GAME_MODE_SCORES)
1835 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1843 case GAME_MODE_EDITOR:
1844 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1845 HandleLevelEditorKeyInput(key);
1848 case GAME_MODE_PLAYING:
1853 RequestQuitGame(setup.ask_on_escape);
1863 if (key == KSYM_Escape)
1865 SetGameStatus(GAME_MODE_MAIN);
1873 HandleKeysDebug(key);
1876 void HandleNoEvent()
1878 // if (button_status && game_status != GAME_MODE_PLAYING)
1879 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1881 HandleButton(0, 0, button_status, -button_status);
1888 #if defined(NETWORK_AVALIABLE)
1889 if (options.network)
1893 switch (game_status)
1895 case GAME_MODE_MAIN:
1896 DrawPreviewLevelAnimation();
1899 case GAME_MODE_EDITOR:
1900 HandleLevelEditorIdle();
1903 #if defined(TARGET_SDL2)
1904 case GAME_MODE_PLAYING:
1905 HandleFollowFinger(-1, -1, -1);
1914 static int HandleJoystickForAllPlayers()
1919 for (i = 0; i < MAX_PLAYERS; i++)
1921 byte joy_action = 0;
1924 if (!setup.input[i].use_joystick)
1928 joy_action = Joystick(i);
1929 result |= joy_action;
1931 if (!setup.input[i].use_joystick)
1934 stored_player[i].action = joy_action;
1940 void HandleJoystick()
1942 int joystick = HandleJoystickForAllPlayers();
1943 int keyboard = key_joystick_mapping;
1944 int joy = (joystick | keyboard);
1945 int left = joy & JOY_LEFT;
1946 int right = joy & JOY_RIGHT;
1947 int up = joy & JOY_UP;
1948 int down = joy & JOY_DOWN;
1949 int button = joy & JOY_BUTTON;
1950 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1951 int dx = (left ? -1 : right ? 1 : 0);
1952 int dy = (up ? -1 : down ? 1 : 0);
1954 switch (game_status)
1956 case GAME_MODE_TITLE:
1957 case GAME_MODE_MAIN:
1958 case GAME_MODE_LEVELS:
1959 case GAME_MODE_LEVELNR:
1960 case GAME_MODE_SETUP:
1961 case GAME_MODE_INFO:
1963 static unsigned int joystickmove_delay = 0;
1965 if (joystick && !button &&
1966 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1967 newbutton = dx = dy = 0;
1969 if (game_status == GAME_MODE_TITLE)
1970 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1971 else if (game_status == GAME_MODE_MAIN)
1972 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1973 else if (game_status == GAME_MODE_LEVELS)
1974 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1975 else if (game_status == GAME_MODE_LEVELNR)
1976 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1977 else if (game_status == GAME_MODE_SETUP)
1978 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1979 else if (game_status == GAME_MODE_INFO)
1980 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1984 case GAME_MODE_SCORES:
1985 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1988 case GAME_MODE_PLAYING:
1989 if (tape.playing || keyboard)
1990 newbutton = ((joy & JOY_BUTTON) != 0);
1992 if (newbutton && AllPlayersGone)