1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
27 #define DEBUG_EVENTS 0
30 static boolean cursor_inside_playfield = FALSE;
31 static boolean playfield_cursor_set = FALSE;
32 static unsigned int playfield_cursor_delay = 0;
35 /* event filter especially needed for SDL event filtering due to
36 delay problems with lots of mouse motion events when mouse button
37 not pressed (X11 can handle this with 'PointerMotionHintMask') */
39 /* event filter addition for SDL2: as SDL2 does not have a function to enable
40 or disable keyboard auto-repeat, filter repeated keyboard events instead */
42 static int FilterEventsExt(const Event *event)
46 #if defined(TARGET_SDL2)
47 /* skip repeated key press events if keyboard auto-repeat is disabled */
48 if (event->type == EVENT_KEYPRESS &&
54 /* non-motion events are directly passed to event handler functions */
55 if (event->type != EVENT_MOTIONNOTIFY)
58 motion = (MotionEvent *)event;
59 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
60 motion->y >= SY && motion->y < SY + SYSIZE);
62 if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
64 SetMouseCursor(CURSOR_DEFAULT);
65 playfield_cursor_set = FALSE;
66 DelayReached(&playfield_cursor_delay, 0);
69 /* skip mouse motion events without pressed button outside level editor */
70 if (button_status == MB_RELEASED &&
71 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
77 #if defined(TARGET_SDL2)
78 int FilterEvents(void *userdata, Event *event)
80 return FilterEventsExt(event);
83 int FilterEvents(const Event *event)
85 return FilterEventsExt(event);
89 /* to prevent delay problems, skip mouse motion events if the very next
90 event is also a mouse motion event (and therefore effectively only
91 handling the last of a row of mouse motion events in the event queue) */
93 boolean SkipPressedMouseMotionEvent(const Event *event)
95 /* nothing to do if the current event is not a mouse motion event */
96 if (event->type != EVENT_MOTIONNOTIFY)
99 /* only skip motion events with pressed button outside level editor */
100 if (button_status == MB_RELEASED ||
101 game_status == GAME_MODE_EDITOR || game_status == GAME_MODE_PLAYING)
108 PeekEvent(&next_event);
110 /* if next event is also a mouse motion event, skip the current one */
111 if (next_event.type == EVENT_MOTIONNOTIFY)
118 /* this is only really needed for non-SDL targets to filter unwanted events;
119 when using SDL with properly installed event filter, this function can be
120 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
122 static boolean NextValidEvent(Event *event)
124 while (PendingEvent())
126 boolean handle_this_event = FALSE;
130 if (FilterEventsExt(event))
131 handle_this_event = TRUE;
133 if (SkipPressedMouseMotionEvent(event))
134 handle_this_event = FALSE;
136 if (handle_this_event)
147 if (PendingEvent()) /* got event */
151 while (NextValidEvent(&event))
155 case EVENT_BUTTONPRESS:
156 case EVENT_BUTTONRELEASE:
157 HandleButtonEvent((ButtonEvent *) &event);
160 case EVENT_MOTIONNOTIFY:
161 HandleMotionEvent((MotionEvent *) &event);
164 #if defined(TARGET_SDL2)
165 case SDL_WINDOWEVENT:
166 HandleWindowEvent((WindowEvent *) &event);
169 case EVENT_FINGERPRESS:
170 case EVENT_FINGERRELEASE:
171 case EVENT_FINGERMOTION:
172 HandleFingerEvent((FingerEvent *) &event);
175 case EVENT_TEXTINPUT:
176 HandleTextEvent((TextEvent *) &event);
181 case EVENT_KEYRELEASE:
182 HandleKeyEvent((KeyEvent *) &event);
186 HandleOtherEvents(&event);
193 /* when playing, display a special mouse pointer inside the playfield */
194 if (game_status == GAME_MODE_PLAYING && !tape.pausing)
196 if (!playfield_cursor_set && cursor_inside_playfield &&
197 DelayReached(&playfield_cursor_delay, 1000))
199 SetMouseCursor(CURSOR_PLAYFIELD);
200 playfield_cursor_set = TRUE;
203 else if (playfield_cursor_set)
205 SetMouseCursor(CURSOR_DEFAULT);
206 playfield_cursor_set = FALSE;
218 /* don't use all CPU time when idle; the main loop while playing
219 has its own synchronization and is CPU friendly, too */
221 if (game_status == GAME_MODE_PLAYING)
228 if (!PendingEvent()) /* delay only if no pending events */
232 /* refresh window contents from drawing buffer, if needed */
235 if (game_status == GAME_MODE_QUIT)
240 void HandleOtherEvents(Event *event)
245 HandleExposeEvent((ExposeEvent *) event);
248 case EVENT_UNMAPNOTIFY:
250 /* This causes the game to stop not only when iconified, but also
251 when on another virtual desktop, which might be not desired. */
252 SleepWhileUnmapped();
258 HandleFocusEvent((FocusChangeEvent *) event);
261 case EVENT_CLIENTMESSAGE:
262 HandleClientMessageEvent((ClientMessageEvent *) event);
265 #if defined(TARGET_SDL)
266 case SDL_JOYAXISMOTION:
267 case SDL_JOYBUTTONDOWN:
268 case SDL_JOYBUTTONUP:
269 HandleJoystickEvent(event);
273 HandleWindowManagerEvent(event);
282 void ClearEventQueue()
284 while (PendingEvent())
292 case EVENT_BUTTONRELEASE:
293 button_status = MB_RELEASED;
296 case EVENT_KEYRELEASE:
300 key_joystick_mapping = 0;
305 HandleOtherEvents(&event);
311 void ClearPlayerAction()
315 /* simulate key release events for still pressed keys */
316 key_joystick_mapping = 0;
317 for (i = 0; i < MAX_PLAYERS; i++)
318 stored_player[i].action = 0;
321 void SleepWhileUnmapped()
323 boolean window_unmapped = TRUE;
325 KeyboardAutoRepeatOn();
327 while (window_unmapped)
335 case EVENT_BUTTONRELEASE:
336 button_status = MB_RELEASED;
339 case EVENT_KEYRELEASE:
340 key_joystick_mapping = 0;
343 case EVENT_MAPNOTIFY:
344 window_unmapped = FALSE;
347 case EVENT_UNMAPNOTIFY:
348 /* this is only to surely prevent the 'should not happen' case
349 * of recursively looping between 'SleepWhileUnmapped()' and
350 * 'HandleOtherEvents()' which usually calls this funtion.
355 HandleOtherEvents(&event);
360 if (game_status == GAME_MODE_PLAYING)
361 KeyboardAutoRepeatOffUnlessAutoplay();
364 void HandleExposeEvent(ExposeEvent *event)
366 #if !defined(TARGET_SDL)
370 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
376 void HandleButtonEvent(ButtonEvent *event)
379 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
381 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
385 motion_status = FALSE;
387 if (event->type == EVENT_BUTTONPRESS)
388 button_status = event->button;
390 button_status = MB_RELEASED;
392 HandleButton(event->x, event->y, button_status, event->button);
395 void HandleMotionEvent(MotionEvent *event)
397 if (!PointerInWindow(window))
398 return; /* window and pointer are on different screens */
400 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
403 motion_status = TRUE;
406 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
407 button_status, event->x, event->y);
410 HandleButton(event->x, event->y, button_status, button_status);
413 #if defined(TARGET_SDL2)
414 void HandleWindowEvent(WindowEvent *event)
417 int subtype = event->event;
420 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
421 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
422 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
423 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
424 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
425 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
426 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
427 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
428 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
429 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
430 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
431 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
432 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
433 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
436 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
437 event_name, event->data1, event->data2);
440 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
441 event->event == SDL_WINDOWEVENT_RESIZED ||
442 event->event == SDL_WINDOWEVENT_EXPOSED)
446 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
448 // if game started in fullscreen mode, window will also get fullscreen size
449 if (!video.fullscreen_enabled && video.fullscreen_initial)
451 SDLSetWindowScaling(setup.window_scaling_percent);
453 // only do this correction once
454 video.fullscreen_initial = FALSE;
459 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
462 int new_window_width = event->data1;
463 int new_window_height = event->data2;
465 // if window size has changed after resizing, calculate new scaling factor
466 if (new_window_width != video.window_width ||
467 new_window_height != video.window_height)
469 int new_xpercent = (100 * new_window_width / video.width);
470 int new_ypercent = (100 * new_window_height / video.height);
473 printf("::: RESIZED from %d, %d to %d, %d\n",
474 video.window_width, video.window_height,
475 new_window_width, new_window_height);
478 setup.window_scaling_percent = video.window_scaling_percent =
479 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
480 MAX_WINDOW_SCALING_PERCENT);
482 video.window_width = new_window_width;
483 video.window_height = new_window_height;
486 printf("::: setup.window_scaling_percent set to %d\n",
487 setup.window_scaling_percent);
490 if (game_status == GAME_MODE_SETUP)
491 RedrawSetupScreenAfterFullscreenToggle();
496 // prevent slightly wrong scaling factor due to rounding differences
497 float scaling_factor = (float)setup.window_scaling_percent / 100;
498 int old_xsize = (int)(scaling_factor * video.width);
499 int old_ysize = (int)(scaling_factor * video.height);
500 int new_xsize = event->data1;
501 int new_ysize = event->data2;
503 // window size is unchanged when going from fullscreen to window mode,
504 // but reverse calculation of scaling factor might result in a scaling
505 // factor that is slightly different due to rounding differences;
506 // therefore compare old/new window size and not old/new scaling factor
507 if (old_xsize != new_xsize ||
508 old_ysize != new_ysize)
510 int new_xpercent = (100 * new_xsize / video.width);
511 int new_ypercent = (100 * new_ysize / video.height);
513 setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
515 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
516 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
517 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
518 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
520 printf("::: setup.window_scaling_percent set to %d\n",
521 setup.window_scaling_percent);
529 #define NUM_TOUCH_FINGERS 3
534 SDL_FingerID finger_id;
537 } touch_info[NUM_TOUCH_FINGERS];
539 void HandleFingerEvent(FingerEvent *event)
541 static Key motion_key_x = KSYM_UNDEFINED;
542 static Key motion_key_y = KSYM_UNDEFINED;
543 static Key button_key = KSYM_UNDEFINED;
544 static float motion_x1, motion_y1;
545 static float button_x1, button_y1;
546 static SDL_FingerID motion_id = -1;
547 static SDL_FingerID button_id = -1;
548 int move_trigger_distance_percent = 2; // percent of touchpad width/height
549 int drop_trigger_distance_percent = 5; // percent of touchpad width/height
550 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
551 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
552 float event_x = event->x;
553 float event_y = event->y;
557 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
558 event->type == EVENT_FINGERPRESS ? "pressed" :
559 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
563 event->dx, event->dy,
568 if (game_status != GAME_MODE_PLAYING)
572 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
574 boolean use_virtual_button_control = FALSE;
576 if (use_virtual_button_control)
579 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
582 Key key = (event->x < 1.0 / 3.0 ?
583 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
584 setup.input[0].key.drop) :
585 event->x > 2.0 / 3.0 ?
586 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
587 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
588 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
589 setup.input[0].key.right) :
592 char *key_name = (key == setup.input[0].key.snap ? "SNAP" :
593 key == setup.input[0].key.drop ? "DROP" :
594 key == setup.input[0].key.up ? "UP" :
595 key == setup.input[0].key.down ? "DOWN" :
596 key == setup.input[0].key.left ? "LEFT" :
597 key == setup.input[0].key.right ? "RIGHT" : "(unknown)");
599 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
602 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
603 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
604 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
605 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
606 setup.input[0].key.snap);
610 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
611 getKeyNameFromKey(key), key_status_name, event->fingerId);
613 // check if we already know this touch event's finger id
614 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
616 if (touch_info[i].touched &&
617 touch_info[i].finger_id == event->fingerId)
619 // Error(ERR_DEBUG, "MARK 1: %d", i);
625 if (i >= NUM_TOUCH_FINGERS)
627 if (key_status == KEY_PRESSED)
629 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
631 // unknown finger id -- get new, empty slot, if available
632 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
634 if (touch_info[i].counter < oldest_counter)
637 oldest_counter = touch_info[i].counter;
639 // Error(ERR_DEBUG, "MARK 2: %d", i);
642 if (!touch_info[i].touched)
644 // Error(ERR_DEBUG, "MARK 3: %d", i);
650 if (i >= NUM_TOUCH_FINGERS)
652 // all slots allocated -- use oldest slot
655 // Error(ERR_DEBUG, "MARK 4: %d", i);
660 // release of previously unknown key (should not happen)
662 if (key != KSYM_UNDEFINED)
664 HandleKey(key, KEY_RELEASED);
666 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
667 getKeyNameFromKey(key), "KEY_RELEASED", i);
672 if (i < NUM_TOUCH_FINGERS)
674 if (key_status == KEY_PRESSED)
676 if (touch_info[i].key != key)
678 if (touch_info[i].key != KSYM_UNDEFINED)
680 HandleKey(touch_info[i].key, KEY_RELEASED);
682 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
683 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
686 if (key != KSYM_UNDEFINED)
688 HandleKey(key, KEY_PRESSED);
690 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
691 getKeyNameFromKey(key), "KEY_PRESSED", i);
695 touch_info[i].touched = TRUE;
696 touch_info[i].finger_id = event->fingerId;
697 touch_info[i].counter = Counter();
698 touch_info[i].key = key;
702 if (touch_info[i].key != KSYM_UNDEFINED)
704 HandleKey(touch_info[i].key, KEY_RELEASED);
706 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
707 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
710 touch_info[i].touched = FALSE;
711 touch_info[i].finger_id = 0;
712 touch_info[i].counter = 0;
713 touch_info[i].key = 0;
719 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d]",
720 key_name, key_status_name, i);
722 Error(ERR_DEBUG, "=> key == %d, key_status == %d [%d]", key, key_status, i);
729 // use touch direction control
731 if (event->type == EVENT_FINGERPRESS)
733 if (event_x > 1.0 / 3.0)
737 motion_id = event->fingerId;
742 motion_key_x = KSYM_UNDEFINED;
743 motion_key_y = KSYM_UNDEFINED;
745 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
751 button_id = event->fingerId;
756 button_key = setup.input[0].key.snap;
758 HandleKey(button_key, KEY_PRESSED);
760 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
763 else if (event->type == EVENT_FINGERRELEASE)
765 if (event->fingerId == motion_id)
769 if (motion_key_x != KSYM_UNDEFINED)
770 HandleKey(motion_key_x, KEY_RELEASED);
771 if (motion_key_y != KSYM_UNDEFINED)
772 HandleKey(motion_key_y, KEY_RELEASED);
774 motion_key_x = KSYM_UNDEFINED;
775 motion_key_y = KSYM_UNDEFINED;
777 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
779 else if (event->fingerId == button_id)
783 if (button_key != KSYM_UNDEFINED)
784 HandleKey(button_key, KEY_RELEASED);
786 button_key = KSYM_UNDEFINED;
788 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
791 else if (event->type == EVENT_FINGERMOTION)
793 if (event->fingerId == motion_id)
795 float distance_x = ABS(event_x - motion_x1);
796 float distance_y = ABS(event_y - motion_y1);
797 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
798 event_x > motion_x1 ? setup.input[0].key.right :
800 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
801 event_y > motion_y1 ? setup.input[0].key.down :
804 if (distance_x < move_trigger_distance / 2 ||
805 distance_x < distance_y)
806 new_motion_key_x = KSYM_UNDEFINED;
808 if (distance_y < move_trigger_distance / 2 ||
809 distance_y < distance_x)
810 new_motion_key_y = KSYM_UNDEFINED;
812 if (distance_x > move_trigger_distance ||
813 distance_y > move_trigger_distance)
815 if (new_motion_key_x != motion_key_x)
817 if (motion_key_x != KSYM_UNDEFINED)
818 HandleKey(motion_key_x, KEY_RELEASED);
819 if (new_motion_key_x != KSYM_UNDEFINED)
820 HandleKey(new_motion_key_x, KEY_PRESSED);
823 if (new_motion_key_y != motion_key_y)
825 if (motion_key_y != KSYM_UNDEFINED)
826 HandleKey(motion_key_y, KEY_RELEASED);
827 if (new_motion_key_y != KSYM_UNDEFINED)
828 HandleKey(new_motion_key_y, KEY_PRESSED);
834 motion_key_x = new_motion_key_x;
835 motion_key_y = new_motion_key_y;
837 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
840 else if (event->fingerId == button_id)
842 float distance_x = ABS(event_x - button_x1);
843 float distance_y = ABS(event_y - button_y1);
845 if (distance_x < drop_trigger_distance / 2 &&
846 distance_y > drop_trigger_distance)
848 if (button_key == setup.input[0].key.snap)
849 HandleKey(button_key, KEY_RELEASED);
854 button_key = setup.input[0].key.drop;
856 HandleKey(button_key, KEY_PRESSED);
858 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
866 void HandleFingerEvent(FingerEvent *event)
869 static int num_events = 0;
875 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
876 event->type == EVENT_FINGERPRESS ? "pressed" :
877 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
881 event->dx, event->dy,
887 int x = (int)(event->x * video.width);
888 int y = (int)(event->y * video.height);
889 int button = MB_LEFTBUTTON;
891 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
895 if (++num_events >= max_events)
901 if (event->type == EVENT_FINGERPRESS ||
902 event->type == EVENT_FINGERMOTION)
903 button_status = button;
905 button_status = MB_RELEASED;
907 int max_x = SX + SXSIZE;
908 int max_y = SY + SYSIZE;
912 if (game_status == GAME_MODE_PLAYING)
914 if (game_status == GAME_MODE_PLAYING &&
918 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
921 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
922 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
923 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
924 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
925 setup.input[0].key.drop);
927 Key key = (y < max_y / 3 ? setup.input[0].key.up :
928 y > 2 * max_y / 3 ? setup.input[0].key.down :
929 x < max_x / 3 ? setup.input[0].key.left :
930 x > 2 * max_x / 3 ? setup.input[0].key.right :
931 setup.input[0].key.drop);
934 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
936 HandleKey(key, key_status);
941 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
942 button_status, button);
944 HandleButton(x, y, button_status, button);
952 static boolean checkTextInputKeyModState()
954 // when playing, only handle raw key events and ignore text input
955 if (game_status == GAME_MODE_PLAYING)
958 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
961 void HandleTextEvent(TextEvent *event)
963 char *text = event->text;
964 Key key = getKeyFromKeyName(text);
967 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
970 text[0], (int)(text[0]),
972 getKeyNameFromKey(key),
976 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
978 if (game_status != GAME_MODE_PLAYING &&
979 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
981 if (checkTextInputKeyModState())
983 HandleKey(key, KEY_PRESSED);
984 HandleKey(key, KEY_RELEASED);
989 void HandleKeyEvent(KeyEvent *event)
991 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
992 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
993 Key key = GetEventKey(event, with_modifiers);
994 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
997 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
998 event->type == EVENT_KEYPRESS ? "pressed" : "released",
999 event->keysym.scancode,
1004 getKeyNameFromKey(key));
1008 if (key == KSYM_Menu)
1009 Error(ERR_DEBUG, "menu key pressed");
1010 else if (key == KSYM_Back)
1011 Error(ERR_DEBUG, "back key pressed");
1014 #if defined(PLATFORM_ANDROID)
1015 // always map the "back" button to the "escape" key on Android devices
1016 if (key == KSYM_Back)
1020 HandleKeyModState(keymod, key_status);
1022 #if defined(TARGET_SDL2)
1024 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
1026 if (game_status == GAME_MODE_PLAYING ||
1027 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
1029 if (!checkTextInputKeyModState())
1030 HandleKey(key, key_status);
1032 HandleKey(key, key_status);
1036 void HandleFocusEvent(FocusChangeEvent *event)
1038 static int old_joystick_status = -1;
1040 if (event->type == EVENT_FOCUSOUT)
1042 KeyboardAutoRepeatOn();
1043 old_joystick_status = joystick.status;
1044 joystick.status = JOYSTICK_NOT_AVAILABLE;
1046 ClearPlayerAction();
1048 else if (event->type == EVENT_FOCUSIN)
1050 /* When there are two Rocks'n'Diamonds windows which overlap and
1051 the player moves the pointer from one game window to the other,
1052 a 'FocusOut' event is generated for the window the pointer is
1053 leaving and a 'FocusIn' event is generated for the window the
1054 pointer is entering. In some cases, it can happen that the
1055 'FocusIn' event is handled by the one game process before the
1056 'FocusOut' event by the other game process. In this case the
1057 X11 environment would end up with activated keyboard auto repeat,
1058 because unfortunately this is a global setting and not (which
1059 would be far better) set for each X11 window individually.
1060 The effect would be keyboard auto repeat while playing the game
1061 (game_status == GAME_MODE_PLAYING), which is not desired.
1062 To avoid this special case, we just wait 1/10 second before
1063 processing the 'FocusIn' event.
1066 if (game_status == GAME_MODE_PLAYING)
1069 KeyboardAutoRepeatOffUnlessAutoplay();
1072 if (old_joystick_status != -1)
1073 joystick.status = old_joystick_status;
1077 void HandleClientMessageEvent(ClientMessageEvent *event)
1079 if (CheckCloseWindowEvent(event))
1083 void HandleWindowManagerEvent(Event *event)
1085 #if defined(TARGET_SDL)
1086 SDLHandleWindowManagerEvent(event);
1090 void HandleButton(int mx, int my, int button, int button_nr)
1092 static int old_mx = 0, old_my = 0;
1106 #if defined(PLATFORM_ANDROID)
1107 if (game_status != GAME_MODE_PLAYING &&
1108 HandleGadgets(mx, my, button))
1110 /* do not handle this button event anymore */
1111 mx = my = -32; /* force mouse event to be outside screen tiles */
1114 if (HandleGadgets(mx, my, button))
1116 /* do not handle this button event anymore */
1117 mx = my = -32; /* force mouse event to be outside screen tiles */
1121 /* do not use scroll wheel button events for anything other than gadgets */
1122 if (IS_WHEEL_BUTTON(button_nr))
1126 Error(ERR_DEBUG, "::: game_status == %d", game_status);
1129 switch (game_status)
1131 case GAME_MODE_TITLE:
1132 HandleTitleScreen(mx, my, 0, 0, button);
1135 case GAME_MODE_MAIN:
1136 HandleMainMenu(mx, my, 0, 0, button);
1139 case GAME_MODE_PSEUDO_TYPENAME:
1140 HandleTypeName(0, KSYM_Return);
1143 case GAME_MODE_LEVELS:
1144 HandleChooseLevelSet(mx, my, 0, 0, button);
1147 case GAME_MODE_LEVELNR:
1148 HandleChooseLevelNr(mx, my, 0, 0, button);
1151 case GAME_MODE_SCORES:
1152 HandleHallOfFame(0, 0, 0, 0, button);
1155 case GAME_MODE_EDITOR:
1156 HandleLevelEditorIdle();
1159 case GAME_MODE_INFO:
1160 HandleInfoScreen(mx, my, 0, 0, button);
1163 case GAME_MODE_SETUP:
1164 HandleSetupScreen(mx, my, 0, 0, button);
1167 case GAME_MODE_PLAYING:
1169 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1170 DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
1171 LEVELY((my - SY) / TILESIZE_VAR));
1172 // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1181 static boolean is_string_suffix(char *string, char *suffix)
1183 int string_len = strlen(string);
1184 int suffix_len = strlen(suffix);
1186 if (suffix_len > string_len)
1189 return (strEqual(&string[string_len - suffix_len], suffix));
1192 #define MAX_CHEAT_INPUT_LEN 32
1194 static void HandleKeysSpecial(Key key)
1196 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1197 char letter = getCharFromKey(key);
1198 int cheat_input_len = strlen(cheat_input);
1204 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1206 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1207 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1209 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1212 cheat_input[cheat_input_len++] = letter;
1213 cheat_input[cheat_input_len] = '\0';
1216 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1219 if (game_status == GAME_MODE_MAIN)
1221 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1222 is_string_suffix(cheat_input, ":ist"))
1224 InsertSolutionTape();
1226 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1227 is_string_suffix(cheat_input, ":rg"))
1229 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1232 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1233 is_string_suffix(cheat_input, ":rs"))
1235 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1238 else if (is_string_suffix(cheat_input, ":reload-music") ||
1239 is_string_suffix(cheat_input, ":rm"))
1241 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1244 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1245 is_string_suffix(cheat_input, ":ra"))
1247 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1248 1 << ARTWORK_TYPE_SOUNDS |
1249 1 << ARTWORK_TYPE_MUSIC);
1252 else if (is_string_suffix(cheat_input, ":dump-level") ||
1253 is_string_suffix(cheat_input, ":dl"))
1257 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1258 is_string_suffix(cheat_input, ":dt"))
1262 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1263 is_string_suffix(cheat_input, ":ft"))
1265 /* fix single-player tapes that contain player input for more than one
1266 player (due to a bug in 3.3.1.2 and earlier versions), which results
1267 in playing levels with more than one player in multi-player mode,
1268 even though the tape was originally recorded in single-player mode */
1270 /* remove player input actions for all players but the first one */
1271 for (i = 1; i < MAX_PLAYERS; i++)
1272 tape.player_participates[i] = FALSE;
1274 tape.changed = TRUE;
1276 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1277 is_string_suffix(cheat_input, ":snl"))
1279 SaveNativeLevel(&level);
1282 else if (game_status == GAME_MODE_PLAYING)
1285 if (is_string_suffix(cheat_input, ".q"))
1286 DEBUG_SetMaximumDynamite();
1289 else if (game_status == GAME_MODE_EDITOR)
1291 if (is_string_suffix(cheat_input, ":dump-brush") ||
1292 is_string_suffix(cheat_input, ":DB"))
1296 else if (is_string_suffix(cheat_input, ":DDB"))
1303 void HandleKey(Key key, int key_status)
1305 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1306 static struct SetupKeyboardInfo ski;
1307 static struct SetupShortcutInfo ssi;
1316 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1317 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1318 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1319 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1320 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1321 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1326 if (game_status == GAME_MODE_PLAYING)
1328 /* only needed for single-step tape recording mode */
1329 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1330 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1331 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1332 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1335 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1337 byte key_action = 0;
1339 if (setup.input[pnr].use_joystick)
1342 ski = setup.input[pnr].key;
1344 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1345 if (key == *key_info[i].key_custom)
1346 key_action |= key_info[i].action;
1348 /* use combined snap+direction keys for the first player only */
1351 ssi = setup.shortcut;
1353 for (i = 0; i < NUM_DIRECTIONS; i++)
1354 if (key == *key_info[i].key_snap)
1355 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1358 /* clear delayed snap and drop actions in single step mode (see below) */
1359 if (tape.single_step)
1361 if (clear_snap_button[pnr])
1363 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1364 clear_snap_button[pnr] = FALSE;
1367 if (clear_drop_button[pnr])
1369 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1370 clear_drop_button[pnr] = FALSE;
1374 if (key_status == KEY_PRESSED)
1375 stored_player[pnr].action |= key_action;
1377 stored_player[pnr].action &= ~key_action;
1379 if (tape.single_step && tape.recording && tape.pausing)
1381 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1383 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1385 /* if snap key already pressed, don't snap when releasing (below) */
1386 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1387 element_snapped[pnr] = TRUE;
1389 /* if drop key already pressed, don't drop when releasing (below) */
1390 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1391 element_dropped[pnr] = TRUE;
1394 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1396 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1397 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1400 printf("::: drop key pressed\n");
1403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1404 getRedDiskReleaseFlag_SP() == 0)
1405 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1407 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1411 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1413 if (key_action & KEY_BUTTON_SNAP)
1415 /* if snap key was released without moving (see above), snap now */
1416 if (!element_snapped[pnr])
1418 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1420 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1422 /* clear delayed snap button on next event */
1423 clear_snap_button[pnr] = TRUE;
1426 element_snapped[pnr] = FALSE;
1430 if (key_action & KEY_BUTTON_DROP &&
1431 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1433 /* if drop key was released without moving (see above), drop now */
1434 if (!element_dropped[pnr])
1436 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1438 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1439 getRedDiskReleaseFlag_SP() != 0)
1440 stored_player[pnr].action |= KEY_BUTTON_DROP;
1442 /* clear delayed drop button on next event */
1443 clear_drop_button[pnr] = TRUE;
1446 element_dropped[pnr] = FALSE;
1451 else if (tape.recording && tape.pausing)
1453 /* prevent key release events from un-pausing a paused game */
1454 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1455 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1461 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1462 if (key == key_info[i].key_default)
1463 joy |= key_info[i].action;
1468 if (key_status == KEY_PRESSED)
1469 key_joystick_mapping |= joy;
1471 key_joystick_mapping &= ~joy;
1476 if (game_status != GAME_MODE_PLAYING)
1477 key_joystick_mapping = 0;
1479 if (key_status == KEY_RELEASED)
1482 if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1483 (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1485 setup.fullscreen = !setup.fullscreen;
1488 printf("::: %d\n", setup.window_scaling_percent);
1491 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1493 if (game_status == GAME_MODE_SETUP)
1494 RedrawSetupScreenAfterFullscreenToggle();
1499 if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1500 (GetKeyModState() & KMOD_Alt) && video.window_scaling_available &&
1501 !video.fullscreen_enabled)
1504 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1506 setup.window_scaling_percent +=
1507 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1509 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1510 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1511 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1512 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1514 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1516 if (game_status == GAME_MODE_SETUP)
1517 RedrawSetupScreenAfterFullscreenToggle();
1523 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1524 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1526 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1527 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1535 if (game_status == GAME_MODE_MAIN &&
1536 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1538 StartGameActions(options.network, setup.autorecord, level.random_seed);
1543 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1545 if (key == setup.shortcut.save_game)
1547 else if (key == setup.shortcut.load_game)
1549 else if (key == setup.shortcut.toggle_pause)
1550 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1552 HandleTapeButtonKeys(key);
1553 HandleSoundButtonKeys(key);
1556 if (game_status == GAME_MODE_PLAYING && !network_playing)
1558 int centered_player_nr_next = -999;
1560 if (key == setup.shortcut.focus_player_all)
1561 centered_player_nr_next = -1;
1563 for (i = 0; i < MAX_PLAYERS; i++)
1564 if (key == setup.shortcut.focus_player[i])
1565 centered_player_nr_next = i;
1567 if (centered_player_nr_next != -999)
1569 game.centered_player_nr_next = centered_player_nr_next;
1570 game.set_centered_player = TRUE;
1574 tape.centered_player_nr_next = game.centered_player_nr_next;
1575 tape.set_centered_player = TRUE;
1580 HandleKeysSpecial(key);
1582 if (HandleGadgetsKeyInput(key))
1584 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1585 key = KSYM_UNDEFINED;
1588 switch (game_status)
1590 case GAME_MODE_PSEUDO_TYPENAME:
1591 HandleTypeName(0, key);
1594 case GAME_MODE_TITLE:
1595 case GAME_MODE_MAIN:
1596 case GAME_MODE_LEVELS:
1597 case GAME_MODE_LEVELNR:
1598 case GAME_MODE_SETUP:
1599 case GAME_MODE_INFO:
1600 case GAME_MODE_SCORES:
1605 if (game_status == GAME_MODE_TITLE)
1606 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1607 else if (game_status == GAME_MODE_MAIN)
1608 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1609 else if (game_status == GAME_MODE_LEVELS)
1610 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1611 else if (game_status == GAME_MODE_LEVELNR)
1612 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1613 else if (game_status == GAME_MODE_SETUP)
1614 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1615 else if (game_status == GAME_MODE_INFO)
1616 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1617 else if (game_status == GAME_MODE_SCORES)
1618 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1622 if (game_status != GAME_MODE_MAIN)
1623 FadeSkipNextFadeIn();
1625 if (game_status == GAME_MODE_TITLE)
1626 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1627 else if (game_status == GAME_MODE_LEVELS)
1628 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1629 else if (game_status == GAME_MODE_LEVELNR)
1630 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1631 else if (game_status == GAME_MODE_SETUP)
1632 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1633 else if (game_status == GAME_MODE_INFO)
1634 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1635 else if (game_status == GAME_MODE_SCORES)
1636 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1640 if (game_status == GAME_MODE_LEVELS)
1641 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1642 else if (game_status == GAME_MODE_LEVELNR)
1643 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1644 else if (game_status == GAME_MODE_SETUP)
1645 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1646 else if (game_status == GAME_MODE_INFO)
1647 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1648 else if (game_status == GAME_MODE_SCORES)
1649 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1652 case KSYM_Page_Down:
1653 if (game_status == GAME_MODE_LEVELS)
1654 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1655 else if (game_status == GAME_MODE_LEVELNR)
1656 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1657 else if (game_status == GAME_MODE_SETUP)
1658 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1659 else if (game_status == GAME_MODE_INFO)
1660 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1661 else if (game_status == GAME_MODE_SCORES)
1662 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1667 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1671 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1672 printf("Supaplex border elements %s\n",
1673 setup.sp_show_border_elements ? "enabled" : "disabled");
1682 case GAME_MODE_EDITOR:
1683 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1684 HandleLevelEditorKeyInput(key);
1687 case GAME_MODE_PLAYING:
1692 RequestQuitGame(setup.ask_on_escape);
1710 if (GameFrameDelay == 500)
1711 GameFrameDelay = GAME_FRAME_DELAY;
1713 GameFrameDelay = 500;
1716 GameFrameDelay = (key - KSYM_0) * 10;
1717 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1718 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1724 options.debug = FALSE;
1725 printf("debug mode disabled\n");
1729 options.debug = TRUE;
1730 printf("debug mode enabled\n");
1736 if (!global.fps_slowdown)
1738 global.fps_slowdown = TRUE;
1739 global.fps_slowdown_factor = 2;
1740 printf("fps slowdown enabled -- display only every 2nd frame\n");
1742 else if (global.fps_slowdown_factor == 2)
1744 global.fps_slowdown_factor = 4;
1745 printf("fps slowdown enabled -- display only every 4th frame\n");
1749 global.fps_slowdown = FALSE;
1750 global.fps_slowdown_factor = 1;
1751 printf("fps slowdown disabled\n");
1758 ScrollStepSize = TILEX / 8;
1759 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1763 ScrollStepSize = TILEX / 4;
1764 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1768 ScrollStepSize = TILEX / 2;
1769 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1773 ScrollStepSize = TILEX;
1774 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1779 printf("::: currently using game engine version %d\n",
1780 game.engine_version);
1791 if (key == KSYM_Escape)
1793 game_status = GAME_MODE_MAIN;
1801 void HandleNoEvent()
1803 if (button_status && game_status != GAME_MODE_PLAYING)
1805 HandleButton(0, 0, -button_status, button_status);
1816 #if defined(NETWORK_AVALIABLE)
1817 if (options.network)
1821 switch (game_status)
1823 case GAME_MODE_MAIN:
1824 DrawPreviewLevelAnimation();
1828 case GAME_MODE_LEVELS:
1829 case GAME_MODE_LEVELNR:
1830 case GAME_MODE_SETUP:
1831 case GAME_MODE_INFO:
1832 case GAME_MODE_SCORES:
1836 case GAME_MODE_EDITOR:
1837 HandleLevelEditorIdle();
1845 static int HandleJoystickForAllPlayers()
1850 for (i = 0; i < MAX_PLAYERS; i++)
1852 byte joy_action = 0;
1855 if (!setup.input[i].use_joystick)
1859 joy_action = Joystick(i);
1860 result |= joy_action;
1862 if (!setup.input[i].use_joystick)
1865 stored_player[i].action = joy_action;
1871 void HandleJoystick()
1873 int joystick = HandleJoystickForAllPlayers();
1874 int keyboard = key_joystick_mapping;
1875 int joy = (joystick | keyboard);
1876 int left = joy & JOY_LEFT;
1877 int right = joy & JOY_RIGHT;
1878 int up = joy & JOY_UP;
1879 int down = joy & JOY_DOWN;
1880 int button = joy & JOY_BUTTON;
1881 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1882 int dx = (left ? -1 : right ? 1 : 0);
1883 int dy = (up ? -1 : down ? 1 : 0);
1885 switch (game_status)
1887 case GAME_MODE_TITLE:
1888 case GAME_MODE_MAIN:
1889 case GAME_MODE_LEVELS:
1890 case GAME_MODE_LEVELNR:
1891 case GAME_MODE_SETUP:
1892 case GAME_MODE_INFO:
1894 static unsigned int joystickmove_delay = 0;
1896 if (joystick && !button &&
1897 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1898 newbutton = dx = dy = 0;
1900 if (game_status == GAME_MODE_TITLE)
1901 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1902 else if (game_status == GAME_MODE_MAIN)
1903 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1904 else if (game_status == GAME_MODE_LEVELS)
1905 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1906 else if (game_status == GAME_MODE_LEVELNR)
1907 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1908 else if (game_status == GAME_MODE_SETUP)
1909 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1910 else if (game_status == GAME_MODE_INFO)
1911 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1915 case GAME_MODE_SCORES:
1916 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1920 case GAME_MODE_EDITOR:
1921 HandleLevelEditorIdle();
1925 case GAME_MODE_PLAYING:
1926 if (tape.playing || keyboard)
1927 newbutton = ((joy & JOY_BUTTON) != 0);
1930 if (newbutton && local_player->LevelSolved_GameEnd)
1932 if (newbutton && AllPlayersGone)