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 #if defined(HAS_SCREEN_KEYBOARD)
416 if (video.shifted_up)
417 event->y += video.shifted_up_pos;
420 motion_status = FALSE;
422 if (event->type == EVENT_BUTTONPRESS)
423 button_status = event->button;
425 button_status = MB_RELEASED;
427 HandleButton(event->x, event->y, button_status, event->button);
430 void HandleMotionEvent(MotionEvent *event)
432 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
435 motion_status = TRUE;
437 #if DEBUG_EVENTS_MOTION
438 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
439 button_status, event->x, event->y);
442 HandleButton(event->x, event->y, button_status, button_status);
445 #if defined(TARGET_SDL2)
447 void HandleWheelEvent(WheelEvent *event)
451 #if DEBUG_EVENTS_WHEEL
453 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
454 event->which, event->x, event->y);
456 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
457 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
458 event->which, event->x, event->y,
459 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
460 "SDL_MOUSEWHEEL_FLIPPED"));
464 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
465 event->x > 0 ? MB_WHEEL_RIGHT :
466 event->y < 0 ? MB_WHEEL_DOWN :
467 event->y > 0 ? MB_WHEEL_UP : 0);
469 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
470 // accelerated mouse wheel available on Mac and Windows
471 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
473 // no accelerated mouse wheel available on Unix/Linux
474 wheel_steps = DEFAULT_WHEEL_STEPS;
477 motion_status = FALSE;
479 button_status = button_nr;
480 HandleButton(0, 0, button_status, -button_nr);
482 button_status = MB_RELEASED;
483 HandleButton(0, 0, button_status, -button_nr);
486 void HandleWindowEvent(WindowEvent *event)
488 #if DEBUG_EVENTS_WINDOW
489 int subtype = event->event;
492 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
493 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
494 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
495 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
496 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
497 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
498 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
499 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
500 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
501 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
502 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
503 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
504 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
505 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
508 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
509 event_name, event->data1, event->data2);
513 // (not needed, as the screen gets redrawn every 20 ms anyway)
514 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
515 event->event == SDL_WINDOWEVENT_RESIZED ||
516 event->event == SDL_WINDOWEVENT_EXPOSED)
520 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
522 int new_window_width = event->data1;
523 int new_window_height = event->data2;
525 // if window size has changed after resizing, calculate new scaling factor
526 if (new_window_width != video.window_width ||
527 new_window_height != video.window_height)
529 int new_xpercent = (100 * new_window_width / video.width);
530 int new_ypercent = (100 * new_window_height / video.height);
532 // (extreme window scaling allowed, but cannot be saved permanently)
533 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
534 setup.window_scaling_percent =
535 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
536 MAX_WINDOW_SCALING_PERCENT);
538 video.window_width = new_window_width;
539 video.window_height = new_window_height;
541 if (game_status == GAME_MODE_SETUP)
542 RedrawSetupScreenAfterFullscreenToggle();
549 #define NUM_TOUCH_FINGERS 3
554 SDL_FingerID finger_id;
557 } touch_info[NUM_TOUCH_FINGERS];
559 void HandleFingerEvent(FingerEvent *event)
561 static Key motion_key_x = KSYM_UNDEFINED;
562 static Key motion_key_y = KSYM_UNDEFINED;
563 static Key button_key = KSYM_UNDEFINED;
564 static float motion_x1, motion_y1;
565 static float button_x1, button_y1;
566 static SDL_FingerID motion_id = -1;
567 static SDL_FingerID button_id = -1;
568 int move_trigger_distance_percent = 2; // percent of touchpad width/height
569 int drop_trigger_distance_percent = 5; // percent of touchpad width/height
570 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
571 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
572 float event_x = event->x;
573 float event_y = event->y;
575 #if DEBUG_EVENTS_FINGER
576 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
577 event->type == EVENT_FINGERPRESS ? "pressed" :
578 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
582 event->dx, event->dy,
586 if (game_status != GAME_MODE_PLAYING)
589 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
591 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
593 Key key = (event->x < 1.0 / 3.0 ?
594 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
595 setup.input[0].key.drop) :
596 event->x > 2.0 / 3.0 ?
597 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
598 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
599 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
600 setup.input[0].key.right) :
602 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
606 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
607 getKeyNameFromKey(key), key_status_name, event->fingerId);
609 // check if we already know this touch event's finger id
610 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
612 if (touch_info[i].touched &&
613 touch_info[i].finger_id == event->fingerId)
615 // Error(ERR_DEBUG, "MARK 1: %d", i);
621 if (i >= NUM_TOUCH_FINGERS)
623 if (key_status == KEY_PRESSED)
625 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
627 // unknown finger id -- get new, empty slot, if available
628 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
630 if (touch_info[i].counter < oldest_counter)
633 oldest_counter = touch_info[i].counter;
635 // Error(ERR_DEBUG, "MARK 2: %d", i);
638 if (!touch_info[i].touched)
640 // Error(ERR_DEBUG, "MARK 3: %d", i);
646 if (i >= NUM_TOUCH_FINGERS)
648 // all slots allocated -- use oldest slot
651 // Error(ERR_DEBUG, "MARK 4: %d", i);
656 // release of previously unknown key (should not happen)
658 if (key != KSYM_UNDEFINED)
660 HandleKey(key, KEY_RELEASED);
662 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
663 getKeyNameFromKey(key), "KEY_RELEASED", i);
668 if (i < NUM_TOUCH_FINGERS)
670 if (key_status == KEY_PRESSED)
672 if (touch_info[i].key != key)
674 if (touch_info[i].key != KSYM_UNDEFINED)
676 HandleKey(touch_info[i].key, KEY_RELEASED);
678 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
679 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
682 if (key != KSYM_UNDEFINED)
684 HandleKey(key, KEY_PRESSED);
686 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
687 getKeyNameFromKey(key), "KEY_PRESSED", i);
691 touch_info[i].touched = TRUE;
692 touch_info[i].finger_id = event->fingerId;
693 touch_info[i].counter = Counter();
694 touch_info[i].key = key;
698 if (touch_info[i].key != KSYM_UNDEFINED)
700 HandleKey(touch_info[i].key, KEY_RELEASED);
702 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
703 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
706 touch_info[i].touched = FALSE;
707 touch_info[i].finger_id = 0;
708 touch_info[i].counter = 0;
709 touch_info[i].key = 0;
716 // use touch direction control
718 if (event->type == EVENT_FINGERPRESS)
720 if (event_x > 1.0 / 3.0)
724 motion_id = event->fingerId;
729 motion_key_x = KSYM_UNDEFINED;
730 motion_key_y = KSYM_UNDEFINED;
732 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
738 button_id = event->fingerId;
743 button_key = setup.input[0].key.snap;
745 HandleKey(button_key, KEY_PRESSED);
747 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
750 else if (event->type == EVENT_FINGERRELEASE)
752 if (event->fingerId == motion_id)
756 if (motion_key_x != KSYM_UNDEFINED)
757 HandleKey(motion_key_x, KEY_RELEASED);
758 if (motion_key_y != KSYM_UNDEFINED)
759 HandleKey(motion_key_y, KEY_RELEASED);
761 motion_key_x = KSYM_UNDEFINED;
762 motion_key_y = KSYM_UNDEFINED;
764 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
766 else if (event->fingerId == button_id)
770 if (button_key != KSYM_UNDEFINED)
771 HandleKey(button_key, KEY_RELEASED);
773 button_key = KSYM_UNDEFINED;
775 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
778 else if (event->type == EVENT_FINGERMOTION)
780 if (event->fingerId == motion_id)
782 float distance_x = ABS(event_x - motion_x1);
783 float distance_y = ABS(event_y - motion_y1);
784 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
785 event_x > motion_x1 ? setup.input[0].key.right :
787 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
788 event_y > motion_y1 ? setup.input[0].key.down :
791 if (distance_x < move_trigger_distance / 2 ||
792 distance_x < distance_y)
793 new_motion_key_x = KSYM_UNDEFINED;
795 if (distance_y < move_trigger_distance / 2 ||
796 distance_y < distance_x)
797 new_motion_key_y = KSYM_UNDEFINED;
799 if (distance_x > move_trigger_distance ||
800 distance_y > move_trigger_distance)
802 if (new_motion_key_x != motion_key_x)
804 if (motion_key_x != KSYM_UNDEFINED)
805 HandleKey(motion_key_x, KEY_RELEASED);
806 if (new_motion_key_x != KSYM_UNDEFINED)
807 HandleKey(new_motion_key_x, KEY_PRESSED);
810 if (new_motion_key_y != motion_key_y)
812 if (motion_key_y != KSYM_UNDEFINED)
813 HandleKey(motion_key_y, KEY_RELEASED);
814 if (new_motion_key_y != KSYM_UNDEFINED)
815 HandleKey(new_motion_key_y, KEY_PRESSED);
821 motion_key_x = new_motion_key_x;
822 motion_key_y = new_motion_key_y;
824 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
827 else if (event->fingerId == button_id)
829 float distance_x = ABS(event_x - button_x1);
830 float distance_y = ABS(event_y - button_y1);
832 if (distance_x < drop_trigger_distance / 2 &&
833 distance_y > drop_trigger_distance)
835 if (button_key == setup.input[0].key.snap)
836 HandleKey(button_key, KEY_RELEASED);
841 button_key = setup.input[0].key.drop;
843 HandleKey(button_key, KEY_PRESSED);
845 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
851 static boolean checkTextInputKeyModState()
853 // when playing, only handle raw key events and ignore text input
854 if (game_status == GAME_MODE_PLAYING)
857 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
860 void HandleTextEvent(TextEvent *event)
862 char *text = event->text;
863 Key key = getKeyFromKeyName(text);
865 #if DEBUG_EVENTS_TEXT
866 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
869 text[0], (int)(text[0]),
871 getKeyNameFromKey(key),
875 #if !defined(HAS_SCREEN_KEYBOARD)
876 // non-mobile devices: only handle key input with modifier keys pressed here
877 // (every other key input is handled directly as physical key input event)
878 if (!checkTextInputKeyModState())
882 // process text input as "classic" (with uppercase etc.) key input event
883 HandleKey(key, KEY_PRESSED);
884 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)