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 HandleKey(Key key, int key_status)
1207 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1208 static boolean ignore_repeated_key = FALSE;
1209 static struct SetupKeyboardInfo ski;
1210 static struct SetupShortcutInfo ssi;
1219 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1220 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1221 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1222 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1223 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1224 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1229 if (game_status == GAME_MODE_PLAYING)
1231 /* only needed for single-step tape recording mode */
1232 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1233 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1234 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1235 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1238 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1240 byte key_action = 0;
1242 if (setup.input[pnr].use_joystick)
1245 ski = setup.input[pnr].key;
1247 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1248 if (key == *key_info[i].key_custom)
1249 key_action |= key_info[i].action;
1251 /* use combined snap+direction keys for the first player only */
1254 ssi = setup.shortcut;
1256 for (i = 0; i < NUM_DIRECTIONS; i++)
1257 if (key == *key_info[i].key_snap)
1258 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1261 /* clear delayed snap and drop actions in single step mode (see below) */
1262 if (tape.single_step)
1264 if (clear_snap_button[pnr])
1266 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1267 clear_snap_button[pnr] = FALSE;
1270 if (clear_drop_button[pnr])
1272 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1273 clear_drop_button[pnr] = FALSE;
1277 if (key_status == KEY_PRESSED)
1278 stored_player[pnr].action |= key_action;
1280 stored_player[pnr].action &= ~key_action;
1282 if (tape.single_step && tape.recording && tape.pausing)
1284 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1286 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1288 /* if snap key already pressed, don't snap when releasing (below) */
1289 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1290 element_snapped[pnr] = TRUE;
1292 /* if drop key already pressed, don't drop when releasing (below) */
1293 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1294 element_dropped[pnr] = TRUE;
1296 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1298 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1299 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1302 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1303 getRedDiskReleaseFlag_SP() == 0)
1304 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1306 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1309 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1311 if (key_action & KEY_BUTTON_SNAP)
1313 /* if snap key was released without moving (see above), snap now */
1314 if (!element_snapped[pnr])
1316 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1318 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1320 /* clear delayed snap button on next event */
1321 clear_snap_button[pnr] = TRUE;
1324 element_snapped[pnr] = FALSE;
1327 if (key_action & KEY_BUTTON_DROP &&
1328 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1330 /* if drop key was released without moving (see above), drop now */
1331 if (!element_dropped[pnr])
1333 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1335 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1336 getRedDiskReleaseFlag_SP() != 0)
1337 stored_player[pnr].action |= KEY_BUTTON_DROP;
1339 /* clear delayed drop button on next event */
1340 clear_drop_button[pnr] = TRUE;
1343 element_dropped[pnr] = FALSE;
1347 else if (tape.recording && tape.pausing)
1349 /* prevent key release events from un-pausing a paused game */
1350 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1351 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1357 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1358 if (key == key_info[i].key_default)
1359 joy |= key_info[i].action;
1364 if (key_status == KEY_PRESSED)
1365 key_joystick_mapping |= joy;
1367 key_joystick_mapping &= ~joy;
1372 if (game_status != GAME_MODE_PLAYING)
1373 key_joystick_mapping = 0;
1375 if (key_status == KEY_RELEASED)
1377 // reset flag to ignore repeated "key pressed" events after key release
1378 ignore_repeated_key = FALSE;
1383 if ((key == KSYM_F11 ||
1384 ((key == KSYM_Return ||
1385 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1386 video.fullscreen_available &&
1387 !ignore_repeated_key)
1389 setup.fullscreen = !setup.fullscreen;
1391 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1393 if (game_status == GAME_MODE_SETUP)
1394 RedrawSetupScreenAfterFullscreenToggle();
1396 // set flag to ignore repeated "key pressed" events
1397 ignore_repeated_key = TRUE;
1402 if ((key == KSYM_minus ||
1404 key == KSYM_equal || // ("Shift-=" is "+" on US keyboards)
1406 ((GetKeyModState() & KMOD_Control) ||
1407 (GetKeyModState() & KMOD_Alt) ||
1408 (GetKeyModState() & KMOD_Meta)) &&
1409 video.window_scaling_available &&
1410 !video.fullscreen_enabled)
1413 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1415 setup.window_scaling_percent +=
1416 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1418 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1419 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1420 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1421 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1423 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1425 if (game_status == GAME_MODE_SETUP)
1426 RedrawSetupScreenAfterFullscreenToggle();
1431 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1432 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1439 if (game_status == GAME_MODE_MAIN &&
1440 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1442 StartGameActions(options.network, setup.autorecord, level.random_seed);
1447 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1449 if (key == setup.shortcut.save_game)
1451 else if (key == setup.shortcut.load_game)
1453 else if (key == setup.shortcut.toggle_pause)
1454 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1456 HandleTapeButtonKeys(key);
1457 HandleSoundButtonKeys(key);
1460 if (game_status == GAME_MODE_PLAYING && !network_playing)
1462 int centered_player_nr_next = -999;
1464 if (key == setup.shortcut.focus_player_all)
1465 centered_player_nr_next = -1;
1467 for (i = 0; i < MAX_PLAYERS; i++)
1468 if (key == setup.shortcut.focus_player[i])
1469 centered_player_nr_next = i;
1471 if (centered_player_nr_next != -999)
1473 game.centered_player_nr_next = centered_player_nr_next;
1474 game.set_centered_player = TRUE;
1478 tape.centered_player_nr_next = game.centered_player_nr_next;
1479 tape.set_centered_player = TRUE;
1484 HandleKeysSpecial(key);
1486 if (HandleGadgetsKeyInput(key))
1488 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1489 key = KSYM_UNDEFINED;
1492 switch (game_status)
1494 case GAME_MODE_PSEUDO_TYPENAME:
1495 HandleTypeName(0, key);
1498 case GAME_MODE_TITLE:
1499 case GAME_MODE_MAIN:
1500 case GAME_MODE_LEVELS:
1501 case GAME_MODE_LEVELNR:
1502 case GAME_MODE_SETUP:
1503 case GAME_MODE_INFO:
1504 case GAME_MODE_SCORES:
1509 if (game_status == GAME_MODE_TITLE)
1510 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1511 else if (game_status == GAME_MODE_MAIN)
1512 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1513 else if (game_status == GAME_MODE_LEVELS)
1514 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1515 else if (game_status == GAME_MODE_LEVELNR)
1516 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1517 else if (game_status == GAME_MODE_SETUP)
1518 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1519 else if (game_status == GAME_MODE_INFO)
1520 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1521 else if (game_status == GAME_MODE_SCORES)
1522 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1526 if (game_status != GAME_MODE_MAIN)
1527 FadeSkipNextFadeIn();
1529 if (game_status == GAME_MODE_TITLE)
1530 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1531 else if (game_status == GAME_MODE_LEVELS)
1532 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1533 else if (game_status == GAME_MODE_LEVELNR)
1534 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1535 else if (game_status == GAME_MODE_SETUP)
1536 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1537 else if (game_status == GAME_MODE_INFO)
1538 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1539 else if (game_status == GAME_MODE_SCORES)
1540 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1544 if (game_status == GAME_MODE_LEVELS)
1545 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1546 else if (game_status == GAME_MODE_LEVELNR)
1547 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1548 else if (game_status == GAME_MODE_SETUP)
1549 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1550 else if (game_status == GAME_MODE_INFO)
1551 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1552 else if (game_status == GAME_MODE_SCORES)
1553 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1556 case KSYM_Page_Down:
1557 if (game_status == GAME_MODE_LEVELS)
1558 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1559 else if (game_status == GAME_MODE_LEVELNR)
1560 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1561 else if (game_status == GAME_MODE_SETUP)
1562 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1563 else if (game_status == GAME_MODE_INFO)
1564 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1565 else if (game_status == GAME_MODE_SCORES)
1566 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1574 case GAME_MODE_EDITOR:
1575 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1576 HandleLevelEditorKeyInput(key);
1579 case GAME_MODE_PLAYING:
1584 RequestQuitGame(setup.ask_on_escape);
1591 options.debug = FALSE;
1592 printf("debug mode disabled\n");
1596 options.debug = TRUE;
1597 printf("debug mode enabled\n");
1602 printf("::: currently using game engine version %d\n",
1603 game.engine_version);
1614 if (key == KSYM_Escape)
1616 SetGameStatus(GAME_MODE_MAIN);
1625 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1627 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Control) ||
1628 (GetKeyModState() & KMOD_Alt) ||
1629 (GetKeyModState() & KMOD_Meta));
1631 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1633 if (key == setup.debug.frame_delay_key[i] &&
1634 (mod_key_pressed || !setup.debug.frame_delay_use_mod_key))
1636 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1637 setup.debug.frame_delay[i] : GAME_FRAME_DELAY);
1639 if (!setup.debug.frame_delay_game_only)
1640 MenuFrameDelay = GameFrameDelay;
1642 SetVideoFrameDelay(GameFrameDelay);
1644 if (GameFrameDelay > ONE_SECOND_DELAY)
1645 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1646 else if (GameFrameDelay != 0)
1647 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1648 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1649 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1651 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
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)