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.0 * new_window_width / video.screen_width + .5;
536 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
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 HandleGlobalAnimClicks(mx, my, button);
1238 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1241 /* do not use scroll wheel button events for anything other than gadgets */
1242 if (IS_WHEEL_BUTTON(button_nr))
1245 switch (game_status)
1247 case GAME_MODE_TITLE:
1248 HandleTitleScreen(mx, my, 0, 0, button);
1251 case GAME_MODE_MAIN:
1252 HandleMainMenu(mx, my, 0, 0, button);
1255 case GAME_MODE_PSEUDO_TYPENAME:
1256 HandleTypeName(0, KSYM_Return);
1259 case GAME_MODE_LEVELS:
1260 HandleChooseLevelSet(mx, my, 0, 0, button);
1263 case GAME_MODE_LEVELNR:
1264 HandleChooseLevelNr(mx, my, 0, 0, button);
1267 case GAME_MODE_SCORES:
1268 HandleHallOfFame(0, 0, 0, 0, button);
1271 case GAME_MODE_EDITOR:
1272 HandleLevelEditorIdle();
1275 case GAME_MODE_INFO:
1276 HandleInfoScreen(mx, my, 0, 0, button);
1279 case GAME_MODE_SETUP:
1280 HandleSetupScreen(mx, my, 0, 0, button);
1283 #if defined(TARGET_SDL2)
1284 case GAME_MODE_PLAYING:
1285 HandleFollowFinger(mx, my, button);
1289 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1290 GetKeyModState() & KMOD_Control)
1291 DumpTileFromScreen(mx, my);
1301 static boolean is_string_suffix(char *string, char *suffix)
1303 int string_len = strlen(string);
1304 int suffix_len = strlen(suffix);
1306 if (suffix_len > string_len)
1309 return (strEqual(&string[string_len - suffix_len], suffix));
1312 #define MAX_CHEAT_INPUT_LEN 32
1314 static void HandleKeysSpecial(Key key)
1316 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1317 char letter = getCharFromKey(key);
1318 int cheat_input_len = strlen(cheat_input);
1324 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1326 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1327 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1329 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1332 cheat_input[cheat_input_len++] = letter;
1333 cheat_input[cheat_input_len] = '\0';
1335 #if DEBUG_EVENTS_KEY
1336 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1339 if (game_status == GAME_MODE_MAIN)
1341 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1342 is_string_suffix(cheat_input, ":ist"))
1344 InsertSolutionTape();
1346 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1347 is_string_suffix(cheat_input, ":rg"))
1349 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1352 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1353 is_string_suffix(cheat_input, ":rs"))
1355 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1358 else if (is_string_suffix(cheat_input, ":reload-music") ||
1359 is_string_suffix(cheat_input, ":rm"))
1361 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1364 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1365 is_string_suffix(cheat_input, ":ra"))
1367 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1368 1 << ARTWORK_TYPE_SOUNDS |
1369 1 << ARTWORK_TYPE_MUSIC);
1372 else if (is_string_suffix(cheat_input, ":dump-level") ||
1373 is_string_suffix(cheat_input, ":dl"))
1377 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1378 is_string_suffix(cheat_input, ":dt"))
1382 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1383 is_string_suffix(cheat_input, ":ft"))
1385 /* fix single-player tapes that contain player input for more than one
1386 player (due to a bug in 3.3.1.2 and earlier versions), which results
1387 in playing levels with more than one player in multi-player mode,
1388 even though the tape was originally recorded in single-player mode */
1390 /* remove player input actions for all players but the first one */
1391 for (i = 1; i < MAX_PLAYERS; i++)
1392 tape.player_participates[i] = FALSE;
1394 tape.changed = TRUE;
1396 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1397 is_string_suffix(cheat_input, ":snl"))
1399 SaveNativeLevel(&level);
1402 else if (game_status == GAME_MODE_PLAYING)
1405 if (is_string_suffix(cheat_input, ".q"))
1406 DEBUG_SetMaximumDynamite();
1409 else if (game_status == GAME_MODE_EDITOR)
1411 if (is_string_suffix(cheat_input, ":dump-brush") ||
1412 is_string_suffix(cheat_input, ":DB"))
1416 else if (is_string_suffix(cheat_input, ":DDB"))
1423 void HandleKeysDebug(Key key)
1428 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1430 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1432 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1434 if (key == setup.debug.frame_delay_key[i] &&
1435 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1437 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1438 setup.debug.frame_delay[i] : setup.game_frame_delay);
1440 if (!setup.debug.frame_delay_game_only)
1441 MenuFrameDelay = GameFrameDelay;
1443 SetVideoFrameDelay(GameFrameDelay);
1445 if (GameFrameDelay > ONE_SECOND_DELAY)
1446 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1447 else if (GameFrameDelay != 0)
1448 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1449 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1450 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1452 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1459 if (game_status == GAME_MODE_PLAYING)
1463 options.debug = !options.debug;
1465 Error(ERR_DEBUG, "debug mode %s",
1466 (options.debug ? "enabled" : "disabled"));
1468 else if (key == KSYM_v)
1470 Error(ERR_DEBUG, "currently using game engine version %d",
1471 game.engine_version);
1477 void HandleKey(Key key, int key_status)
1479 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1480 static boolean ignore_repeated_key = FALSE;
1481 static struct SetupKeyboardInfo ski;
1482 static struct SetupShortcutInfo ssi;
1491 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1492 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1493 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1494 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1495 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1496 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1501 if (game_status == GAME_MODE_PLAYING)
1503 /* only needed for single-step tape recording mode */
1504 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1505 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1506 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1507 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1510 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1512 byte key_action = 0;
1514 if (setup.input[pnr].use_joystick)
1517 ski = setup.input[pnr].key;
1519 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1520 if (key == *key_info[i].key_custom)
1521 key_action |= key_info[i].action;
1523 /* use combined snap+direction keys for the first player only */
1526 ssi = setup.shortcut;
1528 for (i = 0; i < NUM_DIRECTIONS; i++)
1529 if (key == *key_info[i].key_snap)
1530 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1533 /* clear delayed snap and drop actions in single step mode (see below) */
1534 if (tape.single_step)
1536 if (clear_snap_button[pnr])
1538 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1539 clear_snap_button[pnr] = FALSE;
1542 if (clear_drop_button[pnr])
1544 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1545 clear_drop_button[pnr] = FALSE;
1549 if (key_status == KEY_PRESSED)
1550 stored_player[pnr].action |= key_action;
1552 stored_player[pnr].action &= ~key_action;
1554 if (tape.single_step && tape.recording && tape.pausing)
1556 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1558 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1560 /* if snap key already pressed, don't snap when releasing (below) */
1561 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1562 element_snapped[pnr] = TRUE;
1564 /* if drop key already pressed, don't drop when releasing (below) */
1565 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1566 element_dropped[pnr] = TRUE;
1568 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1570 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1571 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1574 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1575 getRedDiskReleaseFlag_SP() == 0)
1576 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1578 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1581 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1583 if (key_action & KEY_BUTTON_SNAP)
1585 /* if snap key was released without moving (see above), snap now */
1586 if (!element_snapped[pnr])
1588 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1590 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1592 /* clear delayed snap button on next event */
1593 clear_snap_button[pnr] = TRUE;
1596 element_snapped[pnr] = FALSE;
1599 if (key_action & KEY_BUTTON_DROP &&
1600 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1602 /* if drop key was released without moving (see above), drop now */
1603 if (!element_dropped[pnr])
1605 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1607 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1608 getRedDiskReleaseFlag_SP() != 0)
1609 stored_player[pnr].action |= KEY_BUTTON_DROP;
1611 /* clear delayed drop button on next event */
1612 clear_drop_button[pnr] = TRUE;
1615 element_dropped[pnr] = FALSE;
1619 else if (tape.recording && tape.pausing)
1621 /* prevent key release events from un-pausing a paused game */
1622 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1623 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1629 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1630 if (key == key_info[i].key_default)
1631 joy |= key_info[i].action;
1636 if (key_status == KEY_PRESSED)
1637 key_joystick_mapping |= joy;
1639 key_joystick_mapping &= ~joy;
1644 if (game_status != GAME_MODE_PLAYING)
1645 key_joystick_mapping = 0;
1647 if (key_status == KEY_RELEASED)
1649 // reset flag to ignore repeated "key pressed" events after key release
1650 ignore_repeated_key = FALSE;
1655 if ((key == KSYM_F11 ||
1656 ((key == KSYM_Return ||
1657 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1658 video.fullscreen_available &&
1659 !ignore_repeated_key)
1661 setup.fullscreen = !setup.fullscreen;
1663 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1665 if (game_status == GAME_MODE_SETUP)
1666 RedrawSetupScreenAfterFullscreenToggle();
1668 // set flag to ignore repeated "key pressed" events
1669 ignore_repeated_key = TRUE;
1674 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1675 key == KSYM_minus || key == KSYM_KP_Subtract ||
1676 key == KSYM_plus || key == KSYM_KP_Add ||
1677 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1678 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1679 video.window_scaling_available &&
1680 !video.fullscreen_enabled)
1682 if (key == KSYM_0 || key == KSYM_KP_0)
1683 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1684 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1685 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1687 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1689 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1690 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1691 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1692 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1694 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1696 if (game_status == GAME_MODE_SETUP)
1697 RedrawSetupScreenAfterFullscreenToggle();
1702 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1703 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1710 if (game_status == GAME_MODE_MAIN &&
1711 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1713 StartGameActions(options.network, setup.autorecord, level.random_seed);
1718 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1720 if (key == setup.shortcut.save_game)
1722 else if (key == setup.shortcut.load_game)
1724 else if (key == setup.shortcut.toggle_pause)
1725 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1727 HandleTapeButtonKeys(key);
1728 HandleSoundButtonKeys(key);
1731 if (game_status == GAME_MODE_PLAYING && !network_playing)
1733 int centered_player_nr_next = -999;
1735 if (key == setup.shortcut.focus_player_all)
1736 centered_player_nr_next = -1;
1738 for (i = 0; i < MAX_PLAYERS; i++)
1739 if (key == setup.shortcut.focus_player[i])
1740 centered_player_nr_next = i;
1742 if (centered_player_nr_next != -999)
1744 game.centered_player_nr_next = centered_player_nr_next;
1745 game.set_centered_player = TRUE;
1749 tape.centered_player_nr_next = game.centered_player_nr_next;
1750 tape.set_centered_player = TRUE;
1755 HandleKeysSpecial(key);
1757 if (HandleGadgetsKeyInput(key))
1759 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1760 key = KSYM_UNDEFINED;
1763 switch (game_status)
1765 case GAME_MODE_PSEUDO_TYPENAME:
1766 HandleTypeName(0, key);
1769 case GAME_MODE_TITLE:
1770 case GAME_MODE_MAIN:
1771 case GAME_MODE_LEVELS:
1772 case GAME_MODE_LEVELNR:
1773 case GAME_MODE_SETUP:
1774 case GAME_MODE_INFO:
1775 case GAME_MODE_SCORES:
1780 if (game_status == GAME_MODE_TITLE)
1781 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1782 else if (game_status == GAME_MODE_MAIN)
1783 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1784 else if (game_status == GAME_MODE_LEVELS)
1785 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1786 else if (game_status == GAME_MODE_LEVELNR)
1787 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1788 else if (game_status == GAME_MODE_SETUP)
1789 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1790 else if (game_status == GAME_MODE_INFO)
1791 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1792 else if (game_status == GAME_MODE_SCORES)
1793 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1797 if (game_status != GAME_MODE_MAIN)
1798 FadeSkipNextFadeIn();
1800 if (game_status == GAME_MODE_TITLE)
1801 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1802 else if (game_status == GAME_MODE_LEVELS)
1803 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1804 else if (game_status == GAME_MODE_LEVELNR)
1805 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1806 else if (game_status == GAME_MODE_SETUP)
1807 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1808 else if (game_status == GAME_MODE_INFO)
1809 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1810 else if (game_status == GAME_MODE_SCORES)
1811 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1815 if (game_status == GAME_MODE_LEVELS)
1816 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1817 else if (game_status == GAME_MODE_LEVELNR)
1818 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1819 else if (game_status == GAME_MODE_SETUP)
1820 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1821 else if (game_status == GAME_MODE_INFO)
1822 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1823 else if (game_status == GAME_MODE_SCORES)
1824 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1827 case KSYM_Page_Down:
1828 if (game_status == GAME_MODE_LEVELS)
1829 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1830 else if (game_status == GAME_MODE_LEVELNR)
1831 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1832 else if (game_status == GAME_MODE_SETUP)
1833 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1834 else if (game_status == GAME_MODE_INFO)
1835 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1836 else if (game_status == GAME_MODE_SCORES)
1837 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1845 case GAME_MODE_EDITOR:
1846 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1847 HandleLevelEditorKeyInput(key);
1850 case GAME_MODE_PLAYING:
1855 RequestQuitGame(setup.ask_on_escape);
1865 if (key == KSYM_Escape)
1867 SetGameStatus(GAME_MODE_MAIN);
1875 HandleKeysDebug(key);
1878 void HandleNoEvent()
1880 // if (button_status && game_status != GAME_MODE_PLAYING)
1881 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1883 HandleButton(0, 0, button_status, -button_status);
1890 #if defined(NETWORK_AVALIABLE)
1891 if (options.network)
1895 switch (game_status)
1897 case GAME_MODE_MAIN:
1898 DrawPreviewLevelAnimation();
1901 case GAME_MODE_EDITOR:
1902 HandleLevelEditorIdle();
1905 #if defined(TARGET_SDL2)
1906 case GAME_MODE_PLAYING:
1907 HandleFollowFinger(-1, -1, -1);
1916 static int HandleJoystickForAllPlayers()
1921 for (i = 0; i < MAX_PLAYERS; i++)
1923 byte joy_action = 0;
1926 if (!setup.input[i].use_joystick)
1930 joy_action = Joystick(i);
1931 result |= joy_action;
1933 if (!setup.input[i].use_joystick)
1936 stored_player[i].action = joy_action;
1942 void HandleJoystick()
1944 int joystick = HandleJoystickForAllPlayers();
1945 int keyboard = key_joystick_mapping;
1946 int joy = (joystick | keyboard);
1947 int left = joy & JOY_LEFT;
1948 int right = joy & JOY_RIGHT;
1949 int up = joy & JOY_UP;
1950 int down = joy & JOY_DOWN;
1951 int button = joy & JOY_BUTTON;
1952 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1953 int dx = (left ? -1 : right ? 1 : 0);
1954 int dy = (up ? -1 : down ? 1 : 0);
1956 switch (game_status)
1958 case GAME_MODE_TITLE:
1959 case GAME_MODE_MAIN:
1960 case GAME_MODE_LEVELS:
1961 case GAME_MODE_LEVELNR:
1962 case GAME_MODE_SETUP:
1963 case GAME_MODE_INFO:
1965 static unsigned int joystickmove_delay = 0;
1967 if (joystick && !button &&
1968 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1969 newbutton = dx = dy = 0;
1971 if (game_status == GAME_MODE_TITLE)
1972 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1973 else if (game_status == GAME_MODE_MAIN)
1974 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1975 else if (game_status == GAME_MODE_LEVELS)
1976 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1977 else if (game_status == GAME_MODE_LEVELNR)
1978 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1979 else if (game_status == GAME_MODE_SETUP)
1980 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1981 else if (game_status == GAME_MODE_INFO)
1982 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1986 case GAME_MODE_SCORES:
1987 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1990 case GAME_MODE_PLAYING:
1991 if (tape.playing || keyboard)
1992 newbutton = ((joy & JOY_BUTTON) != 0);
1994 if (newbutton && AllPlayersGone)