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 #if defined(TARGET_SDL2)
251 case SDL_CONTROLLERBUTTONDOWN:
252 case SDL_CONTROLLERBUTTONUP:
253 HandleSpecialGameControllerButtons(event);
255 case SDL_CONTROLLERDEVICEADDED:
256 case SDL_CONTROLLERDEVICEREMOVED:
257 case SDL_CONTROLLERAXISMOTION:
259 case SDL_JOYAXISMOTION:
260 case SDL_JOYBUTTONDOWN:
261 case SDL_JOYBUTTONUP:
262 HandleJoystickEvent(event);
266 HandleWindowManagerEvent(event);
275 void HandleMouseCursor()
277 if (game_status == GAME_MODE_TITLE)
279 /* when showing title screens, hide mouse pointer (if not moved) */
281 if (gfx.cursor_mode != CURSOR_NONE &&
282 DelayReached(&special_cursor_delay, special_cursor_delay_value))
284 SetMouseCursor(CURSOR_NONE);
287 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
290 /* when playing, display a special mouse pointer inside the playfield */
292 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
293 cursor_inside_playfield &&
294 DelayReached(&special_cursor_delay, special_cursor_delay_value))
296 SetMouseCursor(CURSOR_PLAYFIELD);
299 else if (gfx.cursor_mode != CURSOR_DEFAULT)
301 SetMouseCursor(CURSOR_DEFAULT);
304 /* this is set after all pending events have been processed */
305 cursor_mode_last = gfx.cursor_mode;
317 /* also execute after pending events have been processed before */
320 /* don't use all CPU time when idle; the main loop while playing
321 has its own synchronization and is CPU friendly, too */
323 if (game_status == GAME_MODE_PLAYING)
326 /* always copy backbuffer to visible screen for every video frame */
329 /* reset video frame delay to default (may change again while playing) */
330 SetVideoFrameDelay(MenuFrameDelay);
332 if (game_status == GAME_MODE_QUIT)
337 void ClearEventQueue()
339 while (PendingEvent())
347 case EVENT_BUTTONRELEASE:
348 button_status = MB_RELEASED;
351 case EVENT_KEYRELEASE:
355 #if defined(TARGET_SDL2)
356 case SDL_CONTROLLERBUTTONUP:
357 HandleJoystickEvent(&event);
363 HandleOtherEvents(&event);
369 void ClearPlayerAction()
373 /* simulate key release events for still pressed keys */
374 key_joystick_mapping = 0;
375 for (i = 0; i < MAX_PLAYERS; i++)
376 stored_player[i].action = 0;
379 void SleepWhileUnmapped()
381 boolean window_unmapped = TRUE;
383 KeyboardAutoRepeatOn();
385 while (window_unmapped)
393 case EVENT_BUTTONRELEASE:
394 button_status = MB_RELEASED;
397 case EVENT_KEYRELEASE:
398 key_joystick_mapping = 0;
401 #if defined(TARGET_SDL2)
402 case SDL_CONTROLLERBUTTONUP:
403 HandleJoystickEvent(&event);
404 key_joystick_mapping = 0;
408 case EVENT_MAPNOTIFY:
409 window_unmapped = FALSE;
412 case EVENT_UNMAPNOTIFY:
413 /* this is only to surely prevent the 'should not happen' case
414 * of recursively looping between 'SleepWhileUnmapped()' and
415 * 'HandleOtherEvents()' which usually calls this funtion.
420 HandleOtherEvents(&event);
425 if (game_status == GAME_MODE_PLAYING)
426 KeyboardAutoRepeatOffUnlessAutoplay();
429 void HandleExposeEvent(ExposeEvent *event)
433 void HandleButtonEvent(ButtonEvent *event)
435 #if DEBUG_EVENTS_BUTTON
436 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
438 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
442 #if defined(HAS_SCREEN_KEYBOARD)
443 if (video.shifted_up)
444 event->y += video.shifted_up_pos;
447 motion_status = FALSE;
449 if (event->type == EVENT_BUTTONPRESS)
450 button_status = event->button;
452 button_status = MB_RELEASED;
454 HandleButton(event->x, event->y, button_status, event->button);
457 void HandleMotionEvent(MotionEvent *event)
459 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
462 motion_status = TRUE;
464 #if DEBUG_EVENTS_MOTION
465 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
466 button_status, event->x, event->y);
469 HandleButton(event->x, event->y, button_status, button_status);
472 #if defined(TARGET_SDL2)
474 void HandleWheelEvent(WheelEvent *event)
478 #if DEBUG_EVENTS_WHEEL
480 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
481 event->which, event->x, event->y);
483 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
484 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
485 event->which, event->x, event->y,
486 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
487 "SDL_MOUSEWHEEL_FLIPPED"));
491 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
492 event->x > 0 ? MB_WHEEL_RIGHT :
493 event->y < 0 ? MB_WHEEL_DOWN :
494 event->y > 0 ? MB_WHEEL_UP : 0);
496 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
497 // accelerated mouse wheel available on Mac and Windows
498 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
500 // no accelerated mouse wheel available on Unix/Linux
501 wheel_steps = DEFAULT_WHEEL_STEPS;
504 motion_status = FALSE;
506 button_status = button_nr;
507 HandleButton(0, 0, button_status, -button_nr);
509 button_status = MB_RELEASED;
510 HandleButton(0, 0, button_status, -button_nr);
513 void HandleWindowEvent(WindowEvent *event)
515 #if DEBUG_EVENTS_WINDOW
516 int subtype = event->event;
519 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
520 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
521 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
522 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
523 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
524 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
525 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
526 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
527 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
528 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
529 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
530 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
531 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
532 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
535 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
536 event_name, event->data1, event->data2);
540 // (not needed, as the screen gets redrawn every 20 ms anyway)
541 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
542 event->event == SDL_WINDOWEVENT_RESIZED ||
543 event->event == SDL_WINDOWEVENT_EXPOSED)
547 if (event->event == SDL_WINDOWEVENT_RESIZED)
549 if (!video.fullscreen_enabled)
551 int new_window_width = event->data1;
552 int new_window_height = event->data2;
554 // if window size has changed after resizing, calculate new scaling factor
555 if (new_window_width != video.window_width ||
556 new_window_height != video.window_height)
558 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
559 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
561 // (extreme window scaling allowed, but cannot be saved permanently)
562 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
563 setup.window_scaling_percent =
564 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
565 MAX_WINDOW_SCALING_PERCENT);
567 video.window_width = new_window_width;
568 video.window_height = new_window_height;
570 if (game_status == GAME_MODE_SETUP)
571 RedrawSetupScreenAfterFullscreenToggle();
576 #if defined(PLATFORM_ANDROID)
579 int new_display_width = event->data1;
580 int new_display_height = event->data2;
582 // if fullscreen display size has changed, device has been rotated
583 if (new_display_width != video.display_width ||
584 new_display_height != video.display_height)
586 video.display_width = new_display_width;
587 video.display_height = new_display_height;
589 SDLSetScreenProperties();
596 #define NUM_TOUCH_FINGERS 3
601 SDL_FingerID finger_id;
604 } touch_info[NUM_TOUCH_FINGERS];
606 void HandleFingerEvent(FingerEvent *event)
608 static Key motion_key_x = KSYM_UNDEFINED;
609 static Key motion_key_y = KSYM_UNDEFINED;
610 static Key button_key = KSYM_UNDEFINED;
611 static float motion_x1, motion_y1;
612 static float button_x1, button_y1;
613 static SDL_FingerID motion_id = -1;
614 static SDL_FingerID button_id = -1;
615 int move_trigger_distance_percent = setup.touch.move_distance;
616 int drop_trigger_distance_percent = setup.touch.drop_distance;
617 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
618 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
619 float event_x = event->x;
620 float event_y = event->y;
622 #if DEBUG_EVENTS_FINGER
623 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
624 event->type == EVENT_FINGERPRESS ? "pressed" :
625 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
629 event->dx, event->dy,
633 if (game_status != GAME_MODE_PLAYING)
636 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
639 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
641 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
643 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
645 event_y = (event_y - ypos) / (1 - ypos);
647 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
648 event_y > 2.0 / 3.0 && event_y < 1 ?
649 setup.input[0].key.snap :
650 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
651 event_y > 2.0 / 3.0 && event_y < 1 ?
652 setup.input[0].key.drop :
653 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
654 event_y > 0 && event_y < 1.0 / 3.0 ?
655 setup.input[0].key.up :
656 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
657 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
658 setup.input[0].key.left :
659 event_x > 8.0 / 9.0 && event_x < 1 &&
660 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
661 setup.input[0].key.right :
662 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
663 event_y > 2.0 / 3.0 && event_y < 1 ?
664 setup.input[0].key.down :
667 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
671 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
672 getKeyNameFromKey(key), key_status_name, event->fingerId);
674 // check if we already know this touch event's finger id
675 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
677 if (touch_info[i].touched &&
678 touch_info[i].finger_id == event->fingerId)
680 // Error(ERR_DEBUG, "MARK 1: %d", i);
686 if (i >= NUM_TOUCH_FINGERS)
688 if (key_status == KEY_PRESSED)
690 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
692 // unknown finger id -- get new, empty slot, if available
693 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
695 if (touch_info[i].counter < oldest_counter)
698 oldest_counter = touch_info[i].counter;
700 // Error(ERR_DEBUG, "MARK 2: %d", i);
703 if (!touch_info[i].touched)
705 // Error(ERR_DEBUG, "MARK 3: %d", i);
711 if (i >= NUM_TOUCH_FINGERS)
713 // all slots allocated -- use oldest slot
716 // Error(ERR_DEBUG, "MARK 4: %d", i);
721 // release of previously unknown key (should not happen)
723 if (key != KSYM_UNDEFINED)
725 HandleKey(key, KEY_RELEASED);
727 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
728 getKeyNameFromKey(key), "KEY_RELEASED", i);
733 if (i < NUM_TOUCH_FINGERS)
735 if (key_status == KEY_PRESSED)
737 if (touch_info[i].key != key)
739 if (touch_info[i].key != KSYM_UNDEFINED)
741 HandleKey(touch_info[i].key, KEY_RELEASED);
743 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
744 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
747 if (key != KSYM_UNDEFINED)
749 HandleKey(key, KEY_PRESSED);
751 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
752 getKeyNameFromKey(key), "KEY_PRESSED", i);
756 touch_info[i].touched = TRUE;
757 touch_info[i].finger_id = event->fingerId;
758 touch_info[i].counter = Counter();
759 touch_info[i].key = key;
763 if (touch_info[i].key != KSYM_UNDEFINED)
765 HandleKey(touch_info[i].key, KEY_RELEASED);
767 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
768 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
771 touch_info[i].touched = FALSE;
772 touch_info[i].finger_id = 0;
773 touch_info[i].counter = 0;
774 touch_info[i].key = 0;
781 // use touch direction control
783 if (event->type == EVENT_FINGERPRESS)
785 if (event_x > 1.0 / 3.0)
789 motion_id = event->fingerId;
794 motion_key_x = KSYM_UNDEFINED;
795 motion_key_y = KSYM_UNDEFINED;
797 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
803 button_id = event->fingerId;
808 button_key = setup.input[0].key.snap;
810 HandleKey(button_key, KEY_PRESSED);
812 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
815 else if (event->type == EVENT_FINGERRELEASE)
817 if (event->fingerId == motion_id)
821 if (motion_key_x != KSYM_UNDEFINED)
822 HandleKey(motion_key_x, KEY_RELEASED);
823 if (motion_key_y != KSYM_UNDEFINED)
824 HandleKey(motion_key_y, KEY_RELEASED);
826 motion_key_x = KSYM_UNDEFINED;
827 motion_key_y = KSYM_UNDEFINED;
829 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
831 else if (event->fingerId == button_id)
835 if (button_key != KSYM_UNDEFINED)
836 HandleKey(button_key, KEY_RELEASED);
838 button_key = KSYM_UNDEFINED;
840 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
843 else if (event->type == EVENT_FINGERMOTION)
845 if (event->fingerId == motion_id)
847 float distance_x = ABS(event_x - motion_x1);
848 float distance_y = ABS(event_y - motion_y1);
849 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
850 event_x > motion_x1 ? setup.input[0].key.right :
852 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
853 event_y > motion_y1 ? setup.input[0].key.down :
856 if (distance_x < move_trigger_distance / 2 ||
857 distance_x < distance_y)
858 new_motion_key_x = KSYM_UNDEFINED;
860 if (distance_y < move_trigger_distance / 2 ||
861 distance_y < distance_x)
862 new_motion_key_y = KSYM_UNDEFINED;
864 if (distance_x > move_trigger_distance ||
865 distance_y > move_trigger_distance)
867 if (new_motion_key_x != motion_key_x)
869 if (motion_key_x != KSYM_UNDEFINED)
870 HandleKey(motion_key_x, KEY_RELEASED);
871 if (new_motion_key_x != KSYM_UNDEFINED)
872 HandleKey(new_motion_key_x, KEY_PRESSED);
875 if (new_motion_key_y != motion_key_y)
877 if (motion_key_y != KSYM_UNDEFINED)
878 HandleKey(motion_key_y, KEY_RELEASED);
879 if (new_motion_key_y != KSYM_UNDEFINED)
880 HandleKey(new_motion_key_y, KEY_PRESSED);
886 motion_key_x = new_motion_key_x;
887 motion_key_y = new_motion_key_y;
889 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
892 else if (event->fingerId == button_id)
894 float distance_x = ABS(event_x - button_x1);
895 float distance_y = ABS(event_y - button_y1);
897 if (distance_x < drop_trigger_distance / 2 &&
898 distance_y > drop_trigger_distance)
900 if (button_key == setup.input[0].key.snap)
901 HandleKey(button_key, KEY_RELEASED);
906 button_key = setup.input[0].key.drop;
908 HandleKey(button_key, KEY_PRESSED);
910 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
916 static void HandleFollowFinger(int mx, int my, int button)
918 static int old_mx = 0, old_my = 0;
919 static Key motion_key_x = KSYM_UNDEFINED;
920 static Key motion_key_y = KSYM_UNDEFINED;
921 static boolean started_on_player = FALSE;
922 static boolean player_is_dropping = FALSE;
923 static int player_drop_count = 0;
924 static int last_player_x = -1;
925 static int last_player_y = -1;
927 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
930 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
932 touch_info[0].touched = TRUE;
933 touch_info[0].key = 0;
940 started_on_player = FALSE;
941 player_is_dropping = FALSE;
942 player_drop_count = 0;
946 motion_key_x = KSYM_UNDEFINED;
947 motion_key_y = KSYM_UNDEFINED;
949 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
952 else if (button == MB_RELEASED && touch_info[0].touched)
954 touch_info[0].touched = FALSE;
955 touch_info[0].key = 0;
960 if (motion_key_x != KSYM_UNDEFINED)
961 HandleKey(motion_key_x, KEY_RELEASED);
962 if (motion_key_y != KSYM_UNDEFINED)
963 HandleKey(motion_key_y, KEY_RELEASED);
965 if (started_on_player)
967 if (player_is_dropping)
969 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
971 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
975 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
977 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
981 motion_key_x = KSYM_UNDEFINED;
982 motion_key_y = KSYM_UNDEFINED;
984 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
987 if (touch_info[0].touched)
989 int src_x = local_player->jx;
990 int src_y = local_player->jy;
991 int dst_x = getLevelFromScreenX(old_mx);
992 int dst_y = getLevelFromScreenY(old_my);
993 int dx = dst_x - src_x;
994 int dy = dst_y - src_y;
995 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
996 dx > 0 ? setup.input[0].key.right :
998 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
999 dy > 0 ? setup.input[0].key.down :
1002 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1003 (last_player_x != local_player->jx ||
1004 last_player_y != local_player->jy))
1006 // in case of asymmetric diagonal movement, use "preferred" direction
1008 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1010 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1011 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1013 local_player->last_move_dir = last_move_dir;
1015 // (required to prevent accidentally forcing direction for next movement)
1016 last_player_x = local_player->jx;
1017 last_player_y = local_player->jy;
1020 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1022 started_on_player = TRUE;
1023 player_drop_count = getPlayerInventorySize(0);
1024 player_is_dropping = (player_drop_count > 0);
1026 if (player_is_dropping)
1028 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1030 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1034 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1036 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1039 else if (dx != 0 || dy != 0)
1041 if (player_is_dropping &&
1042 player_drop_count == getPlayerInventorySize(0))
1044 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1046 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1047 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1049 player_is_dropping = FALSE;
1053 if (new_motion_key_x != motion_key_x)
1055 Error(ERR_DEBUG, "---------- %s %s ----------",
1056 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1057 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1059 if (motion_key_x != KSYM_UNDEFINED)
1060 HandleKey(motion_key_x, KEY_RELEASED);
1061 if (new_motion_key_x != KSYM_UNDEFINED)
1062 HandleKey(new_motion_key_x, KEY_PRESSED);
1065 if (new_motion_key_y != motion_key_y)
1067 Error(ERR_DEBUG, "---------- %s %s ----------",
1068 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1069 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1071 if (motion_key_y != KSYM_UNDEFINED)
1072 HandleKey(motion_key_y, KEY_RELEASED);
1073 if (new_motion_key_y != KSYM_UNDEFINED)
1074 HandleKey(new_motion_key_y, KEY_PRESSED);
1077 motion_key_x = new_motion_key_x;
1078 motion_key_y = new_motion_key_y;
1082 static boolean checkTextInputKeyModState()
1084 // when playing, only handle raw key events and ignore text input
1085 if (game_status == GAME_MODE_PLAYING)
1088 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1091 void HandleTextEvent(TextEvent *event)
1093 char *text = event->text;
1094 Key key = getKeyFromKeyName(text);
1096 #if DEBUG_EVENTS_TEXT
1097 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1100 text[0], (int)(text[0]),
1102 getKeyNameFromKey(key),
1106 #if !defined(HAS_SCREEN_KEYBOARD)
1107 // non-mobile devices: only handle key input with modifier keys pressed here
1108 // (every other key input is handled directly as physical key input event)
1109 if (!checkTextInputKeyModState())
1113 // process text input as "classic" (with uppercase etc.) key input event
1114 HandleKey(key, KEY_PRESSED);
1115 HandleKey(key, KEY_RELEASED);
1118 void HandlePauseResumeEvent(PauseResumeEvent *event)
1120 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1124 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1132 void HandleKeyEvent(KeyEvent *event)
1134 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1135 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1136 Key key = GetEventKey(event, with_modifiers);
1137 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1139 #if DEBUG_EVENTS_KEY
1140 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1141 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1142 event->keysym.scancode,
1147 getKeyNameFromKey(key));
1150 #if defined(PLATFORM_ANDROID)
1151 // always map the "back" button to the "escape" key on Android devices
1152 if (key == KSYM_Back)
1156 HandleKeyModState(keymod, key_status);
1158 #if defined(TARGET_SDL2)
1159 // only handle raw key input without text modifier keys pressed
1160 if (!checkTextInputKeyModState())
1161 HandleKey(key, key_status);
1163 HandleKey(key, key_status);
1167 void HandleFocusEvent(FocusChangeEvent *event)
1169 static int old_joystick_status = -1;
1171 if (event->type == EVENT_FOCUSOUT)
1173 KeyboardAutoRepeatOn();
1174 old_joystick_status = joystick.status;
1175 joystick.status = JOYSTICK_NOT_AVAILABLE;
1177 ClearPlayerAction();
1179 else if (event->type == EVENT_FOCUSIN)
1181 /* When there are two Rocks'n'Diamonds windows which overlap and
1182 the player moves the pointer from one game window to the other,
1183 a 'FocusOut' event is generated for the window the pointer is
1184 leaving and a 'FocusIn' event is generated for the window the
1185 pointer is entering. In some cases, it can happen that the
1186 'FocusIn' event is handled by the one game process before the
1187 'FocusOut' event by the other game process. In this case the
1188 X11 environment would end up with activated keyboard auto repeat,
1189 because unfortunately this is a global setting and not (which
1190 would be far better) set for each X11 window individually.
1191 The effect would be keyboard auto repeat while playing the game
1192 (game_status == GAME_MODE_PLAYING), which is not desired.
1193 To avoid this special case, we just wait 1/10 second before
1194 processing the 'FocusIn' event.
1197 if (game_status == GAME_MODE_PLAYING)
1200 KeyboardAutoRepeatOffUnlessAutoplay();
1203 if (old_joystick_status != -1)
1204 joystick.status = old_joystick_status;
1208 void HandleClientMessageEvent(ClientMessageEvent *event)
1210 if (CheckCloseWindowEvent(event))
1214 void HandleWindowManagerEvent(Event *event)
1216 #if defined(TARGET_SDL)
1217 SDLHandleWindowManagerEvent(event);
1221 void HandleButton(int mx, int my, int button, int button_nr)
1223 static int old_mx = 0, old_my = 0;
1224 boolean button_hold = FALSE;
1230 button_nr = -button_nr;
1239 #if defined(PLATFORM_ANDROID)
1240 // when playing, only handle gadgets when using "follow finger" controls
1241 boolean handle_gadgets =
1242 (game_status != GAME_MODE_PLAYING ||
1243 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1245 if (handle_gadgets &&
1246 HandleGadgets(mx, my, button))
1248 /* do not handle this button event anymore */
1249 mx = my = -32; /* force mouse event to be outside screen tiles */
1252 if (HandleGadgets(mx, my, button))
1254 /* do not handle this button event anymore */
1255 mx = my = -32; /* force mouse event to be outside screen tiles */
1259 if (HandleGlobalAnimClicks(mx, my, button))
1261 /* do not handle this button event anymore */
1262 mx = my = -32; /* force mouse event to be outside screen tiles */
1265 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1268 /* do not use scroll wheel button events for anything other than gadgets */
1269 if (IS_WHEEL_BUTTON(button_nr))
1272 switch (game_status)
1274 case GAME_MODE_TITLE:
1275 HandleTitleScreen(mx, my, 0, 0, button);
1278 case GAME_MODE_MAIN:
1279 HandleMainMenu(mx, my, 0, 0, button);
1282 case GAME_MODE_PSEUDO_TYPENAME:
1283 HandleTypeName(0, KSYM_Return);
1286 case GAME_MODE_LEVELS:
1287 HandleChooseLevelSet(mx, my, 0, 0, button);
1290 case GAME_MODE_LEVELNR:
1291 HandleChooseLevelNr(mx, my, 0, 0, button);
1294 case GAME_MODE_SCORES:
1295 HandleHallOfFame(0, 0, 0, 0, button);
1298 case GAME_MODE_EDITOR:
1299 HandleLevelEditorIdle();
1302 case GAME_MODE_INFO:
1303 HandleInfoScreen(mx, my, 0, 0, button);
1306 case GAME_MODE_SETUP:
1307 HandleSetupScreen(mx, my, 0, 0, button);
1310 #if defined(TARGET_SDL2)
1311 case GAME_MODE_PLAYING:
1312 HandleFollowFinger(mx, my, button);
1316 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1317 GetKeyModState() & KMOD_Control)
1318 DumpTileFromScreen(mx, my);
1328 static boolean is_string_suffix(char *string, char *suffix)
1330 int string_len = strlen(string);
1331 int suffix_len = strlen(suffix);
1333 if (suffix_len > string_len)
1336 return (strEqual(&string[string_len - suffix_len], suffix));
1339 #define MAX_CHEAT_INPUT_LEN 32
1341 static void HandleKeysSpecial(Key key)
1343 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1344 char letter = getCharFromKey(key);
1345 int cheat_input_len = strlen(cheat_input);
1351 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1353 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1354 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1356 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1359 cheat_input[cheat_input_len++] = letter;
1360 cheat_input[cheat_input_len] = '\0';
1362 #if DEBUG_EVENTS_KEY
1363 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1366 if (game_status == GAME_MODE_MAIN)
1368 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1369 is_string_suffix(cheat_input, ":ist"))
1371 InsertSolutionTape();
1373 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1374 is_string_suffix(cheat_input, ":rg"))
1376 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1379 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1380 is_string_suffix(cheat_input, ":rs"))
1382 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1385 else if (is_string_suffix(cheat_input, ":reload-music") ||
1386 is_string_suffix(cheat_input, ":rm"))
1388 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1391 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1392 is_string_suffix(cheat_input, ":ra"))
1394 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1395 1 << ARTWORK_TYPE_SOUNDS |
1396 1 << ARTWORK_TYPE_MUSIC);
1399 else if (is_string_suffix(cheat_input, ":dump-level") ||
1400 is_string_suffix(cheat_input, ":dl"))
1404 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1405 is_string_suffix(cheat_input, ":dt"))
1409 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1410 is_string_suffix(cheat_input, ":ft"))
1412 /* fix single-player tapes that contain player input for more than one
1413 player (due to a bug in 3.3.1.2 and earlier versions), which results
1414 in playing levels with more than one player in multi-player mode,
1415 even though the tape was originally recorded in single-player mode */
1417 /* remove player input actions for all players but the first one */
1418 for (i = 1; i < MAX_PLAYERS; i++)
1419 tape.player_participates[i] = FALSE;
1421 tape.changed = TRUE;
1423 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1424 is_string_suffix(cheat_input, ":snl"))
1426 SaveNativeLevel(&level);
1429 else if (game_status == GAME_MODE_PLAYING)
1432 if (is_string_suffix(cheat_input, ".q"))
1433 DEBUG_SetMaximumDynamite();
1436 else if (game_status == GAME_MODE_EDITOR)
1438 if (is_string_suffix(cheat_input, ":dump-brush") ||
1439 is_string_suffix(cheat_input, ":DB"))
1443 else if (is_string_suffix(cheat_input, ":DDB"))
1450 void HandleKeysDebug(Key key)
1455 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1457 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1459 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1461 if (key == setup.debug.frame_delay_key[i] &&
1462 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1464 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1465 setup.debug.frame_delay[i] : setup.game_frame_delay);
1467 if (!setup.debug.frame_delay_game_only)
1468 MenuFrameDelay = GameFrameDelay;
1470 SetVideoFrameDelay(GameFrameDelay);
1472 if (GameFrameDelay > ONE_SECOND_DELAY)
1473 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1474 else if (GameFrameDelay != 0)
1475 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1476 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1477 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1479 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1486 if (game_status == GAME_MODE_PLAYING)
1490 options.debug = !options.debug;
1492 Error(ERR_DEBUG, "debug mode %s",
1493 (options.debug ? "enabled" : "disabled"));
1495 else if (key == KSYM_v)
1497 Error(ERR_DEBUG, "currently using game engine version %d",
1498 game.engine_version);
1504 void HandleKey(Key key, int key_status)
1506 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1507 static boolean ignore_repeated_key = FALSE;
1508 static struct SetupKeyboardInfo ski;
1509 static struct SetupShortcutInfo ssi;
1518 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1519 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1520 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1521 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1522 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1523 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1528 if (game_status == GAME_MODE_PLAYING)
1530 /* only needed for single-step tape recording mode */
1531 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1532 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1533 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1534 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1537 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1539 byte key_action = 0;
1541 if (setup.input[pnr].use_joystick)
1544 ski = setup.input[pnr].key;
1546 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1547 if (key == *key_info[i].key_custom)
1548 key_action |= key_info[i].action;
1550 /* use combined snap+direction keys for the first player only */
1553 ssi = setup.shortcut;
1555 for (i = 0; i < NUM_DIRECTIONS; i++)
1556 if (key == *key_info[i].key_snap)
1557 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1560 /* clear delayed snap and drop actions in single step mode (see below) */
1561 if (tape.single_step)
1563 if (clear_snap_button[pnr])
1565 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1566 clear_snap_button[pnr] = FALSE;
1569 if (clear_drop_button[pnr])
1571 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1572 clear_drop_button[pnr] = FALSE;
1576 if (key_status == KEY_PRESSED)
1577 stored_player[pnr].action |= key_action;
1579 stored_player[pnr].action &= ~key_action;
1581 if (tape.single_step && tape.recording && tape.pausing)
1583 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1585 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1587 /* if snap key already pressed, don't snap when releasing (below) */
1588 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1589 element_snapped[pnr] = TRUE;
1591 /* if drop key already pressed, don't drop when releasing (below) */
1592 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1593 element_dropped[pnr] = TRUE;
1595 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1597 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1598 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1601 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1602 getRedDiskReleaseFlag_SP() == 0)
1603 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1605 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1608 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1610 if (key_action & KEY_BUTTON_SNAP)
1612 /* if snap key was released without moving (see above), snap now */
1613 if (!element_snapped[pnr])
1615 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1617 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1619 /* clear delayed snap button on next event */
1620 clear_snap_button[pnr] = TRUE;
1623 element_snapped[pnr] = FALSE;
1626 if (key_action & KEY_BUTTON_DROP &&
1627 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1629 /* if drop key was released without moving (see above), drop now */
1630 if (!element_dropped[pnr])
1632 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1634 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1635 getRedDiskReleaseFlag_SP() != 0)
1636 stored_player[pnr].action |= KEY_BUTTON_DROP;
1638 /* clear delayed drop button on next event */
1639 clear_drop_button[pnr] = TRUE;
1642 element_dropped[pnr] = FALSE;
1646 else if (tape.recording && tape.pausing)
1648 /* prevent key release events from un-pausing a paused game */
1649 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1650 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1656 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1657 if (key == key_info[i].key_default)
1658 joy |= key_info[i].action;
1663 if (key_status == KEY_PRESSED)
1664 key_joystick_mapping |= joy;
1666 key_joystick_mapping &= ~joy;
1671 if (game_status != GAME_MODE_PLAYING)
1672 key_joystick_mapping = 0;
1674 if (key_status == KEY_RELEASED)
1676 // reset flag to ignore repeated "key pressed" events after key release
1677 ignore_repeated_key = FALSE;
1682 if ((key == KSYM_F11 ||
1683 ((key == KSYM_Return ||
1684 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1685 video.fullscreen_available &&
1686 !ignore_repeated_key)
1688 setup.fullscreen = !setup.fullscreen;
1690 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1692 if (game_status == GAME_MODE_SETUP)
1693 RedrawSetupScreenAfterFullscreenToggle();
1695 // set flag to ignore repeated "key pressed" events
1696 ignore_repeated_key = TRUE;
1701 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1702 key == KSYM_minus || key == KSYM_KP_Subtract ||
1703 key == KSYM_plus || key == KSYM_KP_Add ||
1704 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1705 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1706 video.window_scaling_available &&
1707 !video.fullscreen_enabled)
1709 if (key == KSYM_0 || key == KSYM_KP_0)
1710 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1711 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1712 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1714 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1716 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1717 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1718 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1719 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1721 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1723 if (game_status == GAME_MODE_SETUP)
1724 RedrawSetupScreenAfterFullscreenToggle();
1729 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1730 key == KSYM_Return ||
1731 key == KSYM_Escape)))
1733 /* do not handle this key event anymore */
1734 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1738 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1739 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1746 if (game_status == GAME_MODE_MAIN &&
1747 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1749 StartGameActions(options.network, setup.autorecord, level.random_seed);
1754 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1756 if (key == setup.shortcut.save_game)
1758 else if (key == setup.shortcut.load_game)
1760 else if (key == setup.shortcut.toggle_pause)
1761 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1763 HandleTapeButtonKeys(key);
1764 HandleSoundButtonKeys(key);
1767 if (game_status == GAME_MODE_PLAYING && !network_playing)
1769 int centered_player_nr_next = -999;
1771 if (key == setup.shortcut.focus_player_all)
1772 centered_player_nr_next = -1;
1774 for (i = 0; i < MAX_PLAYERS; i++)
1775 if (key == setup.shortcut.focus_player[i])
1776 centered_player_nr_next = i;
1778 if (centered_player_nr_next != -999)
1780 game.centered_player_nr_next = centered_player_nr_next;
1781 game.set_centered_player = TRUE;
1785 tape.centered_player_nr_next = game.centered_player_nr_next;
1786 tape.set_centered_player = TRUE;
1791 HandleKeysSpecial(key);
1793 if (HandleGadgetsKeyInput(key))
1795 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1796 key = KSYM_UNDEFINED;
1799 switch (game_status)
1801 case GAME_MODE_PSEUDO_TYPENAME:
1802 HandleTypeName(0, key);
1805 case GAME_MODE_TITLE:
1806 case GAME_MODE_MAIN:
1807 case GAME_MODE_LEVELS:
1808 case GAME_MODE_LEVELNR:
1809 case GAME_MODE_SETUP:
1810 case GAME_MODE_INFO:
1811 case GAME_MODE_SCORES:
1816 if (game_status == GAME_MODE_TITLE)
1817 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1818 else if (game_status == GAME_MODE_MAIN)
1819 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1820 else if (game_status == GAME_MODE_LEVELS)
1821 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1822 else if (game_status == GAME_MODE_LEVELNR)
1823 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1824 else if (game_status == GAME_MODE_SETUP)
1825 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1826 else if (game_status == GAME_MODE_INFO)
1827 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1828 else if (game_status == GAME_MODE_SCORES)
1829 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1833 if (game_status != GAME_MODE_MAIN)
1834 FadeSkipNextFadeIn();
1836 if (game_status == GAME_MODE_TITLE)
1837 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1838 else if (game_status == GAME_MODE_LEVELS)
1839 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1840 else if (game_status == GAME_MODE_LEVELNR)
1841 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1842 else if (game_status == GAME_MODE_SETUP)
1843 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1844 else if (game_status == GAME_MODE_INFO)
1845 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1846 else if (game_status == GAME_MODE_SCORES)
1847 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1851 if (game_status == GAME_MODE_LEVELS)
1852 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1853 else if (game_status == GAME_MODE_LEVELNR)
1854 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1855 else if (game_status == GAME_MODE_SETUP)
1856 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1857 else if (game_status == GAME_MODE_INFO)
1858 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1859 else if (game_status == GAME_MODE_SCORES)
1860 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1863 case KSYM_Page_Down:
1864 if (game_status == GAME_MODE_LEVELS)
1865 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1866 else if (game_status == GAME_MODE_LEVELNR)
1867 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1868 else if (game_status == GAME_MODE_SETUP)
1869 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1870 else if (game_status == GAME_MODE_INFO)
1871 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1872 else if (game_status == GAME_MODE_SCORES)
1873 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1881 case GAME_MODE_EDITOR:
1882 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1883 HandleLevelEditorKeyInput(key);
1886 case GAME_MODE_PLAYING:
1891 RequestQuitGame(setup.ask_on_escape);
1901 if (key == KSYM_Escape)
1903 SetGameStatus(GAME_MODE_MAIN);
1911 HandleKeysDebug(key);
1914 void HandleNoEvent()
1916 // if (button_status && game_status != GAME_MODE_PLAYING)
1917 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1919 HandleButton(0, 0, button_status, -button_status);
1926 #if defined(NETWORK_AVALIABLE)
1927 if (options.network)
1931 switch (game_status)
1933 case GAME_MODE_MAIN:
1934 DrawPreviewLevelAnimation();
1937 case GAME_MODE_EDITOR:
1938 HandleLevelEditorIdle();
1941 #if defined(TARGET_SDL2)
1942 case GAME_MODE_PLAYING:
1943 HandleFollowFinger(-1, -1, -1);
1952 static int HandleJoystickForAllPlayers()
1956 boolean no_joysticks_configured = TRUE;
1957 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1958 static byte joy_action_last[MAX_PLAYERS];
1960 for (i = 0; i < MAX_PLAYERS; i++)
1961 if (setup.input[i].use_joystick)
1962 no_joysticks_configured = FALSE;
1964 /* if no joysticks configured, map connected joysticks to players */
1965 if (no_joysticks_configured)
1966 use_as_joystick_nr = TRUE;
1968 for (i = 0; i < MAX_PLAYERS; i++)
1970 byte joy_action = 0;
1972 joy_action = JoystickExt(i, use_as_joystick_nr);
1973 result |= joy_action;
1975 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1976 joy_action != joy_action_last[i])
1977 stored_player[i].action = joy_action;
1979 joy_action_last[i] = joy_action;
1985 void HandleJoystick()
1987 int joystick = HandleJoystickForAllPlayers();
1988 int keyboard = key_joystick_mapping;
1989 int joy = (joystick | keyboard);
1990 int left = joy & JOY_LEFT;
1991 int right = joy & JOY_RIGHT;
1992 int up = joy & JOY_UP;
1993 int down = joy & JOY_DOWN;
1994 int button = joy & JOY_BUTTON;
1995 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1996 int dx = (left ? -1 : right ? 1 : 0);
1997 int dy = (up ? -1 : down ? 1 : 0);
1999 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2001 /* do not handle this button event anymore */
2005 switch (game_status)
2007 case GAME_MODE_TITLE:
2008 case GAME_MODE_MAIN:
2009 case GAME_MODE_LEVELS:
2010 case GAME_MODE_LEVELNR:
2011 case GAME_MODE_SETUP:
2012 case GAME_MODE_INFO:
2014 static unsigned int joystickmove_delay = 0;
2015 static unsigned int joystickmove_delay_value = GADGET_FRAME_DELAY;
2016 static int joystick_last = 0;
2018 if (joystick && !button &&
2019 !DelayReached(&joystickmove_delay, joystickmove_delay_value))
2021 /* delay joystick actions if buttons/axes continually pressed */
2022 newbutton = dx = dy = 0;
2026 /* start with longer delay, then continue with shorter delay */
2027 if (joystick != joystick_last)
2028 joystickmove_delay_value = GADGET_FRAME_DELAY_FIRST;
2030 joystickmove_delay_value = GADGET_FRAME_DELAY;
2033 if (game_status == GAME_MODE_TITLE)
2034 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2035 else if (game_status == GAME_MODE_MAIN)
2036 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2037 else if (game_status == GAME_MODE_LEVELS)
2038 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2039 else if (game_status == GAME_MODE_LEVELNR)
2040 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2041 else if (game_status == GAME_MODE_SETUP)
2042 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2043 else if (game_status == GAME_MODE_INFO)
2044 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2046 joystick_last = joystick;
2051 case GAME_MODE_SCORES:
2052 HandleHallOfFame(0, 0, dx, dy, !newbutton);
2055 case GAME_MODE_PLAYING:
2056 if (tape.playing || keyboard)
2057 newbutton = ((joy & JOY_BUTTON) != 0);
2059 if (newbutton && AllPlayersGone)
2066 if (tape.recording && tape.pausing)
2068 if (joystick & JOY_ACTION)
2069 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2079 void HandleSpecialGameControllerButtons(Event *event)
2081 #if defined(TARGET_SDL2)
2082 switch (event->type)
2084 case SDL_CONTROLLERBUTTONDOWN:
2085 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2086 HandleKey(KSYM_space, KEY_PRESSED);
2087 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2088 HandleKey(KSYM_Escape, KEY_PRESSED);
2092 case SDL_CONTROLLERBUTTONUP:
2093 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2094 HandleKey(KSYM_space, KEY_RELEASED);
2095 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2096 HandleKey(KSYM_Escape, KEY_RELEASED);