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)
367 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
372 void HandleButtonEvent(ButtonEvent *event)
375 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
377 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
381 motion_status = FALSE;
383 if (event->type == EVENT_BUTTONPRESS)
384 button_status = event->button;
386 button_status = MB_RELEASED;
388 HandleButton(event->x, event->y, button_status, event->button);
391 void HandleMotionEvent(MotionEvent *event)
393 if (!PointerInWindow(window))
394 return; /* window and pointer are on different screens */
396 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
399 motion_status = TRUE;
402 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
403 button_status, event->x, event->y);
406 HandleButton(event->x, event->y, button_status, button_status);
409 #if defined(TARGET_SDL2)
410 void HandleWindowEvent(WindowEvent *event)
413 int subtype = event->event;
416 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
417 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
418 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
419 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
420 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
421 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
422 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
423 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
424 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
425 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
426 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
427 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
428 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
429 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
432 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
433 event_name, event->data1, event->data2);
436 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
437 event->event == SDL_WINDOWEVENT_RESIZED ||
438 event->event == SDL_WINDOWEVENT_EXPOSED)
442 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
444 // if game started in fullscreen mode, window will also get fullscreen size
445 if (!video.fullscreen_enabled && video.fullscreen_initial)
447 SDLSetWindowScaling(setup.window_scaling_percent);
449 // only do this correction once
450 video.fullscreen_initial = FALSE;
455 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
458 int new_window_width = event->data1;
459 int new_window_height = event->data2;
461 // if window size has changed after resizing, calculate new scaling factor
462 if (new_window_width != video.window_width ||
463 new_window_height != video.window_height)
465 int new_xpercent = (100 * new_window_width / video.width);
466 int new_ypercent = (100 * new_window_height / video.height);
469 printf("::: RESIZED from %d, %d to %d, %d\n",
470 video.window_width, video.window_height,
471 new_window_width, new_window_height);
474 setup.window_scaling_percent = video.window_scaling_percent =
475 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
476 MAX_WINDOW_SCALING_PERCENT);
478 video.window_width = new_window_width;
479 video.window_height = new_window_height;
482 printf("::: setup.window_scaling_percent set to %d\n",
483 setup.window_scaling_percent);
486 if (game_status == GAME_MODE_SETUP)
487 RedrawSetupScreenAfterFullscreenToggle();
492 // prevent slightly wrong scaling factor due to rounding differences
493 float scaling_factor = (float)setup.window_scaling_percent / 100;
494 int old_xsize = (int)(scaling_factor * video.width);
495 int old_ysize = (int)(scaling_factor * video.height);
496 int new_xsize = event->data1;
497 int new_ysize = event->data2;
499 // window size is unchanged when going from fullscreen to window mode,
500 // but reverse calculation of scaling factor might result in a scaling
501 // factor that is slightly different due to rounding differences;
502 // therefore compare old/new window size and not old/new scaling factor
503 if (old_xsize != new_xsize ||
504 old_ysize != new_ysize)
506 int new_xpercent = (100 * new_xsize / video.width);
507 int new_ypercent = (100 * new_ysize / video.height);
509 setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
511 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
512 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
513 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
514 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
516 printf("::: setup.window_scaling_percent set to %d\n",
517 setup.window_scaling_percent);
525 #define NUM_TOUCH_FINGERS 3
530 SDL_FingerID finger_id;
533 } touch_info[NUM_TOUCH_FINGERS];
535 void HandleFingerEvent(FingerEvent *event)
537 static Key motion_key_x = KSYM_UNDEFINED;
538 static Key motion_key_y = KSYM_UNDEFINED;
539 static Key button_key = KSYM_UNDEFINED;
540 static float motion_x1, motion_y1;
541 static float button_x1, button_y1;
542 static SDL_FingerID motion_id = -1;
543 static SDL_FingerID button_id = -1;
544 int move_trigger_distance_percent = 2; // percent of touchpad width/height
545 int drop_trigger_distance_percent = 5; // percent of touchpad width/height
546 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
547 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
548 float event_x = event->x;
549 float event_y = event->y;
553 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
554 event->type == EVENT_FINGERPRESS ? "pressed" :
555 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
559 event->dx, event->dy,
564 if (game_status != GAME_MODE_PLAYING)
568 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
570 boolean use_virtual_button_control = FALSE;
572 if (use_virtual_button_control)
575 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
578 Key key = (event->x < 1.0 / 3.0 ?
579 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
580 setup.input[0].key.drop) :
581 event->x > 2.0 / 3.0 ?
582 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
583 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
584 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
585 setup.input[0].key.right) :
588 char *key_name = (key == setup.input[0].key.snap ? "SNAP" :
589 key == setup.input[0].key.drop ? "DROP" :
590 key == setup.input[0].key.up ? "UP" :
591 key == setup.input[0].key.down ? "DOWN" :
592 key == setup.input[0].key.left ? "LEFT" :
593 key == setup.input[0].key.right ? "RIGHT" : "(unknown)");
595 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
598 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
599 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
600 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
601 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
602 setup.input[0].key.snap);
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;
715 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d]",
716 key_name, key_status_name, i);
718 Error(ERR_DEBUG, "=> key == %d, key_status == %d [%d]", key, key_status, i);
725 // use touch direction control
727 if (event->type == EVENT_FINGERPRESS)
729 if (event_x > 1.0 / 3.0)
733 motion_id = event->fingerId;
738 motion_key_x = KSYM_UNDEFINED;
739 motion_key_y = KSYM_UNDEFINED;
741 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
747 button_id = event->fingerId;
752 button_key = setup.input[0].key.snap;
754 HandleKey(button_key, KEY_PRESSED);
756 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
759 else if (event->type == EVENT_FINGERRELEASE)
761 if (event->fingerId == motion_id)
765 if (motion_key_x != KSYM_UNDEFINED)
766 HandleKey(motion_key_x, KEY_RELEASED);
767 if (motion_key_y != KSYM_UNDEFINED)
768 HandleKey(motion_key_y, KEY_RELEASED);
770 motion_key_x = KSYM_UNDEFINED;
771 motion_key_y = KSYM_UNDEFINED;
773 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
775 else if (event->fingerId == button_id)
779 if (button_key != KSYM_UNDEFINED)
780 HandleKey(button_key, KEY_RELEASED);
782 button_key = KSYM_UNDEFINED;
784 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
787 else if (event->type == EVENT_FINGERMOTION)
789 if (event->fingerId == motion_id)
791 float distance_x = ABS(event_x - motion_x1);
792 float distance_y = ABS(event_y - motion_y1);
793 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
794 event_x > motion_x1 ? setup.input[0].key.right :
796 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
797 event_y > motion_y1 ? setup.input[0].key.down :
800 if (distance_x < move_trigger_distance / 2 ||
801 distance_x < distance_y)
802 new_motion_key_x = KSYM_UNDEFINED;
804 if (distance_y < move_trigger_distance / 2 ||
805 distance_y < distance_x)
806 new_motion_key_y = KSYM_UNDEFINED;
808 if (distance_x > move_trigger_distance ||
809 distance_y > move_trigger_distance)
811 if (new_motion_key_x != motion_key_x)
813 if (motion_key_x != KSYM_UNDEFINED)
814 HandleKey(motion_key_x, KEY_RELEASED);
815 if (new_motion_key_x != KSYM_UNDEFINED)
816 HandleKey(new_motion_key_x, KEY_PRESSED);
819 if (new_motion_key_y != motion_key_y)
821 if (motion_key_y != KSYM_UNDEFINED)
822 HandleKey(motion_key_y, KEY_RELEASED);
823 if (new_motion_key_y != KSYM_UNDEFINED)
824 HandleKey(new_motion_key_y, KEY_PRESSED);
830 motion_key_x = new_motion_key_x;
831 motion_key_y = new_motion_key_y;
833 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
836 else if (event->fingerId == button_id)
838 float distance_x = ABS(event_x - button_x1);
839 float distance_y = ABS(event_y - button_y1);
841 if (distance_x < drop_trigger_distance / 2 &&
842 distance_y > drop_trigger_distance)
844 if (button_key == setup.input[0].key.snap)
845 HandleKey(button_key, KEY_RELEASED);
850 button_key = setup.input[0].key.drop;
852 HandleKey(button_key, KEY_PRESSED);
854 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
862 void HandleFingerEvent(FingerEvent *event)
865 static int num_events = 0;
871 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
872 event->type == EVENT_FINGERPRESS ? "pressed" :
873 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
877 event->dx, event->dy,
883 int x = (int)(event->x * video.width);
884 int y = (int)(event->y * video.height);
885 int button = MB_LEFTBUTTON;
887 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
891 if (++num_events >= max_events)
897 if (event->type == EVENT_FINGERPRESS ||
898 event->type == EVENT_FINGERMOTION)
899 button_status = button;
901 button_status = MB_RELEASED;
903 int max_x = SX + SXSIZE;
904 int max_y = SY + SYSIZE;
908 if (game_status == GAME_MODE_PLAYING)
910 if (game_status == GAME_MODE_PLAYING &&
914 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
917 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
918 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
919 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
920 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
921 setup.input[0].key.drop);
923 Key key = (y < max_y / 3 ? setup.input[0].key.up :
924 y > 2 * max_y / 3 ? setup.input[0].key.down :
925 x < max_x / 3 ? setup.input[0].key.left :
926 x > 2 * max_x / 3 ? setup.input[0].key.right :
927 setup.input[0].key.drop);
930 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
932 HandleKey(key, key_status);
937 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
938 button_status, button);
940 HandleButton(x, y, button_status, button);
948 static boolean checkTextInputKeyModState()
950 // when playing, only handle raw key events and ignore text input
951 if (game_status == GAME_MODE_PLAYING)
954 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
957 void HandleTextEvent(TextEvent *event)
959 char *text = event->text;
960 Key key = getKeyFromKeyName(text);
963 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
966 text[0], (int)(text[0]),
968 getKeyNameFromKey(key),
972 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
974 if (game_status != GAME_MODE_PLAYING &&
975 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
977 if (checkTextInputKeyModState())
979 HandleKey(key, KEY_PRESSED);
980 HandleKey(key, KEY_RELEASED);
985 void HandleKeyEvent(KeyEvent *event)
987 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
988 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
989 Key key = GetEventKey(event, with_modifiers);
990 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
993 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
994 event->type == EVENT_KEYPRESS ? "pressed" : "released",
995 event->keysym.scancode,
1000 getKeyNameFromKey(key));
1004 if (key == KSYM_Menu)
1005 Error(ERR_DEBUG, "menu key pressed");
1006 else if (key == KSYM_Back)
1007 Error(ERR_DEBUG, "back key pressed");
1010 #if defined(PLATFORM_ANDROID)
1011 // always map the "back" button to the "escape" key on Android devices
1012 if (key == KSYM_Back)
1016 HandleKeyModState(keymod, key_status);
1018 #if defined(TARGET_SDL2)
1020 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
1022 if (game_status == GAME_MODE_PLAYING ||
1023 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
1025 if (!checkTextInputKeyModState())
1026 HandleKey(key, key_status);
1028 HandleKey(key, key_status);
1032 void HandleFocusEvent(FocusChangeEvent *event)
1034 static int old_joystick_status = -1;
1036 if (event->type == EVENT_FOCUSOUT)
1038 KeyboardAutoRepeatOn();
1039 old_joystick_status = joystick.status;
1040 joystick.status = JOYSTICK_NOT_AVAILABLE;
1042 ClearPlayerAction();
1044 else if (event->type == EVENT_FOCUSIN)
1046 /* When there are two Rocks'n'Diamonds windows which overlap and
1047 the player moves the pointer from one game window to the other,
1048 a 'FocusOut' event is generated for the window the pointer is
1049 leaving and a 'FocusIn' event is generated for the window the
1050 pointer is entering. In some cases, it can happen that the
1051 'FocusIn' event is handled by the one game process before the
1052 'FocusOut' event by the other game process. In this case the
1053 X11 environment would end up with activated keyboard auto repeat,
1054 because unfortunately this is a global setting and not (which
1055 would be far better) set for each X11 window individually.
1056 The effect would be keyboard auto repeat while playing the game
1057 (game_status == GAME_MODE_PLAYING), which is not desired.
1058 To avoid this special case, we just wait 1/10 second before
1059 processing the 'FocusIn' event.
1062 if (game_status == GAME_MODE_PLAYING)
1065 KeyboardAutoRepeatOffUnlessAutoplay();
1068 if (old_joystick_status != -1)
1069 joystick.status = old_joystick_status;
1073 void HandleClientMessageEvent(ClientMessageEvent *event)
1075 if (CheckCloseWindowEvent(event))
1079 void HandleWindowManagerEvent(Event *event)
1081 #if defined(TARGET_SDL)
1082 SDLHandleWindowManagerEvent(event);
1086 void HandleButton(int mx, int my, int button, int button_nr)
1088 static int old_mx = 0, old_my = 0;
1102 #if defined(PLATFORM_ANDROID)
1103 if (game_status != GAME_MODE_PLAYING &&
1104 HandleGadgets(mx, my, button))
1106 /* do not handle this button event anymore */
1107 mx = my = -32; /* force mouse event to be outside screen tiles */
1110 if (HandleGadgets(mx, my, button))
1112 /* do not handle this button event anymore */
1113 mx = my = -32; /* force mouse event to be outside screen tiles */
1117 /* do not use scroll wheel button events for anything other than gadgets */
1118 if (IS_WHEEL_BUTTON(button_nr))
1122 Error(ERR_DEBUG, "::: game_status == %d", game_status);
1125 switch (game_status)
1127 case GAME_MODE_TITLE:
1128 HandleTitleScreen(mx, my, 0, 0, button);
1131 case GAME_MODE_MAIN:
1132 HandleMainMenu(mx, my, 0, 0, button);
1135 case GAME_MODE_PSEUDO_TYPENAME:
1136 HandleTypeName(0, KSYM_Return);
1139 case GAME_MODE_LEVELS:
1140 HandleChooseLevelSet(mx, my, 0, 0, button);
1143 case GAME_MODE_LEVELNR:
1144 HandleChooseLevelNr(mx, my, 0, 0, button);
1147 case GAME_MODE_SCORES:
1148 HandleHallOfFame(0, 0, 0, 0, button);
1151 case GAME_MODE_EDITOR:
1152 HandleLevelEditorIdle();
1155 case GAME_MODE_INFO:
1156 HandleInfoScreen(mx, my, 0, 0, button);
1159 case GAME_MODE_SETUP:
1160 HandleSetupScreen(mx, my, 0, 0, button);
1163 case GAME_MODE_PLAYING:
1165 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1166 DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
1167 LEVELY((my - SY) / TILESIZE_VAR));
1168 // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1177 static boolean is_string_suffix(char *string, char *suffix)
1179 int string_len = strlen(string);
1180 int suffix_len = strlen(suffix);
1182 if (suffix_len > string_len)
1185 return (strEqual(&string[string_len - suffix_len], suffix));
1188 #define MAX_CHEAT_INPUT_LEN 32
1190 static void HandleKeysSpecial(Key key)
1192 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1193 char letter = getCharFromKey(key);
1194 int cheat_input_len = strlen(cheat_input);
1200 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1202 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1203 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1205 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1208 cheat_input[cheat_input_len++] = letter;
1209 cheat_input[cheat_input_len] = '\0';
1212 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1215 if (game_status == GAME_MODE_MAIN)
1217 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1218 is_string_suffix(cheat_input, ":ist"))
1220 InsertSolutionTape();
1222 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1223 is_string_suffix(cheat_input, ":rg"))
1225 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1228 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1229 is_string_suffix(cheat_input, ":rs"))
1231 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1234 else if (is_string_suffix(cheat_input, ":reload-music") ||
1235 is_string_suffix(cheat_input, ":rm"))
1237 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1240 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1241 is_string_suffix(cheat_input, ":ra"))
1243 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1244 1 << ARTWORK_TYPE_SOUNDS |
1245 1 << ARTWORK_TYPE_MUSIC);
1248 else if (is_string_suffix(cheat_input, ":dump-level") ||
1249 is_string_suffix(cheat_input, ":dl"))
1253 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1254 is_string_suffix(cheat_input, ":dt"))
1258 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1259 is_string_suffix(cheat_input, ":ft"))
1261 /* fix single-player tapes that contain player input for more than one
1262 player (due to a bug in 3.3.1.2 and earlier versions), which results
1263 in playing levels with more than one player in multi-player mode,
1264 even though the tape was originally recorded in single-player mode */
1266 /* remove player input actions for all players but the first one */
1267 for (i = 1; i < MAX_PLAYERS; i++)
1268 tape.player_participates[i] = FALSE;
1270 tape.changed = TRUE;
1272 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1273 is_string_suffix(cheat_input, ":snl"))
1275 SaveNativeLevel(&level);
1278 else if (game_status == GAME_MODE_PLAYING)
1281 if (is_string_suffix(cheat_input, ".q"))
1282 DEBUG_SetMaximumDynamite();
1285 else if (game_status == GAME_MODE_EDITOR)
1287 if (is_string_suffix(cheat_input, ":dump-brush") ||
1288 is_string_suffix(cheat_input, ":DB"))
1292 else if (is_string_suffix(cheat_input, ":DDB"))
1299 void HandleKey(Key key, int key_status)
1301 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1302 static struct SetupKeyboardInfo ski;
1303 static struct SetupShortcutInfo ssi;
1312 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1313 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1314 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1315 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1316 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1317 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1322 if (game_status == GAME_MODE_PLAYING)
1324 /* only needed for single-step tape recording mode */
1325 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1326 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1327 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1328 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1331 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1333 byte key_action = 0;
1335 if (setup.input[pnr].use_joystick)
1338 ski = setup.input[pnr].key;
1340 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1341 if (key == *key_info[i].key_custom)
1342 key_action |= key_info[i].action;
1344 /* use combined snap+direction keys for the first player only */
1347 ssi = setup.shortcut;
1349 for (i = 0; i < NUM_DIRECTIONS; i++)
1350 if (key == *key_info[i].key_snap)
1351 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1354 /* clear delayed snap and drop actions in single step mode (see below) */
1355 if (tape.single_step)
1357 if (clear_snap_button[pnr])
1359 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1360 clear_snap_button[pnr] = FALSE;
1363 if (clear_drop_button[pnr])
1365 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1366 clear_drop_button[pnr] = FALSE;
1370 if (key_status == KEY_PRESSED)
1371 stored_player[pnr].action |= key_action;
1373 stored_player[pnr].action &= ~key_action;
1375 if (tape.single_step && tape.recording && tape.pausing)
1377 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1379 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1381 /* if snap key already pressed, don't snap when releasing (below) */
1382 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1383 element_snapped[pnr] = TRUE;
1385 /* if drop key already pressed, don't drop when releasing (below) */
1386 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1387 element_dropped[pnr] = TRUE;
1390 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1393 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1396 printf("::: drop key pressed\n");
1399 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1400 getRedDiskReleaseFlag_SP() == 0)
1401 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1403 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1407 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1409 if (key_action & KEY_BUTTON_SNAP)
1411 /* if snap key was released without moving (see above), snap now */
1412 if (!element_snapped[pnr])
1414 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1416 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1418 /* clear delayed snap button on next event */
1419 clear_snap_button[pnr] = TRUE;
1422 element_snapped[pnr] = FALSE;
1426 if (key_action & KEY_BUTTON_DROP &&
1427 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1429 /* if drop key was released without moving (see above), drop now */
1430 if (!element_dropped[pnr])
1432 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1434 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1435 getRedDiskReleaseFlag_SP() != 0)
1436 stored_player[pnr].action |= KEY_BUTTON_DROP;
1438 /* clear delayed drop button on next event */
1439 clear_drop_button[pnr] = TRUE;
1442 element_dropped[pnr] = FALSE;
1447 else if (tape.recording && tape.pausing)
1449 /* prevent key release events from un-pausing a paused game */
1450 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1451 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1457 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1458 if (key == key_info[i].key_default)
1459 joy |= key_info[i].action;
1464 if (key_status == KEY_PRESSED)
1465 key_joystick_mapping |= joy;
1467 key_joystick_mapping &= ~joy;
1472 if (game_status != GAME_MODE_PLAYING)
1473 key_joystick_mapping = 0;
1475 if (key_status == KEY_RELEASED)
1478 if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1479 (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1481 setup.fullscreen = !setup.fullscreen;
1484 printf("::: %d\n", setup.window_scaling_percent);
1487 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1489 if (game_status == GAME_MODE_SETUP)
1490 RedrawSetupScreenAfterFullscreenToggle();
1495 if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1496 (GetKeyModState() & KMOD_Alt) && video.window_scaling_available &&
1497 !video.fullscreen_enabled)
1500 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1502 setup.window_scaling_percent +=
1503 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1505 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1506 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1507 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1508 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1510 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1512 if (game_status == GAME_MODE_SETUP)
1513 RedrawSetupScreenAfterFullscreenToggle();
1519 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1520 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1522 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1523 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1531 if (game_status == GAME_MODE_MAIN &&
1532 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1534 StartGameActions(options.network, setup.autorecord, level.random_seed);
1539 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1541 if (key == setup.shortcut.save_game)
1543 else if (key == setup.shortcut.load_game)
1545 else if (key == setup.shortcut.toggle_pause)
1546 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1548 HandleTapeButtonKeys(key);
1549 HandleSoundButtonKeys(key);
1552 if (game_status == GAME_MODE_PLAYING && !network_playing)
1554 int centered_player_nr_next = -999;
1556 if (key == setup.shortcut.focus_player_all)
1557 centered_player_nr_next = -1;
1559 for (i = 0; i < MAX_PLAYERS; i++)
1560 if (key == setup.shortcut.focus_player[i])
1561 centered_player_nr_next = i;
1563 if (centered_player_nr_next != -999)
1565 game.centered_player_nr_next = centered_player_nr_next;
1566 game.set_centered_player = TRUE;
1570 tape.centered_player_nr_next = game.centered_player_nr_next;
1571 tape.set_centered_player = TRUE;
1576 HandleKeysSpecial(key);
1578 if (HandleGadgetsKeyInput(key))
1580 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1581 key = KSYM_UNDEFINED;
1584 switch (game_status)
1586 case GAME_MODE_PSEUDO_TYPENAME:
1587 HandleTypeName(0, key);
1590 case GAME_MODE_TITLE:
1591 case GAME_MODE_MAIN:
1592 case GAME_MODE_LEVELS:
1593 case GAME_MODE_LEVELNR:
1594 case GAME_MODE_SETUP:
1595 case GAME_MODE_INFO:
1596 case GAME_MODE_SCORES:
1601 if (game_status == GAME_MODE_TITLE)
1602 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1603 else if (game_status == GAME_MODE_MAIN)
1604 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1605 else if (game_status == GAME_MODE_LEVELS)
1606 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1607 else if (game_status == GAME_MODE_LEVELNR)
1608 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1609 else if (game_status == GAME_MODE_SETUP)
1610 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1611 else if (game_status == GAME_MODE_INFO)
1612 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1613 else if (game_status == GAME_MODE_SCORES)
1614 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1618 if (game_status != GAME_MODE_MAIN)
1619 FadeSkipNextFadeIn();
1621 if (game_status == GAME_MODE_TITLE)
1622 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1623 else if (game_status == GAME_MODE_LEVELS)
1624 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1625 else if (game_status == GAME_MODE_LEVELNR)
1626 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1627 else if (game_status == GAME_MODE_SETUP)
1628 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1629 else if (game_status == GAME_MODE_INFO)
1630 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1631 else if (game_status == GAME_MODE_SCORES)
1632 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1636 if (game_status == GAME_MODE_LEVELS)
1637 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1638 else if (game_status == GAME_MODE_LEVELNR)
1639 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1640 else if (game_status == GAME_MODE_SETUP)
1641 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1642 else if (game_status == GAME_MODE_INFO)
1643 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1644 else if (game_status == GAME_MODE_SCORES)
1645 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1648 case KSYM_Page_Down:
1649 if (game_status == GAME_MODE_LEVELS)
1650 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1651 else if (game_status == GAME_MODE_LEVELNR)
1652 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1653 else if (game_status == GAME_MODE_SETUP)
1654 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1655 else if (game_status == GAME_MODE_INFO)
1656 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1657 else if (game_status == GAME_MODE_SCORES)
1658 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1663 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1667 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1668 printf("Supaplex border elements %s\n",
1669 setup.sp_show_border_elements ? "enabled" : "disabled");
1678 case GAME_MODE_EDITOR:
1679 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1680 HandleLevelEditorKeyInput(key);
1683 case GAME_MODE_PLAYING:
1688 RequestQuitGame(setup.ask_on_escape);
1706 if (GameFrameDelay == 500)
1707 GameFrameDelay = GAME_FRAME_DELAY;
1709 GameFrameDelay = 500;
1712 GameFrameDelay = (key - KSYM_0) * 10;
1713 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1714 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1720 options.debug = FALSE;
1721 printf("debug mode disabled\n");
1725 options.debug = TRUE;
1726 printf("debug mode enabled\n");
1732 if (!global.fps_slowdown)
1734 global.fps_slowdown = TRUE;
1735 global.fps_slowdown_factor = 2;
1736 printf("fps slowdown enabled -- display only every 2nd frame\n");
1738 else if (global.fps_slowdown_factor == 2)
1740 global.fps_slowdown_factor = 4;
1741 printf("fps slowdown enabled -- display only every 4th frame\n");
1745 global.fps_slowdown = FALSE;
1746 global.fps_slowdown_factor = 1;
1747 printf("fps slowdown disabled\n");
1754 ScrollStepSize = TILEX / 8;
1755 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1759 ScrollStepSize = TILEX / 4;
1760 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1764 ScrollStepSize = TILEX / 2;
1765 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1769 ScrollStepSize = TILEX;
1770 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1775 printf("::: currently using game engine version %d\n",
1776 game.engine_version);
1787 if (key == KSYM_Escape)
1789 game_status = GAME_MODE_MAIN;
1797 void HandleNoEvent()
1799 if (button_status && game_status != GAME_MODE_PLAYING)
1801 HandleButton(0, 0, -button_status, button_status);
1812 #if defined(NETWORK_AVALIABLE)
1813 if (options.network)
1817 switch (game_status)
1819 case GAME_MODE_MAIN:
1820 DrawPreviewLevelAnimation();
1824 case GAME_MODE_LEVELS:
1825 case GAME_MODE_LEVELNR:
1826 case GAME_MODE_SETUP:
1827 case GAME_MODE_INFO:
1828 case GAME_MODE_SCORES:
1832 case GAME_MODE_EDITOR:
1833 HandleLevelEditorIdle();
1841 static int HandleJoystickForAllPlayers()
1846 for (i = 0; i < MAX_PLAYERS; i++)
1848 byte joy_action = 0;
1851 if (!setup.input[i].use_joystick)
1855 joy_action = Joystick(i);
1856 result |= joy_action;
1858 if (!setup.input[i].use_joystick)
1861 stored_player[i].action = joy_action;
1867 void HandleJoystick()
1869 int joystick = HandleJoystickForAllPlayers();
1870 int keyboard = key_joystick_mapping;
1871 int joy = (joystick | keyboard);
1872 int left = joy & JOY_LEFT;
1873 int right = joy & JOY_RIGHT;
1874 int up = joy & JOY_UP;
1875 int down = joy & JOY_DOWN;
1876 int button = joy & JOY_BUTTON;
1877 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1878 int dx = (left ? -1 : right ? 1 : 0);
1879 int dy = (up ? -1 : down ? 1 : 0);
1881 switch (game_status)
1883 case GAME_MODE_TITLE:
1884 case GAME_MODE_MAIN:
1885 case GAME_MODE_LEVELS:
1886 case GAME_MODE_LEVELNR:
1887 case GAME_MODE_SETUP:
1888 case GAME_MODE_INFO:
1890 static unsigned int joystickmove_delay = 0;
1892 if (joystick && !button &&
1893 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1894 newbutton = dx = dy = 0;
1896 if (game_status == GAME_MODE_TITLE)
1897 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1898 else if (game_status == GAME_MODE_MAIN)
1899 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1900 else if (game_status == GAME_MODE_LEVELS)
1901 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1902 else if (game_status == GAME_MODE_LEVELNR)
1903 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1904 else if (game_status == GAME_MODE_SETUP)
1905 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1906 else if (game_status == GAME_MODE_INFO)
1907 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1911 case GAME_MODE_SCORES:
1912 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1916 case GAME_MODE_EDITOR:
1917 HandleLevelEditorIdle();
1921 case GAME_MODE_PLAYING:
1922 if (tape.playing || keyboard)
1923 newbutton = ((joy & JOY_BUTTON) != 0);
1926 if (newbutton && local_player->LevelSolved_GameEnd)
1928 if (newbutton && AllPlayersGone)