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 FilterEventsExt(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 /* non-motion events are directly passed to event handler functions */
62 if (event->type != EVENT_MOTIONNOTIFY)
65 motion = (MotionEvent *)event;
66 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
67 motion->y >= SY && motion->y < SY + SYSIZE);
69 /* do no reset mouse cursor before all pending events have been processed */
70 if (gfx.cursor_mode == cursor_mode_last &&
71 ((game_status == GAME_MODE_TITLE &&
72 gfx.cursor_mode == CURSOR_NONE) ||
73 (game_status == GAME_MODE_PLAYING &&
74 gfx.cursor_mode == CURSOR_PLAYFIELD)))
76 SetMouseCursor(CURSOR_DEFAULT);
78 DelayReached(&special_cursor_delay, 0);
80 cursor_mode_last = CURSOR_DEFAULT;
83 /* skip mouse motion events without pressed button outside level editor */
84 if (button_status == MB_RELEASED &&
85 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
91 #if defined(TARGET_SDL2)
92 int FilterEvents(void *userdata, Event *event)
94 return FilterEventsExt(event);
97 int FilterEvents(const Event *event)
99 return FilterEventsExt(event);
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 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 only really needed for non-SDL targets to filter unwanted events;
132 when using SDL with properly installed event filter, this function can be
133 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
135 boolean NextValidEvent(Event *event)
137 while (PendingEvent())
139 boolean handle_this_event = FALSE;
143 if (FilterEventsExt(event))
144 handle_this_event = TRUE;
146 if (SkipPressedMouseMotionEvent(event))
147 handle_this_event = FALSE;
149 if (handle_this_event)
159 unsigned int event_frame_delay = 0;
160 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
162 ResetDelayCounter(&event_frame_delay);
164 while (NextValidEvent(&event))
168 case EVENT_BUTTONPRESS:
169 case EVENT_BUTTONRELEASE:
170 HandleButtonEvent((ButtonEvent *) &event);
173 case EVENT_MOTIONNOTIFY:
174 HandleMotionEvent((MotionEvent *) &event);
177 #if defined(TARGET_SDL2)
178 case EVENT_WHEELMOTION:
179 HandleWheelEvent((WheelEvent *) &event);
182 case SDL_WINDOWEVENT:
183 HandleWindowEvent((WindowEvent *) &event);
186 case EVENT_FINGERPRESS:
187 case EVENT_FINGERRELEASE:
188 case EVENT_FINGERMOTION:
189 HandleFingerEvent((FingerEvent *) &event);
192 case EVENT_TEXTINPUT:
193 HandleTextEvent((TextEvent *) &event);
196 case SDL_APP_WILLENTERBACKGROUND:
197 case SDL_APP_DIDENTERBACKGROUND:
198 case SDL_APP_WILLENTERFOREGROUND:
199 case SDL_APP_DIDENTERFOREGROUND:
200 HandlePauseResumeEvent((PauseResumeEvent *) &event);
205 case EVENT_KEYRELEASE:
206 HandleKeyEvent((KeyEvent *) &event);
210 HandleOtherEvents(&event);
214 // do not handle events for longer than standard frame delay period
215 if (DelayReached(&event_frame_delay, event_frame_delay_value))
220 void HandleOtherEvents(Event *event)
225 HandleExposeEvent((ExposeEvent *) event);
228 case EVENT_UNMAPNOTIFY:
230 /* This causes the game to stop not only when iconified, but also
231 when on another virtual desktop, which might be not desired. */
232 SleepWhileUnmapped();
238 HandleFocusEvent((FocusChangeEvent *) event);
241 case EVENT_CLIENTMESSAGE:
242 HandleClientMessageEvent((ClientMessageEvent *) event);
245 #if defined(TARGET_SDL)
246 case SDL_JOYAXISMOTION:
247 case SDL_JOYBUTTONDOWN:
248 case SDL_JOYBUTTONUP:
249 HandleJoystickEvent(event);
253 HandleWindowManagerEvent(event);
262 void HandleMouseCursor()
264 if (game_status == GAME_MODE_TITLE)
266 /* when showing title screens, hide mouse pointer (if not moved) */
268 if (gfx.cursor_mode != CURSOR_NONE &&
269 DelayReached(&special_cursor_delay, special_cursor_delay_value))
271 SetMouseCursor(CURSOR_NONE);
274 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
277 /* when playing, display a special mouse pointer inside the playfield */
279 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
280 cursor_inside_playfield &&
281 DelayReached(&special_cursor_delay, special_cursor_delay_value))
283 SetMouseCursor(CURSOR_PLAYFIELD);
286 else if (gfx.cursor_mode != CURSOR_DEFAULT)
288 SetMouseCursor(CURSOR_DEFAULT);
291 /* this is set after all pending events have been processed */
292 cursor_mode_last = gfx.cursor_mode;
304 /* also execute after pending events have been processed before */
307 /* don't use all CPU time when idle; the main loop while playing
308 has its own synchronization and is CPU friendly, too */
310 if (game_status == GAME_MODE_PLAYING)
313 /* always copy backbuffer to visible screen for every video frame */
316 /* reset video frame delay to default (may change again while playing) */
317 SetVideoFrameDelay(MenuFrameDelay);
319 if (game_status == GAME_MODE_QUIT)
324 void ClearEventQueue()
326 while (PendingEvent())
334 case EVENT_BUTTONRELEASE:
335 button_status = MB_RELEASED;
338 case EVENT_KEYRELEASE:
343 HandleOtherEvents(&event);
349 void ClearPlayerAction()
353 /* simulate key release events for still pressed keys */
354 key_joystick_mapping = 0;
355 for (i = 0; i < MAX_PLAYERS; i++)
356 stored_player[i].action = 0;
359 void SleepWhileUnmapped()
361 boolean window_unmapped = TRUE;
363 KeyboardAutoRepeatOn();
365 while (window_unmapped)
373 case EVENT_BUTTONRELEASE:
374 button_status = MB_RELEASED;
377 case EVENT_KEYRELEASE:
378 key_joystick_mapping = 0;
381 case EVENT_MAPNOTIFY:
382 window_unmapped = FALSE;
385 case EVENT_UNMAPNOTIFY:
386 /* this is only to surely prevent the 'should not happen' case
387 * of recursively looping between 'SleepWhileUnmapped()' and
388 * 'HandleOtherEvents()' which usually calls this funtion.
393 HandleOtherEvents(&event);
398 if (game_status == GAME_MODE_PLAYING)
399 KeyboardAutoRepeatOffUnlessAutoplay();
402 void HandleExposeEvent(ExposeEvent *event)
406 void HandleButtonEvent(ButtonEvent *event)
408 #if DEBUG_EVENTS_BUTTON
409 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
411 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
415 motion_status = FALSE;
417 if (event->type == EVENT_BUTTONPRESS)
418 button_status = event->button;
420 button_status = MB_RELEASED;
422 HandleButton(event->x, event->y, button_status, event->button);
425 void HandleMotionEvent(MotionEvent *event)
427 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
430 motion_status = TRUE;
432 #if DEBUG_EVENTS_MOTION
433 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
434 button_status, event->x, event->y);
437 HandleButton(event->x, event->y, button_status, button_status);
440 #if defined(TARGET_SDL2)
442 void HandleWheelEvent(WheelEvent *event)
446 #if DEBUG_EVENTS_WHEEL
448 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
449 event->which, event->x, event->y);
451 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
452 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
453 event->which, event->x, event->y,
454 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
455 "SDL_MOUSEWHEEL_FLIPPED"));
459 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
460 event->x > 0 ? MB_WHEEL_RIGHT :
461 event->y < 0 ? MB_WHEEL_DOWN :
462 event->y > 0 ? MB_WHEEL_UP : 0);
464 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
465 // accelerated mouse wheel available on Mac and Windows
466 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
468 // no accelerated mouse wheel available on Unix/Linux
469 wheel_steps = DEFAULT_WHEEL_STEPS;
472 motion_status = FALSE;
474 button_status = button_nr;
475 HandleButton(0, 0, button_status, -button_nr);
477 button_status = MB_RELEASED;
478 HandleButton(0, 0, button_status, -button_nr);
481 void HandleWindowEvent(WindowEvent *event)
483 #if DEBUG_EVENTS_WINDOW
484 int subtype = event->event;
487 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
488 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
489 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
490 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
491 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
492 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
493 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
494 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
495 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
496 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
497 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
498 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
499 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
500 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
503 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
504 event_name, event->data1, event->data2);
508 // (not needed, as the screen gets redrawn every 20 ms anyway)
509 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
510 event->event == SDL_WINDOWEVENT_RESIZED ||
511 event->event == SDL_WINDOWEVENT_EXPOSED)
515 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
517 int new_window_width = event->data1;
518 int new_window_height = event->data2;
520 // if window size has changed after resizing, calculate new scaling factor
521 if (new_window_width != video.window_width ||
522 new_window_height != video.window_height)
524 int new_xpercent = (100 * new_window_width / video.width);
525 int new_ypercent = (100 * new_window_height / video.height);
527 // (extreme window scaling allowed, but cannot be saved permanently)
528 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
529 setup.window_scaling_percent =
530 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
531 MAX_WINDOW_SCALING_PERCENT);
533 video.window_width = new_window_width;
534 video.window_height = new_window_height;
536 if (game_status == GAME_MODE_SETUP)
537 RedrawSetupScreenAfterFullscreenToggle();
544 #define NUM_TOUCH_FINGERS 3
549 SDL_FingerID finger_id;
552 } touch_info[NUM_TOUCH_FINGERS];
554 void HandleFingerEvent(FingerEvent *event)
556 static Key motion_key_x = KSYM_UNDEFINED;
557 static Key motion_key_y = KSYM_UNDEFINED;
558 static Key button_key = KSYM_UNDEFINED;
559 static float motion_x1, motion_y1;
560 static float button_x1, button_y1;
561 static SDL_FingerID motion_id = -1;
562 static SDL_FingerID button_id = -1;
563 int move_trigger_distance_percent = 2; // percent of touchpad width/height
564 int drop_trigger_distance_percent = 5; // percent of touchpad width/height
565 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
566 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
567 float event_x = event->x;
568 float event_y = event->y;
570 #if DEBUG_EVENTS_FINGER
571 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
572 event->type == EVENT_FINGERPRESS ? "pressed" :
573 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
577 event->dx, event->dy,
581 if (game_status != GAME_MODE_PLAYING)
584 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
586 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
588 Key key = (event->x < 1.0 / 3.0 ?
589 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
590 setup.input[0].key.drop) :
591 event->x > 2.0 / 3.0 ?
592 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
593 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
594 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
595 setup.input[0].key.right) :
597 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
601 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
602 getKeyNameFromKey(key), key_status_name, event->fingerId);
604 // check if we already know this touch event's finger id
605 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
607 if (touch_info[i].touched &&
608 touch_info[i].finger_id == event->fingerId)
610 // Error(ERR_DEBUG, "MARK 1: %d", i);
616 if (i >= NUM_TOUCH_FINGERS)
618 if (key_status == KEY_PRESSED)
620 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
622 // unknown finger id -- get new, empty slot, if available
623 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
625 if (touch_info[i].counter < oldest_counter)
628 oldest_counter = touch_info[i].counter;
630 // Error(ERR_DEBUG, "MARK 2: %d", i);
633 if (!touch_info[i].touched)
635 // Error(ERR_DEBUG, "MARK 3: %d", i);
641 if (i >= NUM_TOUCH_FINGERS)
643 // all slots allocated -- use oldest slot
646 // Error(ERR_DEBUG, "MARK 4: %d", i);
651 // release of previously unknown key (should not happen)
653 if (key != KSYM_UNDEFINED)
655 HandleKey(key, KEY_RELEASED);
657 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
658 getKeyNameFromKey(key), "KEY_RELEASED", i);
663 if (i < NUM_TOUCH_FINGERS)
665 if (key_status == KEY_PRESSED)
667 if (touch_info[i].key != key)
669 if (touch_info[i].key != KSYM_UNDEFINED)
671 HandleKey(touch_info[i].key, KEY_RELEASED);
673 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
674 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
677 if (key != KSYM_UNDEFINED)
679 HandleKey(key, KEY_PRESSED);
681 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
682 getKeyNameFromKey(key), "KEY_PRESSED", i);
686 touch_info[i].touched = TRUE;
687 touch_info[i].finger_id = event->fingerId;
688 touch_info[i].counter = Counter();
689 touch_info[i].key = key;
693 if (touch_info[i].key != KSYM_UNDEFINED)
695 HandleKey(touch_info[i].key, KEY_RELEASED);
697 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
698 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
701 touch_info[i].touched = FALSE;
702 touch_info[i].finger_id = 0;
703 touch_info[i].counter = 0;
704 touch_info[i].key = 0;
711 // use touch direction control
713 if (event->type == EVENT_FINGERPRESS)
715 if (event_x > 1.0 / 3.0)
719 motion_id = event->fingerId;
724 motion_key_x = KSYM_UNDEFINED;
725 motion_key_y = KSYM_UNDEFINED;
727 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
733 button_id = event->fingerId;
738 button_key = setup.input[0].key.snap;
740 HandleKey(button_key, KEY_PRESSED);
742 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
745 else if (event->type == EVENT_FINGERRELEASE)
747 if (event->fingerId == motion_id)
751 if (motion_key_x != KSYM_UNDEFINED)
752 HandleKey(motion_key_x, KEY_RELEASED);
753 if (motion_key_y != KSYM_UNDEFINED)
754 HandleKey(motion_key_y, KEY_RELEASED);
756 motion_key_x = KSYM_UNDEFINED;
757 motion_key_y = KSYM_UNDEFINED;
759 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
761 else if (event->fingerId == button_id)
765 if (button_key != KSYM_UNDEFINED)
766 HandleKey(button_key, KEY_RELEASED);
768 button_key = KSYM_UNDEFINED;
770 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
773 else if (event->type == EVENT_FINGERMOTION)
775 if (event->fingerId == motion_id)
777 float distance_x = ABS(event_x - motion_x1);
778 float distance_y = ABS(event_y - motion_y1);
779 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
780 event_x > motion_x1 ? setup.input[0].key.right :
782 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
783 event_y > motion_y1 ? setup.input[0].key.down :
786 if (distance_x < move_trigger_distance / 2 ||
787 distance_x < distance_y)
788 new_motion_key_x = KSYM_UNDEFINED;
790 if (distance_y < move_trigger_distance / 2 ||
791 distance_y < distance_x)
792 new_motion_key_y = KSYM_UNDEFINED;
794 if (distance_x > move_trigger_distance ||
795 distance_y > move_trigger_distance)
797 if (new_motion_key_x != motion_key_x)
799 if (motion_key_x != KSYM_UNDEFINED)
800 HandleKey(motion_key_x, KEY_RELEASED);
801 if (new_motion_key_x != KSYM_UNDEFINED)
802 HandleKey(new_motion_key_x, KEY_PRESSED);
805 if (new_motion_key_y != motion_key_y)
807 if (motion_key_y != KSYM_UNDEFINED)
808 HandleKey(motion_key_y, KEY_RELEASED);
809 if (new_motion_key_y != KSYM_UNDEFINED)
810 HandleKey(new_motion_key_y, KEY_PRESSED);
816 motion_key_x = new_motion_key_x;
817 motion_key_y = new_motion_key_y;
819 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
822 else if (event->fingerId == button_id)
824 float distance_x = ABS(event_x - button_x1);
825 float distance_y = ABS(event_y - button_y1);
827 if (distance_x < drop_trigger_distance / 2 &&
828 distance_y > drop_trigger_distance)
830 if (button_key == setup.input[0].key.snap)
831 HandleKey(button_key, KEY_RELEASED);
836 button_key = setup.input[0].key.drop;
838 HandleKey(button_key, KEY_PRESSED);
840 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
846 static boolean checkTextInputKeyModState()
848 // when playing, only handle raw key events and ignore text input
849 if (game_status == GAME_MODE_PLAYING)
852 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
855 void HandleTextEvent(TextEvent *event)
857 char *text = event->text;
858 Key key = getKeyFromKeyName(text);
860 #if DEBUG_EVENTS_TEXT
861 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
864 text[0], (int)(text[0]),
866 getKeyNameFromKey(key),
870 #if defined(PLATFORM_ANDROID)
871 if (game_status == GAME_MODE_PSEUDO_TYPENAME)
873 HandleTypeName(0, key);
879 // only handle key input with text modifier keys pressed
880 if (checkTextInputKeyModState())
882 HandleKey(key, KEY_PRESSED);
883 HandleKey(key, KEY_RELEASED);
887 void HandlePauseResumeEvent(PauseResumeEvent *event)
889 if (event->type == SDL_APP_WILLENTERBACKGROUND)
893 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
901 void HandleKeyEvent(KeyEvent *event)
903 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
904 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
905 Key key = GetEventKey(event, with_modifiers);
906 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
909 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
910 event->type == EVENT_KEYPRESS ? "pressed" : "released",
911 event->keysym.scancode,
916 getKeyNameFromKey(key));
919 #if defined(PLATFORM_ANDROID)
920 // always map the "back" button to the "escape" key on Android devices
921 if (key == KSYM_Back)
925 HandleKeyModState(keymod, key_status);
927 #if defined(TARGET_SDL2)
928 // only handle raw key input without text modifier keys pressed
929 if (!checkTextInputKeyModState())
930 HandleKey(key, key_status);
932 HandleKey(key, key_status);
936 void HandleFocusEvent(FocusChangeEvent *event)
938 static int old_joystick_status = -1;
940 if (event->type == EVENT_FOCUSOUT)
942 KeyboardAutoRepeatOn();
943 old_joystick_status = joystick.status;
944 joystick.status = JOYSTICK_NOT_AVAILABLE;
948 else if (event->type == EVENT_FOCUSIN)
950 /* When there are two Rocks'n'Diamonds windows which overlap and
951 the player moves the pointer from one game window to the other,
952 a 'FocusOut' event is generated for the window the pointer is
953 leaving and a 'FocusIn' event is generated for the window the
954 pointer is entering. In some cases, it can happen that the
955 'FocusIn' event is handled by the one game process before the
956 'FocusOut' event by the other game process. In this case the
957 X11 environment would end up with activated keyboard auto repeat,
958 because unfortunately this is a global setting and not (which
959 would be far better) set for each X11 window individually.
960 The effect would be keyboard auto repeat while playing the game
961 (game_status == GAME_MODE_PLAYING), which is not desired.
962 To avoid this special case, we just wait 1/10 second before
963 processing the 'FocusIn' event.
966 if (game_status == GAME_MODE_PLAYING)
969 KeyboardAutoRepeatOffUnlessAutoplay();
972 if (old_joystick_status != -1)
973 joystick.status = old_joystick_status;
977 void HandleClientMessageEvent(ClientMessageEvent *event)
979 if (CheckCloseWindowEvent(event))
983 void HandleWindowManagerEvent(Event *event)
985 #if defined(TARGET_SDL)
986 SDLHandleWindowManagerEvent(event);
990 void HandleButton(int mx, int my, int button, int button_nr)
992 static int old_mx = 0, old_my = 0;
993 boolean button_hold = FALSE;
999 button_nr = -button_nr;
1008 #if defined(PLATFORM_ANDROID)
1009 // !!! for now, do not handle gadgets when playing -- maybe fix this !!!
1010 if (game_status != GAME_MODE_PLAYING &&
1011 HandleGadgets(mx, my, button))
1013 /* do not handle this button event anymore */
1014 mx = my = -32; /* force mouse event to be outside screen tiles */
1017 if (HandleGadgets(mx, my, button))
1019 /* do not handle this button event anymore */
1020 mx = my = -32; /* force mouse event to be outside screen tiles */
1024 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1027 /* do not use scroll wheel button events for anything other than gadgets */
1028 if (IS_WHEEL_BUTTON(button_nr))
1031 switch (game_status)
1033 case GAME_MODE_TITLE:
1034 HandleTitleScreen(mx, my, 0, 0, button);
1037 case GAME_MODE_MAIN:
1038 HandleMainMenu(mx, my, 0, 0, button);
1041 case GAME_MODE_PSEUDO_TYPENAME:
1042 HandleTypeName(0, KSYM_Return);
1045 case GAME_MODE_LEVELS:
1046 HandleChooseLevelSet(mx, my, 0, 0, button);
1049 case GAME_MODE_LEVELNR:
1050 HandleChooseLevelNr(mx, my, 0, 0, button);
1053 case GAME_MODE_SCORES:
1054 HandleHallOfFame(0, 0, 0, 0, button);
1057 case GAME_MODE_EDITOR:
1058 HandleLevelEditorIdle();
1061 case GAME_MODE_INFO:
1062 HandleInfoScreen(mx, my, 0, 0, button);
1065 case GAME_MODE_SETUP:
1066 HandleSetupScreen(mx, my, 0, 0, button);
1069 case GAME_MODE_PLAYING:
1071 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1072 DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
1073 LEVELY((my - SY) / TILESIZE_VAR));
1074 // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1083 static boolean is_string_suffix(char *string, char *suffix)
1085 int string_len = strlen(string);
1086 int suffix_len = strlen(suffix);
1088 if (suffix_len > string_len)
1091 return (strEqual(&string[string_len - suffix_len], suffix));
1094 #define MAX_CHEAT_INPUT_LEN 32
1096 static void HandleKeysSpecial(Key key)
1098 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1099 char letter = getCharFromKey(key);
1100 int cheat_input_len = strlen(cheat_input);
1106 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1108 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1109 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1111 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1114 cheat_input[cheat_input_len++] = letter;
1115 cheat_input[cheat_input_len] = '\0';
1117 #if DEBUG_EVENTS_KEY
1118 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1121 if (game_status == GAME_MODE_MAIN)
1123 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1124 is_string_suffix(cheat_input, ":ist"))
1126 InsertSolutionTape();
1128 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1129 is_string_suffix(cheat_input, ":rg"))
1131 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1134 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1135 is_string_suffix(cheat_input, ":rs"))
1137 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1140 else if (is_string_suffix(cheat_input, ":reload-music") ||
1141 is_string_suffix(cheat_input, ":rm"))
1143 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1146 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1147 is_string_suffix(cheat_input, ":ra"))
1149 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1150 1 << ARTWORK_TYPE_SOUNDS |
1151 1 << ARTWORK_TYPE_MUSIC);
1154 else if (is_string_suffix(cheat_input, ":dump-level") ||
1155 is_string_suffix(cheat_input, ":dl"))
1159 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1160 is_string_suffix(cheat_input, ":dt"))
1164 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1165 is_string_suffix(cheat_input, ":ft"))
1167 /* fix single-player tapes that contain player input for more than one
1168 player (due to a bug in 3.3.1.2 and earlier versions), which results
1169 in playing levels with more than one player in multi-player mode,
1170 even though the tape was originally recorded in single-player mode */
1172 /* remove player input actions for all players but the first one */
1173 for (i = 1; i < MAX_PLAYERS; i++)
1174 tape.player_participates[i] = FALSE;
1176 tape.changed = TRUE;
1178 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1179 is_string_suffix(cheat_input, ":snl"))
1181 SaveNativeLevel(&level);
1184 else if (game_status == GAME_MODE_PLAYING)
1187 if (is_string_suffix(cheat_input, ".q"))
1188 DEBUG_SetMaximumDynamite();
1191 else if (game_status == GAME_MODE_EDITOR)
1193 if (is_string_suffix(cheat_input, ":dump-brush") ||
1194 is_string_suffix(cheat_input, ":DB"))
1198 else if (is_string_suffix(cheat_input, ":DDB"))
1205 void HandleKeysDebug(Key key)
1210 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1212 boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1214 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1216 if (key == setup.debug.frame_delay_key[i] &&
1217 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1219 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1220 setup.debug.frame_delay[i] : GAME_FRAME_DELAY);
1222 if (!setup.debug.frame_delay_game_only)
1223 MenuFrameDelay = GameFrameDelay;
1225 SetVideoFrameDelay(GameFrameDelay);
1227 if (GameFrameDelay > ONE_SECOND_DELAY)
1228 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1229 else if (GameFrameDelay != 0)
1230 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1231 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1232 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1234 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1241 if (game_status == GAME_MODE_PLAYING)
1245 options.debug = !options.debug;
1247 Error(ERR_DEBUG, "debug mode %s",
1248 (options.debug ? "enabled" : "disabled"));
1250 else if (key == KSYM_v)
1252 Error(ERR_DEBUG, "currently using game engine version %d",
1253 game.engine_version);
1259 void HandleKey(Key key, int key_status)
1261 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1262 static boolean ignore_repeated_key = FALSE;
1263 static struct SetupKeyboardInfo ski;
1264 static struct SetupShortcutInfo ssi;
1273 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1274 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1275 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1276 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1277 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1278 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1283 if (game_status == GAME_MODE_PLAYING)
1285 /* only needed for single-step tape recording mode */
1286 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1287 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1288 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1289 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1292 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1294 byte key_action = 0;
1296 if (setup.input[pnr].use_joystick)
1299 ski = setup.input[pnr].key;
1301 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1302 if (key == *key_info[i].key_custom)
1303 key_action |= key_info[i].action;
1305 /* use combined snap+direction keys for the first player only */
1308 ssi = setup.shortcut;
1310 for (i = 0; i < NUM_DIRECTIONS; i++)
1311 if (key == *key_info[i].key_snap)
1312 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1315 /* clear delayed snap and drop actions in single step mode (see below) */
1316 if (tape.single_step)
1318 if (clear_snap_button[pnr])
1320 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1321 clear_snap_button[pnr] = FALSE;
1324 if (clear_drop_button[pnr])
1326 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1327 clear_drop_button[pnr] = FALSE;
1331 if (key_status == KEY_PRESSED)
1332 stored_player[pnr].action |= key_action;
1334 stored_player[pnr].action &= ~key_action;
1336 if (tape.single_step && tape.recording && tape.pausing)
1338 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1340 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1342 /* if snap key already pressed, don't snap when releasing (below) */
1343 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1344 element_snapped[pnr] = TRUE;
1346 /* if drop key already pressed, don't drop when releasing (below) */
1347 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1348 element_dropped[pnr] = TRUE;
1350 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1352 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1353 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1356 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1357 getRedDiskReleaseFlag_SP() == 0)
1358 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1360 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1363 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1365 if (key_action & KEY_BUTTON_SNAP)
1367 /* if snap key was released without moving (see above), snap now */
1368 if (!element_snapped[pnr])
1370 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1372 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1374 /* clear delayed snap button on next event */
1375 clear_snap_button[pnr] = TRUE;
1378 element_snapped[pnr] = FALSE;
1381 if (key_action & KEY_BUTTON_DROP &&
1382 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1384 /* if drop key was released without moving (see above), drop now */
1385 if (!element_dropped[pnr])
1387 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1389 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1390 getRedDiskReleaseFlag_SP() != 0)
1391 stored_player[pnr].action |= KEY_BUTTON_DROP;
1393 /* clear delayed drop button on next event */
1394 clear_drop_button[pnr] = TRUE;
1397 element_dropped[pnr] = FALSE;
1401 else if (tape.recording && tape.pausing)
1403 /* prevent key release events from un-pausing a paused game */
1404 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1405 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1411 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1412 if (key == key_info[i].key_default)
1413 joy |= key_info[i].action;
1418 if (key_status == KEY_PRESSED)
1419 key_joystick_mapping |= joy;
1421 key_joystick_mapping &= ~joy;
1426 if (game_status != GAME_MODE_PLAYING)
1427 key_joystick_mapping = 0;
1429 if (key_status == KEY_RELEASED)
1431 // reset flag to ignore repeated "key pressed" events after key release
1432 ignore_repeated_key = FALSE;
1437 if ((key == KSYM_F11 ||
1438 ((key == KSYM_Return ||
1439 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1440 video.fullscreen_available &&
1441 !ignore_repeated_key)
1443 setup.fullscreen = !setup.fullscreen;
1445 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1447 if (game_status == GAME_MODE_SETUP)
1448 RedrawSetupScreenAfterFullscreenToggle();
1450 // set flag to ignore repeated "key pressed" events
1451 ignore_repeated_key = TRUE;
1456 if ((key == KSYM_0 || key == KSYM_KP_0 ||
1457 key == KSYM_minus || key == KSYM_KP_Subtract ||
1458 key == KSYM_plus || key == KSYM_KP_Add ||
1459 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
1460 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1461 video.window_scaling_available &&
1462 !video.fullscreen_enabled)
1464 if (key == KSYM_0 || key == KSYM_KP_0)
1465 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1466 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1467 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1469 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1471 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1472 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1473 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1474 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1476 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1478 if (game_status == GAME_MODE_SETUP)
1479 RedrawSetupScreenAfterFullscreenToggle();
1484 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1485 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1492 if (game_status == GAME_MODE_MAIN &&
1493 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1495 StartGameActions(options.network, setup.autorecord, level.random_seed);
1500 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1502 if (key == setup.shortcut.save_game)
1504 else if (key == setup.shortcut.load_game)
1506 else if (key == setup.shortcut.toggle_pause)
1507 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1509 HandleTapeButtonKeys(key);
1510 HandleSoundButtonKeys(key);
1513 if (game_status == GAME_MODE_PLAYING && !network_playing)
1515 int centered_player_nr_next = -999;
1517 if (key == setup.shortcut.focus_player_all)
1518 centered_player_nr_next = -1;
1520 for (i = 0; i < MAX_PLAYERS; i++)
1521 if (key == setup.shortcut.focus_player[i])
1522 centered_player_nr_next = i;
1524 if (centered_player_nr_next != -999)
1526 game.centered_player_nr_next = centered_player_nr_next;
1527 game.set_centered_player = TRUE;
1531 tape.centered_player_nr_next = game.centered_player_nr_next;
1532 tape.set_centered_player = TRUE;
1537 HandleKeysSpecial(key);
1539 if (HandleGadgetsKeyInput(key))
1541 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1542 key = KSYM_UNDEFINED;
1545 switch (game_status)
1547 case GAME_MODE_PSEUDO_TYPENAME:
1548 HandleTypeName(0, key);
1551 case GAME_MODE_TITLE:
1552 case GAME_MODE_MAIN:
1553 case GAME_MODE_LEVELS:
1554 case GAME_MODE_LEVELNR:
1555 case GAME_MODE_SETUP:
1556 case GAME_MODE_INFO:
1557 case GAME_MODE_SCORES:
1562 if (game_status == GAME_MODE_TITLE)
1563 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1564 else if (game_status == GAME_MODE_MAIN)
1565 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1566 else if (game_status == GAME_MODE_LEVELS)
1567 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1568 else if (game_status == GAME_MODE_LEVELNR)
1569 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1570 else if (game_status == GAME_MODE_SETUP)
1571 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1572 else if (game_status == GAME_MODE_INFO)
1573 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1574 else if (game_status == GAME_MODE_SCORES)
1575 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1579 if (game_status != GAME_MODE_MAIN)
1580 FadeSkipNextFadeIn();
1582 if (game_status == GAME_MODE_TITLE)
1583 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1584 else if (game_status == GAME_MODE_LEVELS)
1585 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1586 else if (game_status == GAME_MODE_LEVELNR)
1587 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1588 else if (game_status == GAME_MODE_SETUP)
1589 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1590 else if (game_status == GAME_MODE_INFO)
1591 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1592 else if (game_status == GAME_MODE_SCORES)
1593 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1597 if (game_status == GAME_MODE_LEVELS)
1598 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1599 else if (game_status == GAME_MODE_LEVELNR)
1600 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1601 else if (game_status == GAME_MODE_SETUP)
1602 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1603 else if (game_status == GAME_MODE_INFO)
1604 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1605 else if (game_status == GAME_MODE_SCORES)
1606 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1609 case KSYM_Page_Down:
1610 if (game_status == GAME_MODE_LEVELS)
1611 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1612 else if (game_status == GAME_MODE_LEVELNR)
1613 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1614 else if (game_status == GAME_MODE_SETUP)
1615 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1616 else if (game_status == GAME_MODE_INFO)
1617 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1618 else if (game_status == GAME_MODE_SCORES)
1619 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1627 case GAME_MODE_EDITOR:
1628 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1629 HandleLevelEditorKeyInput(key);
1632 case GAME_MODE_PLAYING:
1637 RequestQuitGame(setup.ask_on_escape);
1647 if (key == KSYM_Escape)
1649 SetGameStatus(GAME_MODE_MAIN);
1657 HandleKeysDebug(key);
1660 void HandleNoEvent()
1662 // if (button_status && game_status != GAME_MODE_PLAYING)
1663 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1665 HandleButton(0, 0, button_status, -button_status);
1672 #if defined(NETWORK_AVALIABLE)
1673 if (options.network)
1677 switch (game_status)
1679 case GAME_MODE_MAIN:
1680 DrawPreviewLevelAnimation();
1683 case GAME_MODE_EDITOR:
1684 HandleLevelEditorIdle();
1692 static int HandleJoystickForAllPlayers()
1697 for (i = 0; i < MAX_PLAYERS; i++)
1699 byte joy_action = 0;
1702 if (!setup.input[i].use_joystick)
1706 joy_action = Joystick(i);
1707 result |= joy_action;
1709 if (!setup.input[i].use_joystick)
1712 stored_player[i].action = joy_action;
1718 void HandleJoystick()
1720 int joystick = HandleJoystickForAllPlayers();
1721 int keyboard = key_joystick_mapping;
1722 int joy = (joystick | keyboard);
1723 int left = joy & JOY_LEFT;
1724 int right = joy & JOY_RIGHT;
1725 int up = joy & JOY_UP;
1726 int down = joy & JOY_DOWN;
1727 int button = joy & JOY_BUTTON;
1728 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1729 int dx = (left ? -1 : right ? 1 : 0);
1730 int dy = (up ? -1 : down ? 1 : 0);
1732 switch (game_status)
1734 case GAME_MODE_TITLE:
1735 case GAME_MODE_MAIN:
1736 case GAME_MODE_LEVELS:
1737 case GAME_MODE_LEVELNR:
1738 case GAME_MODE_SETUP:
1739 case GAME_MODE_INFO:
1741 static unsigned int joystickmove_delay = 0;
1743 if (joystick && !button &&
1744 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1745 newbutton = dx = dy = 0;
1747 if (game_status == GAME_MODE_TITLE)
1748 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1749 else if (game_status == GAME_MODE_MAIN)
1750 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1751 else if (game_status == GAME_MODE_LEVELS)
1752 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1753 else if (game_status == GAME_MODE_LEVELNR)
1754 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1755 else if (game_status == GAME_MODE_SETUP)
1756 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1757 else if (game_status == GAME_MODE_INFO)
1758 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1762 case GAME_MODE_SCORES:
1763 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1766 case GAME_MODE_PLAYING:
1767 if (tape.playing || keyboard)
1768 newbutton = ((joy & JOY_BUTTON) != 0);
1770 if (newbutton && AllPlayersGone)