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_WINDOW (DEBUG_EVENTS * 0)
31 #define DEBUG_EVENTS_FINGER (DEBUG_EVENTS * 0)
32 #define DEBUG_EVENTS_TEXT (DEBUG_EVENTS * 1)
33 #define DEBUG_EVENTS_KEY (DEBUG_EVENTS * 1)
36 static boolean cursor_inside_playfield = FALSE;
37 static int cursor_mode_last = CURSOR_DEFAULT;
38 static unsigned int special_cursor_delay = 0;
39 static unsigned int special_cursor_delay_value = 1000;
41 /* event filter especially needed for SDL event filtering due to
42 delay problems with lots of mouse motion events when mouse button
43 not pressed (X11 can handle this with 'PointerMotionHintMask') */
45 /* event filter addition for SDL2: as SDL2 does not have a function to enable
46 or disable keyboard auto-repeat, filter repeated keyboard events instead */
48 static int FilterEventsExt(const Event *event)
52 #if defined(TARGET_SDL2)
53 /* skip repeated key press events if keyboard auto-repeat is disabled */
54 if (event->type == EVENT_KEYPRESS &&
60 /* non-motion events are directly passed to event handler functions */
61 if (event->type != EVENT_MOTIONNOTIFY)
64 motion = (MotionEvent *)event;
65 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
66 motion->y >= SY && motion->y < SY + SYSIZE);
68 /* do no reset mouse cursor before all pending events have been processed */
69 if (gfx.cursor_mode == cursor_mode_last &&
70 ((game_status == GAME_MODE_TITLE &&
71 gfx.cursor_mode == CURSOR_NONE) ||
72 (game_status == GAME_MODE_PLAYING &&
73 gfx.cursor_mode == CURSOR_PLAYFIELD)))
75 SetMouseCursor(CURSOR_DEFAULT);
77 DelayReached(&special_cursor_delay, 0);
79 cursor_mode_last = CURSOR_DEFAULT;
82 /* skip mouse motion events without pressed button outside level editor */
83 if (button_status == MB_RELEASED &&
84 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
90 #if defined(TARGET_SDL2)
91 int FilterEvents(void *userdata, Event *event)
93 return FilterEventsExt(event);
96 int FilterEvents(const Event *event)
98 return FilterEventsExt(event);
102 /* to prevent delay problems, skip mouse motion events if the very next
103 event is also a mouse motion event (and therefore effectively only
104 handling the last of a row of mouse motion events in the event queue) */
106 boolean SkipPressedMouseMotionEvent(const Event *event)
108 /* nothing to do if the current event is not a mouse motion event */
109 if (event->type != EVENT_MOTIONNOTIFY)
112 /* only skip motion events with pressed button outside the game */
113 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
120 PeekEvent(&next_event);
122 /* if next event is also a mouse motion event, skip the current one */
123 if (next_event.type == EVENT_MOTIONNOTIFY)
130 /* this is only really needed for non-SDL targets to filter unwanted events;
131 when using SDL with properly installed event filter, this function can be
132 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
134 boolean NextValidEvent(Event *event)
136 while (PendingEvent())
138 boolean handle_this_event = FALSE;
142 if (FilterEventsExt(event))
143 handle_this_event = TRUE;
145 if (SkipPressedMouseMotionEvent(event))
146 handle_this_event = FALSE;
148 if (handle_this_event)
158 unsigned int event_frame_delay = 0;
159 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
161 ResetDelayCounter(&event_frame_delay);
163 while (NextValidEvent(&event))
167 case EVENT_BUTTONPRESS:
168 case EVENT_BUTTONRELEASE:
169 HandleButtonEvent((ButtonEvent *) &event);
172 case EVENT_MOTIONNOTIFY:
173 HandleMotionEvent((MotionEvent *) &event);
176 #if defined(TARGET_SDL2)
177 case SDL_WINDOWEVENT:
178 HandleWindowEvent((WindowEvent *) &event);
181 case EVENT_FINGERPRESS:
182 case EVENT_FINGERRELEASE:
183 case EVENT_FINGERMOTION:
184 HandleFingerEvent((FingerEvent *) &event);
187 case EVENT_TEXTINPUT:
188 HandleTextEvent((TextEvent *) &event);
191 case SDL_APP_WILLENTERBACKGROUND:
192 case SDL_APP_DIDENTERBACKGROUND:
193 case SDL_APP_WILLENTERFOREGROUND:
194 case SDL_APP_DIDENTERFOREGROUND:
195 HandlePauseResumeEvent((PauseResumeEvent *) &event);
200 case EVENT_KEYRELEASE:
201 HandleKeyEvent((KeyEvent *) &event);
205 HandleOtherEvents(&event);
209 // do not handle events for longer than standard frame delay period
210 if (DelayReached(&event_frame_delay, event_frame_delay_value))
215 void HandleOtherEvents(Event *event)
220 HandleExposeEvent((ExposeEvent *) event);
223 case EVENT_UNMAPNOTIFY:
225 /* This causes the game to stop not only when iconified, but also
226 when on another virtual desktop, which might be not desired. */
227 SleepWhileUnmapped();
233 HandleFocusEvent((FocusChangeEvent *) event);
236 case EVENT_CLIENTMESSAGE:
237 HandleClientMessageEvent((ClientMessageEvent *) event);
240 #if defined(TARGET_SDL)
241 case SDL_JOYAXISMOTION:
242 case SDL_JOYBUTTONDOWN:
243 case SDL_JOYBUTTONUP:
244 HandleJoystickEvent(event);
248 HandleWindowManagerEvent(event);
257 void HandleMouseCursor()
259 if (game_status == GAME_MODE_TITLE)
261 /* when showing title screens, hide mouse pointer (if not moved) */
263 if (gfx.cursor_mode != CURSOR_NONE &&
264 DelayReached(&special_cursor_delay, special_cursor_delay_value))
266 SetMouseCursor(CURSOR_NONE);
269 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
272 /* when playing, display a special mouse pointer inside the playfield */
274 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
275 cursor_inside_playfield &&
276 DelayReached(&special_cursor_delay, special_cursor_delay_value))
278 SetMouseCursor(CURSOR_PLAYFIELD);
281 else if (gfx.cursor_mode != CURSOR_DEFAULT)
283 SetMouseCursor(CURSOR_DEFAULT);
286 /* this is set after all pending events have been processed */
287 cursor_mode_last = gfx.cursor_mode;
299 /* also execute after pending events have been processed before */
302 /* don't use all CPU time when idle; the main loop while playing
303 has its own synchronization and is CPU friendly, too */
305 if (game_status == GAME_MODE_PLAYING)
308 /* always copy backbuffer to visible screen for every video frame */
311 /* reset video frame delay to default (may change again while playing) */
312 SetVideoFrameDelay(MenuFrameDelay);
314 if (game_status == GAME_MODE_QUIT)
319 void ClearEventQueue()
321 while (PendingEvent())
329 case EVENT_BUTTONRELEASE:
330 button_status = MB_RELEASED;
333 case EVENT_KEYRELEASE:
338 HandleOtherEvents(&event);
344 void ClearPlayerAction()
348 /* simulate key release events for still pressed keys */
349 key_joystick_mapping = 0;
350 for (i = 0; i < MAX_PLAYERS; i++)
351 stored_player[i].action = 0;
354 void SleepWhileUnmapped()
356 boolean window_unmapped = TRUE;
358 KeyboardAutoRepeatOn();
360 while (window_unmapped)
368 case EVENT_BUTTONRELEASE:
369 button_status = MB_RELEASED;
372 case EVENT_KEYRELEASE:
373 key_joystick_mapping = 0;
376 case EVENT_MAPNOTIFY:
377 window_unmapped = FALSE;
380 case EVENT_UNMAPNOTIFY:
381 /* this is only to surely prevent the 'should not happen' case
382 * of recursively looping between 'SleepWhileUnmapped()' and
383 * 'HandleOtherEvents()' which usually calls this funtion.
388 HandleOtherEvents(&event);
393 if (game_status == GAME_MODE_PLAYING)
394 KeyboardAutoRepeatOffUnlessAutoplay();
397 void HandleExposeEvent(ExposeEvent *event)
401 void HandleButtonEvent(ButtonEvent *event)
403 #if DEBUG_EVENTS_BUTTON
404 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
406 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
410 motion_status = FALSE;
412 if (event->type == EVENT_BUTTONPRESS)
413 button_status = event->button;
415 button_status = MB_RELEASED;
417 HandleButton(event->x, event->y, button_status, event->button);
420 void HandleMotionEvent(MotionEvent *event)
422 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
425 motion_status = TRUE;
427 #if DEBUG_EVENTS_MOTION
428 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
429 button_status, event->x, event->y);
432 HandleButton(event->x, event->y, button_status, button_status);
435 #if defined(TARGET_SDL2)
437 void HandleWindowEvent(WindowEvent *event)
439 #if DEBUG_EVENTS_WINDOW
440 int subtype = event->event;
443 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
444 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
445 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
446 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
447 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
448 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
449 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
450 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
451 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
452 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
453 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
454 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
455 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
456 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
459 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
460 event_name, event->data1, event->data2);
464 // (not needed, as the screen gets redrawn every 20 ms anyway)
465 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
466 event->event == SDL_WINDOWEVENT_RESIZED ||
467 event->event == SDL_WINDOWEVENT_EXPOSED)
471 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
473 int new_window_width = event->data1;
474 int new_window_height = event->data2;
476 // if window size has changed after resizing, calculate new scaling factor
477 if (new_window_width != video.window_width ||
478 new_window_height != video.window_height)
480 int new_xpercent = (100 * new_window_width / video.width);
481 int new_ypercent = (100 * new_window_height / video.height);
483 // (extreme window scaling allowed, but cannot be saved permanently)
484 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
485 setup.window_scaling_percent =
486 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
487 MAX_WINDOW_SCALING_PERCENT);
489 video.window_width = new_window_width;
490 video.window_height = new_window_height;
492 if (game_status == GAME_MODE_SETUP)
493 RedrawSetupScreenAfterFullscreenToggle();
500 #define NUM_TOUCH_FINGERS 3
505 SDL_FingerID finger_id;
508 } touch_info[NUM_TOUCH_FINGERS];
510 void HandleFingerEvent(FingerEvent *event)
512 static Key motion_key_x = KSYM_UNDEFINED;
513 static Key motion_key_y = KSYM_UNDEFINED;
514 static Key button_key = KSYM_UNDEFINED;
515 static float motion_x1, motion_y1;
516 static float button_x1, button_y1;
517 static SDL_FingerID motion_id = -1;
518 static SDL_FingerID button_id = -1;
519 int move_trigger_distance_percent = 2; // percent of touchpad width/height
520 int drop_trigger_distance_percent = 5; // percent of touchpad width/height
521 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
522 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
523 float event_x = event->x;
524 float event_y = event->y;
526 #if DEBUG_EVENTS_FINGER
527 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
528 event->type == EVENT_FINGERPRESS ? "pressed" :
529 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
533 event->dx, event->dy,
537 if (game_status != GAME_MODE_PLAYING)
540 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
542 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
544 Key key = (event->x < 1.0 / 3.0 ?
545 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
546 setup.input[0].key.drop) :
547 event->x > 2.0 / 3.0 ?
548 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
549 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
550 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
551 setup.input[0].key.right) :
553 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
557 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
558 getKeyNameFromKey(key), key_status_name, event->fingerId);
560 // check if we already know this touch event's finger id
561 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
563 if (touch_info[i].touched &&
564 touch_info[i].finger_id == event->fingerId)
566 // Error(ERR_DEBUG, "MARK 1: %d", i);
572 if (i >= NUM_TOUCH_FINGERS)
574 if (key_status == KEY_PRESSED)
576 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
578 // unknown finger id -- get new, empty slot, if available
579 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
581 if (touch_info[i].counter < oldest_counter)
584 oldest_counter = touch_info[i].counter;
586 // Error(ERR_DEBUG, "MARK 2: %d", i);
589 if (!touch_info[i].touched)
591 // Error(ERR_DEBUG, "MARK 3: %d", i);
597 if (i >= NUM_TOUCH_FINGERS)
599 // all slots allocated -- use oldest slot
602 // Error(ERR_DEBUG, "MARK 4: %d", i);
607 // release of previously unknown key (should not happen)
609 if (key != KSYM_UNDEFINED)
611 HandleKey(key, KEY_RELEASED);
613 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
614 getKeyNameFromKey(key), "KEY_RELEASED", i);
619 if (i < NUM_TOUCH_FINGERS)
621 if (key_status == KEY_PRESSED)
623 if (touch_info[i].key != key)
625 if (touch_info[i].key != KSYM_UNDEFINED)
627 HandleKey(touch_info[i].key, KEY_RELEASED);
629 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
630 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
633 if (key != KSYM_UNDEFINED)
635 HandleKey(key, KEY_PRESSED);
637 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
638 getKeyNameFromKey(key), "KEY_PRESSED", i);
642 touch_info[i].touched = TRUE;
643 touch_info[i].finger_id = event->fingerId;
644 touch_info[i].counter = Counter();
645 touch_info[i].key = key;
649 if (touch_info[i].key != KSYM_UNDEFINED)
651 HandleKey(touch_info[i].key, KEY_RELEASED);
653 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
654 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
657 touch_info[i].touched = FALSE;
658 touch_info[i].finger_id = 0;
659 touch_info[i].counter = 0;
660 touch_info[i].key = 0;
667 // use touch direction control
669 if (event->type == EVENT_FINGERPRESS)
671 if (event_x > 1.0 / 3.0)
675 motion_id = event->fingerId;
680 motion_key_x = KSYM_UNDEFINED;
681 motion_key_y = KSYM_UNDEFINED;
683 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
689 button_id = event->fingerId;
694 button_key = setup.input[0].key.snap;
696 HandleKey(button_key, KEY_PRESSED);
698 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
701 else if (event->type == EVENT_FINGERRELEASE)
703 if (event->fingerId == motion_id)
707 if (motion_key_x != KSYM_UNDEFINED)
708 HandleKey(motion_key_x, KEY_RELEASED);
709 if (motion_key_y != KSYM_UNDEFINED)
710 HandleKey(motion_key_y, KEY_RELEASED);
712 motion_key_x = KSYM_UNDEFINED;
713 motion_key_y = KSYM_UNDEFINED;
715 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
717 else if (event->fingerId == button_id)
721 if (button_key != KSYM_UNDEFINED)
722 HandleKey(button_key, KEY_RELEASED);
724 button_key = KSYM_UNDEFINED;
726 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
729 else if (event->type == EVENT_FINGERMOTION)
731 if (event->fingerId == motion_id)
733 float distance_x = ABS(event_x - motion_x1);
734 float distance_y = ABS(event_y - motion_y1);
735 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
736 event_x > motion_x1 ? setup.input[0].key.right :
738 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
739 event_y > motion_y1 ? setup.input[0].key.down :
742 if (distance_x < move_trigger_distance / 2 ||
743 distance_x < distance_y)
744 new_motion_key_x = KSYM_UNDEFINED;
746 if (distance_y < move_trigger_distance / 2 ||
747 distance_y < distance_x)
748 new_motion_key_y = KSYM_UNDEFINED;
750 if (distance_x > move_trigger_distance ||
751 distance_y > move_trigger_distance)
753 if (new_motion_key_x != motion_key_x)
755 if (motion_key_x != KSYM_UNDEFINED)
756 HandleKey(motion_key_x, KEY_RELEASED);
757 if (new_motion_key_x != KSYM_UNDEFINED)
758 HandleKey(new_motion_key_x, KEY_PRESSED);
761 if (new_motion_key_y != motion_key_y)
763 if (motion_key_y != KSYM_UNDEFINED)
764 HandleKey(motion_key_y, KEY_RELEASED);
765 if (new_motion_key_y != KSYM_UNDEFINED)
766 HandleKey(new_motion_key_y, KEY_PRESSED);
772 motion_key_x = new_motion_key_x;
773 motion_key_y = new_motion_key_y;
775 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
778 else if (event->fingerId == button_id)
780 float distance_x = ABS(event_x - button_x1);
781 float distance_y = ABS(event_y - button_y1);
783 if (distance_x < drop_trigger_distance / 2 &&
784 distance_y > drop_trigger_distance)
786 if (button_key == setup.input[0].key.snap)
787 HandleKey(button_key, KEY_RELEASED);
792 button_key = setup.input[0].key.drop;
794 HandleKey(button_key, KEY_PRESSED);
796 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
802 static boolean checkTextInputKeyModState()
804 // when playing, only handle raw key events and ignore text input
805 if (game_status == GAME_MODE_PLAYING)
808 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
811 void HandleTextEvent(TextEvent *event)
813 char *text = event->text;
814 Key key = getKeyFromKeyName(text);
816 #if DEBUG_EVENTS_TEXT
817 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
820 text[0], (int)(text[0]),
822 getKeyNameFromKey(key),
826 #if defined(PLATFORM_ANDROID)
827 if (game_status == GAME_MODE_PSEUDO_TYPENAME)
829 HandleTypeName(0, key);
835 // only handle key input with text modifier keys pressed
836 if (checkTextInputKeyModState())
838 HandleKey(key, KEY_PRESSED);
839 HandleKey(key, KEY_RELEASED);
843 void HandlePauseResumeEvent(PauseResumeEvent *event)
845 if (event->type == SDL_APP_WILLENTERBACKGROUND)
849 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
857 void HandleKeyEvent(KeyEvent *event)
859 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
860 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
861 Key key = GetEventKey(event, with_modifiers);
862 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
865 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
866 event->type == EVENT_KEYPRESS ? "pressed" : "released",
867 event->keysym.scancode,
872 getKeyNameFromKey(key));
875 #if defined(PLATFORM_ANDROID)
876 // always map the "back" button to the "escape" key on Android devices
877 if (key == KSYM_Back)
881 HandleKeyModState(keymod, key_status);
883 #if defined(TARGET_SDL2)
884 // only handle raw key input without text modifier keys pressed
885 if (!checkTextInputKeyModState())
886 HandleKey(key, key_status);
888 HandleKey(key, key_status);
892 void HandleFocusEvent(FocusChangeEvent *event)
894 static int old_joystick_status = -1;
896 if (event->type == EVENT_FOCUSOUT)
898 KeyboardAutoRepeatOn();
899 old_joystick_status = joystick.status;
900 joystick.status = JOYSTICK_NOT_AVAILABLE;
904 else if (event->type == EVENT_FOCUSIN)
906 /* When there are two Rocks'n'Diamonds windows which overlap and
907 the player moves the pointer from one game window to the other,
908 a 'FocusOut' event is generated for the window the pointer is
909 leaving and a 'FocusIn' event is generated for the window the
910 pointer is entering. In some cases, it can happen that the
911 'FocusIn' event is handled by the one game process before the
912 'FocusOut' event by the other game process. In this case the
913 X11 environment would end up with activated keyboard auto repeat,
914 because unfortunately this is a global setting and not (which
915 would be far better) set for each X11 window individually.
916 The effect would be keyboard auto repeat while playing the game
917 (game_status == GAME_MODE_PLAYING), which is not desired.
918 To avoid this special case, we just wait 1/10 second before
919 processing the 'FocusIn' event.
922 if (game_status == GAME_MODE_PLAYING)
925 KeyboardAutoRepeatOffUnlessAutoplay();
928 if (old_joystick_status != -1)
929 joystick.status = old_joystick_status;
933 void HandleClientMessageEvent(ClientMessageEvent *event)
935 if (CheckCloseWindowEvent(event))
939 void HandleWindowManagerEvent(Event *event)
941 #if defined(TARGET_SDL)
942 SDLHandleWindowManagerEvent(event);
946 void HandleButton(int mx, int my, int button, int button_nr)
948 static int old_mx = 0, old_my = 0;
949 boolean button_hold = FALSE;
964 #if defined(PLATFORM_ANDROID)
965 // !!! for now, do not handle gadgets when playing -- maybe fix this !!!
966 if (game_status != GAME_MODE_PLAYING &&
967 HandleGadgets(mx, my, button))
969 /* do not handle this button event anymore */
970 mx = my = -32; /* force mouse event to be outside screen tiles */
973 if (HandleGadgets(mx, my, button))
975 /* do not handle this button event anymore */
976 mx = my = -32; /* force mouse event to be outside screen tiles */
980 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
983 /* do not use scroll wheel button events for anything other than gadgets */
984 if (IS_WHEEL_BUTTON(button_nr))
989 case GAME_MODE_TITLE:
990 HandleTitleScreen(mx, my, 0, 0, button);
994 HandleMainMenu(mx, my, 0, 0, button);
997 case GAME_MODE_PSEUDO_TYPENAME:
998 HandleTypeName(0, KSYM_Return);
1001 case GAME_MODE_LEVELS:
1002 HandleChooseLevelSet(mx, my, 0, 0, button);
1005 case GAME_MODE_LEVELNR:
1006 HandleChooseLevelNr(mx, my, 0, 0, button);
1009 case GAME_MODE_SCORES:
1010 HandleHallOfFame(0, 0, 0, 0, button);
1013 case GAME_MODE_EDITOR:
1014 HandleLevelEditorIdle();
1017 case GAME_MODE_INFO:
1018 HandleInfoScreen(mx, my, 0, 0, button);
1021 case GAME_MODE_SETUP:
1022 HandleSetupScreen(mx, my, 0, 0, button);
1025 case GAME_MODE_PLAYING:
1027 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1028 DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
1029 LEVELY((my - SY) / TILESIZE_VAR));
1030 // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1039 static boolean is_string_suffix(char *string, char *suffix)
1041 int string_len = strlen(string);
1042 int suffix_len = strlen(suffix);
1044 if (suffix_len > string_len)
1047 return (strEqual(&string[string_len - suffix_len], suffix));
1050 #define MAX_CHEAT_INPUT_LEN 32
1052 static void HandleKeysSpecial(Key key)
1054 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1055 char letter = getCharFromKey(key);
1056 int cheat_input_len = strlen(cheat_input);
1062 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1064 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1065 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1067 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1070 cheat_input[cheat_input_len++] = letter;
1071 cheat_input[cheat_input_len] = '\0';
1073 #if DEBUG_EVENTS_KEY
1074 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1077 if (game_status == GAME_MODE_MAIN)
1079 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1080 is_string_suffix(cheat_input, ":ist"))
1082 InsertSolutionTape();
1084 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1085 is_string_suffix(cheat_input, ":rg"))
1087 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1090 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1091 is_string_suffix(cheat_input, ":rs"))
1093 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1096 else if (is_string_suffix(cheat_input, ":reload-music") ||
1097 is_string_suffix(cheat_input, ":rm"))
1099 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1102 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1103 is_string_suffix(cheat_input, ":ra"))
1105 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1106 1 << ARTWORK_TYPE_SOUNDS |
1107 1 << ARTWORK_TYPE_MUSIC);
1110 else if (is_string_suffix(cheat_input, ":dump-level") ||
1111 is_string_suffix(cheat_input, ":dl"))
1115 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1116 is_string_suffix(cheat_input, ":dt"))
1120 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1121 is_string_suffix(cheat_input, ":ft"))
1123 /* fix single-player tapes that contain player input for more than one
1124 player (due to a bug in 3.3.1.2 and earlier versions), which results
1125 in playing levels with more than one player in multi-player mode,
1126 even though the tape was originally recorded in single-player mode */
1128 /* remove player input actions for all players but the first one */
1129 for (i = 1; i < MAX_PLAYERS; i++)
1130 tape.player_participates[i] = FALSE;
1132 tape.changed = TRUE;
1134 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1135 is_string_suffix(cheat_input, ":snl"))
1137 SaveNativeLevel(&level);
1140 else if (game_status == GAME_MODE_PLAYING)
1143 if (is_string_suffix(cheat_input, ".q"))
1144 DEBUG_SetMaximumDynamite();
1147 else if (game_status == GAME_MODE_EDITOR)
1149 if (is_string_suffix(cheat_input, ":dump-brush") ||
1150 is_string_suffix(cheat_input, ":DB"))
1154 else if (is_string_suffix(cheat_input, ":DDB"))
1161 void HandleKey(Key key, int key_status)
1163 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1164 static boolean ignore_repeated_key = FALSE;
1165 static struct SetupKeyboardInfo ski;
1166 static struct SetupShortcutInfo ssi;
1175 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1176 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1177 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1178 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1179 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1180 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1185 if (game_status == GAME_MODE_PLAYING)
1187 /* only needed for single-step tape recording mode */
1188 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1189 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1190 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1191 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1194 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1196 byte key_action = 0;
1198 if (setup.input[pnr].use_joystick)
1201 ski = setup.input[pnr].key;
1203 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1204 if (key == *key_info[i].key_custom)
1205 key_action |= key_info[i].action;
1207 /* use combined snap+direction keys for the first player only */
1210 ssi = setup.shortcut;
1212 for (i = 0; i < NUM_DIRECTIONS; i++)
1213 if (key == *key_info[i].key_snap)
1214 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1217 /* clear delayed snap and drop actions in single step mode (see below) */
1218 if (tape.single_step)
1220 if (clear_snap_button[pnr])
1222 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1223 clear_snap_button[pnr] = FALSE;
1226 if (clear_drop_button[pnr])
1228 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1229 clear_drop_button[pnr] = FALSE;
1233 if (key_status == KEY_PRESSED)
1234 stored_player[pnr].action |= key_action;
1236 stored_player[pnr].action &= ~key_action;
1238 if (tape.single_step && tape.recording && tape.pausing)
1240 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1242 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1244 /* if snap key already pressed, don't snap when releasing (below) */
1245 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1246 element_snapped[pnr] = TRUE;
1248 /* if drop key already pressed, don't drop when releasing (below) */
1249 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1250 element_dropped[pnr] = TRUE;
1252 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1254 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1255 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1258 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1259 getRedDiskReleaseFlag_SP() == 0)
1260 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1262 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1265 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1267 if (key_action & KEY_BUTTON_SNAP)
1269 /* if snap key was released without moving (see above), snap now */
1270 if (!element_snapped[pnr])
1272 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1274 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1276 /* clear delayed snap button on next event */
1277 clear_snap_button[pnr] = TRUE;
1280 element_snapped[pnr] = FALSE;
1283 if (key_action & KEY_BUTTON_DROP &&
1284 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1286 /* if drop key was released without moving (see above), drop now */
1287 if (!element_dropped[pnr])
1289 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1291 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1292 getRedDiskReleaseFlag_SP() != 0)
1293 stored_player[pnr].action |= KEY_BUTTON_DROP;
1295 /* clear delayed drop button on next event */
1296 clear_drop_button[pnr] = TRUE;
1299 element_dropped[pnr] = FALSE;
1303 else if (tape.recording && tape.pausing)
1305 /* prevent key release events from un-pausing a paused game */
1306 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1307 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1313 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1314 if (key == key_info[i].key_default)
1315 joy |= key_info[i].action;
1320 if (key_status == KEY_PRESSED)
1321 key_joystick_mapping |= joy;
1323 key_joystick_mapping &= ~joy;
1328 if (game_status != GAME_MODE_PLAYING)
1329 key_joystick_mapping = 0;
1331 if (key_status == KEY_RELEASED)
1333 // reset flag to ignore repeated "key pressed" events after key release
1334 ignore_repeated_key = FALSE;
1339 if ((key == KSYM_F11 ||
1340 ((key == KSYM_Return ||
1341 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1342 video.fullscreen_available &&
1343 !ignore_repeated_key)
1345 setup.fullscreen = !setup.fullscreen;
1347 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1349 if (game_status == GAME_MODE_SETUP)
1350 RedrawSetupScreenAfterFullscreenToggle();
1352 // set flag to ignore repeated "key pressed" events
1353 ignore_repeated_key = TRUE;
1358 if ((key == KSYM_minus ||
1360 key == KSYM_equal || // ("Shift-=" is "+" on US keyboards)
1362 ((GetKeyModState() & KMOD_Control) ||
1363 (GetKeyModState() & KMOD_Alt) ||
1364 (GetKeyModState() & KMOD_Meta)) &&
1365 video.window_scaling_available &&
1366 !video.fullscreen_enabled)
1369 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1371 setup.window_scaling_percent +=
1372 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1374 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1375 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1376 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1377 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1379 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1381 if (game_status == GAME_MODE_SETUP)
1382 RedrawSetupScreenAfterFullscreenToggle();
1387 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1388 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1395 if (game_status == GAME_MODE_MAIN &&
1396 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1398 StartGameActions(options.network, setup.autorecord, level.random_seed);
1403 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1405 if (key == setup.shortcut.save_game)
1407 else if (key == setup.shortcut.load_game)
1409 else if (key == setup.shortcut.toggle_pause)
1410 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1412 HandleTapeButtonKeys(key);
1413 HandleSoundButtonKeys(key);
1416 if (game_status == GAME_MODE_PLAYING && !network_playing)
1418 int centered_player_nr_next = -999;
1420 if (key == setup.shortcut.focus_player_all)
1421 centered_player_nr_next = -1;
1423 for (i = 0; i < MAX_PLAYERS; i++)
1424 if (key == setup.shortcut.focus_player[i])
1425 centered_player_nr_next = i;
1427 if (centered_player_nr_next != -999)
1429 game.centered_player_nr_next = centered_player_nr_next;
1430 game.set_centered_player = TRUE;
1434 tape.centered_player_nr_next = game.centered_player_nr_next;
1435 tape.set_centered_player = TRUE;
1440 HandleKeysSpecial(key);
1442 if (HandleGadgetsKeyInput(key))
1444 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1445 key = KSYM_UNDEFINED;
1448 switch (game_status)
1450 case GAME_MODE_PSEUDO_TYPENAME:
1451 HandleTypeName(0, key);
1454 case GAME_MODE_TITLE:
1455 case GAME_MODE_MAIN:
1456 case GAME_MODE_LEVELS:
1457 case GAME_MODE_LEVELNR:
1458 case GAME_MODE_SETUP:
1459 case GAME_MODE_INFO:
1460 case GAME_MODE_SCORES:
1465 if (game_status == GAME_MODE_TITLE)
1466 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1467 else if (game_status == GAME_MODE_MAIN)
1468 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1469 else if (game_status == GAME_MODE_LEVELS)
1470 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1471 else if (game_status == GAME_MODE_LEVELNR)
1472 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1473 else if (game_status == GAME_MODE_SETUP)
1474 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1475 else if (game_status == GAME_MODE_INFO)
1476 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1477 else if (game_status == GAME_MODE_SCORES)
1478 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1482 if (game_status != GAME_MODE_MAIN)
1483 FadeSkipNextFadeIn();
1485 if (game_status == GAME_MODE_TITLE)
1486 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1487 else if (game_status == GAME_MODE_LEVELS)
1488 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1489 else if (game_status == GAME_MODE_LEVELNR)
1490 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1491 else if (game_status == GAME_MODE_SETUP)
1492 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1493 else if (game_status == GAME_MODE_INFO)
1494 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1495 else if (game_status == GAME_MODE_SCORES)
1496 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1500 if (game_status == GAME_MODE_LEVELS)
1501 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1502 else if (game_status == GAME_MODE_LEVELNR)
1503 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1504 else if (game_status == GAME_MODE_SETUP)
1505 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1506 else if (game_status == GAME_MODE_INFO)
1507 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1508 else if (game_status == GAME_MODE_SCORES)
1509 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1512 case KSYM_Page_Down:
1513 if (game_status == GAME_MODE_LEVELS)
1514 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1515 else if (game_status == GAME_MODE_LEVELNR)
1516 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1517 else if (game_status == GAME_MODE_SETUP)
1518 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1519 else if (game_status == GAME_MODE_INFO)
1520 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1521 else if (game_status == GAME_MODE_SCORES)
1522 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1530 case GAME_MODE_EDITOR:
1531 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1532 HandleLevelEditorKeyInput(key);
1535 case GAME_MODE_PLAYING:
1540 RequestQuitGame(setup.ask_on_escape);
1547 options.debug = FALSE;
1548 printf("debug mode disabled\n");
1552 options.debug = TRUE;
1553 printf("debug mode enabled\n");
1558 printf("::: currently using game engine version %d\n",
1559 game.engine_version);
1570 if (key == KSYM_Escape)
1572 SetGameStatus(GAME_MODE_MAIN);
1581 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1583 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Control) ||
1584 (GetKeyModState() & KMOD_Alt) ||
1585 (GetKeyModState() & KMOD_Meta));
1587 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1589 if (key == setup.debug.frame_delay_key[i] &&
1590 (mod_key_pressed || !setup.debug.frame_delay_use_mod_key))
1592 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1593 setup.debug.frame_delay[i] : GAME_FRAME_DELAY);
1595 if (!setup.debug.frame_delay_game_only)
1596 MenuFrameDelay = GameFrameDelay;
1598 SetVideoFrameDelay(GameFrameDelay);
1600 if (GameFrameDelay > ONE_SECOND_DELAY)
1601 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1602 else if (GameFrameDelay != 0)
1603 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1604 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1605 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1607 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1616 void HandleNoEvent()
1618 // if (button_status && game_status != GAME_MODE_PLAYING)
1619 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1621 HandleButton(0, 0, -button_status, button_status);
1628 #if defined(NETWORK_AVALIABLE)
1629 if (options.network)
1633 switch (game_status)
1635 case GAME_MODE_MAIN:
1636 DrawPreviewLevelAnimation();
1639 case GAME_MODE_EDITOR:
1640 HandleLevelEditorIdle();
1648 static int HandleJoystickForAllPlayers()
1653 for (i = 0; i < MAX_PLAYERS; i++)
1655 byte joy_action = 0;
1658 if (!setup.input[i].use_joystick)
1662 joy_action = Joystick(i);
1663 result |= joy_action;
1665 if (!setup.input[i].use_joystick)
1668 stored_player[i].action = joy_action;
1674 void HandleJoystick()
1676 int joystick = HandleJoystickForAllPlayers();
1677 int keyboard = key_joystick_mapping;
1678 int joy = (joystick | keyboard);
1679 int left = joy & JOY_LEFT;
1680 int right = joy & JOY_RIGHT;
1681 int up = joy & JOY_UP;
1682 int down = joy & JOY_DOWN;
1683 int button = joy & JOY_BUTTON;
1684 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1685 int dx = (left ? -1 : right ? 1 : 0);
1686 int dy = (up ? -1 : down ? 1 : 0);
1688 switch (game_status)
1690 case GAME_MODE_TITLE:
1691 case GAME_MODE_MAIN:
1692 case GAME_MODE_LEVELS:
1693 case GAME_MODE_LEVELNR:
1694 case GAME_MODE_SETUP:
1695 case GAME_MODE_INFO:
1697 static unsigned int joystickmove_delay = 0;
1699 if (joystick && !button &&
1700 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1701 newbutton = dx = dy = 0;
1703 if (game_status == GAME_MODE_TITLE)
1704 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1705 else if (game_status == GAME_MODE_MAIN)
1706 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1707 else if (game_status == GAME_MODE_LEVELS)
1708 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1709 else if (game_status == GAME_MODE_LEVELNR)
1710 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1711 else if (game_status == GAME_MODE_SETUP)
1712 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1713 else if (game_status == GAME_MODE_INFO)
1714 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1718 case GAME_MODE_SCORES:
1719 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1722 case GAME_MODE_PLAYING:
1723 if (tape.playing || keyboard)
1724 newbutton = ((joy & JOY_BUTTON) != 0);
1726 if (newbutton && AllPlayersGone)