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"
25 #define DEBUG_EVENTS 0
28 static boolean cursor_inside_playfield = FALSE;
29 static boolean playfield_cursor_set = FALSE;
30 static unsigned int playfield_cursor_delay = 0;
33 /* event filter especially needed for SDL event filtering due to
34 delay problems with lots of mouse motion events when mouse button
35 not pressed (X11 can handle this with 'PointerMotionHintMask') */
37 /* event filter addition for SDL2: as SDL2 does not have a function to enable
38 or disable keyboard auto-repeat, filter repeated keyboard events instead */
40 static int FilterEventsExt(const Event *event)
44 #if defined(TARGET_SDL2)
45 /* skip repeated key press events if keyboard auto-repeat is disabled */
46 if (event->type == EVENT_KEYPRESS &&
52 /* non-motion events are directly passed to event handler functions */
53 if (event->type != EVENT_MOTIONNOTIFY)
56 motion = (MotionEvent *)event;
57 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
58 motion->y >= SY && motion->y < SY + SYSIZE);
60 if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
62 SetMouseCursor(CURSOR_DEFAULT);
63 playfield_cursor_set = FALSE;
64 DelayReached(&playfield_cursor_delay, 0);
67 /* skip mouse motion events without pressed button outside level editor */
68 if (button_status == MB_RELEASED &&
69 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
75 #if defined(TARGET_SDL2)
76 int FilterEvents(void *userdata, Event *event)
78 return FilterEventsExt(event);
81 int FilterEvents(const Event *event)
83 return FilterEventsExt(event);
87 /* to prevent delay problems, skip mouse motion events if the very next
88 event is also a mouse motion event (and therefore effectively only
89 handling the last of a row of mouse motion events in the event queue) */
91 boolean SkipPressedMouseMotionEvent(const Event *event)
93 /* nothing to do if the current event is not a mouse motion event */
94 if (event->type != EVENT_MOTIONNOTIFY)
97 /* only skip motion events with pressed button outside level editor */
98 if (button_status == MB_RELEASED ||
99 game_status == GAME_MODE_EDITOR || game_status == GAME_MODE_PLAYING)
106 PeekEvent(&next_event);
108 /* if next event is also a mouse motion event, skip the current one */
109 if (next_event.type == EVENT_MOTIONNOTIFY)
116 /* this is only really needed for non-SDL targets to filter unwanted events;
117 when using SDL with properly installed event filter, this function can be
118 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
120 static boolean NextValidEvent(Event *event)
122 while (PendingEvent())
124 boolean handle_this_event = FALSE;
128 if (FilterEventsExt(event))
129 handle_this_event = TRUE;
131 if (SkipPressedMouseMotionEvent(event))
132 handle_this_event = FALSE;
134 if (handle_this_event)
145 if (PendingEvent()) /* got event */
149 while (NextValidEvent(&event))
153 case EVENT_BUTTONPRESS:
154 case EVENT_BUTTONRELEASE:
155 HandleButtonEvent((ButtonEvent *) &event);
158 case EVENT_MOTIONNOTIFY:
159 HandleMotionEvent((MotionEvent *) &event);
162 #if defined(TARGET_SDL2)
163 case SDL_WINDOWEVENT:
164 HandleWindowEvent((WindowEvent *) &event);
167 case EVENT_FINGERPRESS:
168 case EVENT_FINGERRELEASE:
169 case EVENT_FINGERMOTION:
170 HandleFingerEvent((FingerEvent *) &event);
173 case EVENT_TEXTINPUT:
174 HandleTextEvent((TextEvent *) &event);
177 case SDL_APP_WILLENTERBACKGROUND:
178 case SDL_APP_DIDENTERBACKGROUND:
179 case SDL_APP_WILLENTERFOREGROUND:
180 case SDL_APP_DIDENTERFOREGROUND:
181 HandlePauseResumeEvent((PauseResumeEvent *) &event);
186 case EVENT_KEYRELEASE:
187 HandleKeyEvent((KeyEvent *) &event);
191 HandleOtherEvents(&event);
198 /* when playing, display a special mouse pointer inside the playfield */
199 if (game_status == GAME_MODE_PLAYING && !tape.pausing)
201 if (!playfield_cursor_set && cursor_inside_playfield &&
202 DelayReached(&playfield_cursor_delay, 1000))
204 SetMouseCursor(CURSOR_PLAYFIELD);
205 playfield_cursor_set = TRUE;
208 else if (playfield_cursor_set)
210 SetMouseCursor(CURSOR_DEFAULT);
211 playfield_cursor_set = FALSE;
223 /* don't use all CPU time when idle; the main loop while playing
224 has its own synchronization and is CPU friendly, too */
226 if (game_status == GAME_MODE_PLAYING)
233 if (!PendingEvent()) /* delay only if no pending events */
237 /* refresh window contents from drawing buffer, if needed */
240 if (game_status == GAME_MODE_QUIT)
245 void HandleOtherEvents(Event *event)
250 HandleExposeEvent((ExposeEvent *) event);
253 case EVENT_UNMAPNOTIFY:
255 /* This causes the game to stop not only when iconified, but also
256 when on another virtual desktop, which might be not desired. */
257 SleepWhileUnmapped();
263 HandleFocusEvent((FocusChangeEvent *) event);
266 case EVENT_CLIENTMESSAGE:
267 HandleClientMessageEvent((ClientMessageEvent *) event);
270 #if defined(TARGET_SDL)
271 case SDL_JOYAXISMOTION:
272 case SDL_JOYBUTTONDOWN:
273 case SDL_JOYBUTTONUP:
274 HandleJoystickEvent(event);
278 HandleWindowManagerEvent(event);
287 void ClearEventQueue()
289 while (PendingEvent())
297 case EVENT_BUTTONRELEASE:
298 button_status = MB_RELEASED;
301 case EVENT_KEYRELEASE:
305 key_joystick_mapping = 0;
310 HandleOtherEvents(&event);
316 void ClearPlayerAction()
320 /* simulate key release events for still pressed keys */
321 key_joystick_mapping = 0;
322 for (i = 0; i < MAX_PLAYERS; i++)
323 stored_player[i].action = 0;
326 void SleepWhileUnmapped()
328 boolean window_unmapped = TRUE;
330 KeyboardAutoRepeatOn();
332 while (window_unmapped)
340 case EVENT_BUTTONRELEASE:
341 button_status = MB_RELEASED;
344 case EVENT_KEYRELEASE:
345 key_joystick_mapping = 0;
348 case EVENT_MAPNOTIFY:
349 window_unmapped = FALSE;
352 case EVENT_UNMAPNOTIFY:
353 /* this is only to surely prevent the 'should not happen' case
354 * of recursively looping between 'SleepWhileUnmapped()' and
355 * 'HandleOtherEvents()' which usually calls this funtion.
360 HandleOtherEvents(&event);
365 if (game_status == GAME_MODE_PLAYING)
366 KeyboardAutoRepeatOffUnlessAutoplay();
369 void HandleExposeEvent(ExposeEvent *event)
373 void HandleButtonEvent(ButtonEvent *event)
376 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
378 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
382 motion_status = FALSE;
384 if (event->type == EVENT_BUTTONPRESS)
385 button_status = event->button;
387 button_status = MB_RELEASED;
389 HandleButton(event->x, event->y, button_status, event->button);
392 void HandleMotionEvent(MotionEvent *event)
394 if (!PointerInWindow(window))
395 return; /* window and pointer are on different screens */
397 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
400 motion_status = TRUE;
403 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
404 button_status, event->x, event->y);
407 HandleButton(event->x, event->y, button_status, button_status);
410 #if defined(TARGET_SDL2)
411 void HandleWindowEvent(WindowEvent *event)
414 int subtype = event->event;
417 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
418 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
419 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
420 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
421 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
422 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
423 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
424 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
425 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
426 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
427 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
428 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
429 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
430 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
433 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
434 event_name, event->data1, event->data2);
437 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
438 event->event == SDL_WINDOWEVENT_RESIZED ||
439 event->event == SDL_WINDOWEVENT_EXPOSED)
443 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
445 // if game started in fullscreen mode, window will also get fullscreen size
446 if (!video.fullscreen_enabled && video.fullscreen_initial)
448 SDLSetWindowScaling(setup.window_scaling_percent);
450 // only do this correction once
451 video.fullscreen_initial = FALSE;
456 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
459 int new_window_width = event->data1;
460 int new_window_height = event->data2;
462 // if window size has changed after resizing, calculate new scaling factor
463 if (new_window_width != video.window_width ||
464 new_window_height != video.window_height)
466 int new_xpercent = (100 * new_window_width / video.width);
467 int new_ypercent = (100 * new_window_height / video.height);
470 printf("::: RESIZED from %d, %d to %d, %d\n",
471 video.window_width, video.window_height,
472 new_window_width, new_window_height);
475 setup.window_scaling_percent = video.window_scaling_percent =
476 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
477 MAX_WINDOW_SCALING_PERCENT);
479 video.window_width = new_window_width;
480 video.window_height = new_window_height;
483 printf("::: setup.window_scaling_percent set to %d\n",
484 setup.window_scaling_percent);
487 if (game_status == GAME_MODE_SETUP)
488 RedrawSetupScreenAfterFullscreenToggle();
493 // prevent slightly wrong scaling factor due to rounding differences
494 float scaling_factor = (float)setup.window_scaling_percent / 100;
495 int old_xsize = (int)(scaling_factor * video.width);
496 int old_ysize = (int)(scaling_factor * video.height);
497 int new_xsize = event->data1;
498 int new_ysize = event->data2;
500 // window size is unchanged when going from fullscreen to window mode,
501 // but reverse calculation of scaling factor might result in a scaling
502 // factor that is slightly different due to rounding differences;
503 // therefore compare old/new window size and not old/new scaling factor
504 if (old_xsize != new_xsize ||
505 old_ysize != new_ysize)
507 int new_xpercent = (100 * new_xsize / video.width);
508 int new_ypercent = (100 * new_ysize / video.height);
510 setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
512 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
513 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
514 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
515 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
517 printf("::: setup.window_scaling_percent set to %d\n",
518 setup.window_scaling_percent);
526 #define NUM_TOUCH_FINGERS 3
531 SDL_FingerID finger_id;
534 } touch_info[NUM_TOUCH_FINGERS];
536 void HandleFingerEvent(FingerEvent *event)
538 static Key motion_key_x = KSYM_UNDEFINED;
539 static Key motion_key_y = KSYM_UNDEFINED;
540 static Key button_key = KSYM_UNDEFINED;
541 static float motion_x1, motion_y1;
542 static float button_x1, button_y1;
543 static SDL_FingerID motion_id = -1;
544 static SDL_FingerID button_id = -1;
545 int move_trigger_distance_percent = 2; // percent of touchpad width/height
546 int drop_trigger_distance_percent = 5; // percent of touchpad width/height
547 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
548 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
549 float event_x = event->x;
550 float event_y = event->y;
554 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
555 event->type == EVENT_FINGERPRESS ? "pressed" :
556 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
560 event->dx, event->dy,
565 if (game_status != GAME_MODE_PLAYING)
569 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
571 boolean use_virtual_button_control = FALSE;
573 if (use_virtual_button_control)
576 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
579 Key key = (event->x < 1.0 / 3.0 ?
580 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
581 setup.input[0].key.drop) :
582 event->x > 2.0 / 3.0 ?
583 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
584 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
585 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
586 setup.input[0].key.right) :
589 char *key_name = (key == setup.input[0].key.snap ? "SNAP" :
590 key == setup.input[0].key.drop ? "DROP" :
591 key == setup.input[0].key.up ? "UP" :
592 key == setup.input[0].key.down ? "DOWN" :
593 key == setup.input[0].key.left ? "LEFT" :
594 key == setup.input[0].key.right ? "RIGHT" : "(unknown)");
596 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
599 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
600 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
601 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
602 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
603 setup.input[0].key.snap);
607 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
608 getKeyNameFromKey(key), key_status_name, event->fingerId);
610 // check if we already know this touch event's finger id
611 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
613 if (touch_info[i].touched &&
614 touch_info[i].finger_id == event->fingerId)
616 // Error(ERR_DEBUG, "MARK 1: %d", i);
622 if (i >= NUM_TOUCH_FINGERS)
624 if (key_status == KEY_PRESSED)
626 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
628 // unknown finger id -- get new, empty slot, if available
629 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
631 if (touch_info[i].counter < oldest_counter)
634 oldest_counter = touch_info[i].counter;
636 // Error(ERR_DEBUG, "MARK 2: %d", i);
639 if (!touch_info[i].touched)
641 // Error(ERR_DEBUG, "MARK 3: %d", i);
647 if (i >= NUM_TOUCH_FINGERS)
649 // all slots allocated -- use oldest slot
652 // Error(ERR_DEBUG, "MARK 4: %d", i);
657 // release of previously unknown key (should not happen)
659 if (key != KSYM_UNDEFINED)
661 HandleKey(key, KEY_RELEASED);
663 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
664 getKeyNameFromKey(key), "KEY_RELEASED", i);
669 if (i < NUM_TOUCH_FINGERS)
671 if (key_status == KEY_PRESSED)
673 if (touch_info[i].key != key)
675 if (touch_info[i].key != KSYM_UNDEFINED)
677 HandleKey(touch_info[i].key, KEY_RELEASED);
679 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
680 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
683 if (key != KSYM_UNDEFINED)
685 HandleKey(key, KEY_PRESSED);
687 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
688 getKeyNameFromKey(key), "KEY_PRESSED", i);
692 touch_info[i].touched = TRUE;
693 touch_info[i].finger_id = event->fingerId;
694 touch_info[i].counter = Counter();
695 touch_info[i].key = key;
699 if (touch_info[i].key != KSYM_UNDEFINED)
701 HandleKey(touch_info[i].key, KEY_RELEASED);
703 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
704 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
707 touch_info[i].touched = FALSE;
708 touch_info[i].finger_id = 0;
709 touch_info[i].counter = 0;
710 touch_info[i].key = 0;
716 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d]",
717 key_name, key_status_name, i);
719 Error(ERR_DEBUG, "=> key == %d, key_status == %d [%d]", key, key_status, i);
726 // use touch direction control
728 if (event->type == EVENT_FINGERPRESS)
730 if (event_x > 1.0 / 3.0)
734 motion_id = event->fingerId;
739 motion_key_x = KSYM_UNDEFINED;
740 motion_key_y = KSYM_UNDEFINED;
742 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
748 button_id = event->fingerId;
753 button_key = setup.input[0].key.snap;
755 HandleKey(button_key, KEY_PRESSED);
757 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
760 else if (event->type == EVENT_FINGERRELEASE)
762 if (event->fingerId == motion_id)
766 if (motion_key_x != KSYM_UNDEFINED)
767 HandleKey(motion_key_x, KEY_RELEASED);
768 if (motion_key_y != KSYM_UNDEFINED)
769 HandleKey(motion_key_y, KEY_RELEASED);
771 motion_key_x = KSYM_UNDEFINED;
772 motion_key_y = KSYM_UNDEFINED;
774 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
776 else if (event->fingerId == button_id)
780 if (button_key != KSYM_UNDEFINED)
781 HandleKey(button_key, KEY_RELEASED);
783 button_key = KSYM_UNDEFINED;
785 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
788 else if (event->type == EVENT_FINGERMOTION)
790 if (event->fingerId == motion_id)
792 float distance_x = ABS(event_x - motion_x1);
793 float distance_y = ABS(event_y - motion_y1);
794 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
795 event_x > motion_x1 ? setup.input[0].key.right :
797 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
798 event_y > motion_y1 ? setup.input[0].key.down :
801 if (distance_x < move_trigger_distance / 2 ||
802 distance_x < distance_y)
803 new_motion_key_x = KSYM_UNDEFINED;
805 if (distance_y < move_trigger_distance / 2 ||
806 distance_y < distance_x)
807 new_motion_key_y = KSYM_UNDEFINED;
809 if (distance_x > move_trigger_distance ||
810 distance_y > move_trigger_distance)
812 if (new_motion_key_x != motion_key_x)
814 if (motion_key_x != KSYM_UNDEFINED)
815 HandleKey(motion_key_x, KEY_RELEASED);
816 if (new_motion_key_x != KSYM_UNDEFINED)
817 HandleKey(new_motion_key_x, KEY_PRESSED);
820 if (new_motion_key_y != motion_key_y)
822 if (motion_key_y != KSYM_UNDEFINED)
823 HandleKey(motion_key_y, KEY_RELEASED);
824 if (new_motion_key_y != KSYM_UNDEFINED)
825 HandleKey(new_motion_key_y, KEY_PRESSED);
831 motion_key_x = new_motion_key_x;
832 motion_key_y = new_motion_key_y;
834 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
837 else if (event->fingerId == button_id)
839 float distance_x = ABS(event_x - button_x1);
840 float distance_y = ABS(event_y - button_y1);
842 if (distance_x < drop_trigger_distance / 2 &&
843 distance_y > drop_trigger_distance)
845 if (button_key == setup.input[0].key.snap)
846 HandleKey(button_key, KEY_RELEASED);
851 button_key = setup.input[0].key.drop;
853 HandleKey(button_key, KEY_PRESSED);
855 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
863 void HandleFingerEvent(FingerEvent *event)
866 static int num_events = 0;
872 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
873 event->type == EVENT_FINGERPRESS ? "pressed" :
874 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
878 event->dx, event->dy,
884 int x = (int)(event->x * video.width);
885 int y = (int)(event->y * video.height);
886 int button = MB_LEFTBUTTON;
888 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
892 if (++num_events >= max_events)
898 if (event->type == EVENT_FINGERPRESS ||
899 event->type == EVENT_FINGERMOTION)
900 button_status = button;
902 button_status = MB_RELEASED;
904 int max_x = SX + SXSIZE;
905 int max_y = SY + SYSIZE;
909 if (game_status == GAME_MODE_PLAYING)
911 if (game_status == GAME_MODE_PLAYING &&
915 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
918 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
919 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
920 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
921 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
922 setup.input[0].key.drop);
924 Key key = (y < max_y / 3 ? setup.input[0].key.up :
925 y > 2 * max_y / 3 ? setup.input[0].key.down :
926 x < max_x / 3 ? setup.input[0].key.left :
927 x > 2 * max_x / 3 ? setup.input[0].key.right :
928 setup.input[0].key.drop);
931 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
933 HandleKey(key, key_status);
938 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
939 button_status, button);
941 HandleButton(x, y, button_status, button);
949 static boolean checkTextInputKeyModState()
951 // when playing, only handle raw key events and ignore text input
952 if (game_status == GAME_MODE_PLAYING)
955 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
958 void HandleTextEvent(TextEvent *event)
960 char *text = event->text;
961 Key key = getKeyFromKeyName(text);
964 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
967 text[0], (int)(text[0]),
969 getKeyNameFromKey(key),
973 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
975 if (game_status != GAME_MODE_PLAYING &&
976 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
978 if (checkTextInputKeyModState())
980 HandleKey(key, KEY_PRESSED);
981 HandleKey(key, KEY_RELEASED);
985 void HandlePauseResumeEvent(PauseResumeEvent *event)
987 if (event->type == SDL_APP_WILLENTERBACKGROUND)
991 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
999 void HandleKeyEvent(KeyEvent *event)
1001 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1002 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1003 Key key = GetEventKey(event, with_modifiers);
1004 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1007 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1008 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1009 event->keysym.scancode,
1014 getKeyNameFromKey(key));
1018 if (key == KSYM_Menu)
1019 Error(ERR_DEBUG, "menu key pressed");
1020 else if (key == KSYM_Back)
1021 Error(ERR_DEBUG, "back key pressed");
1024 #if defined(PLATFORM_ANDROID)
1025 // always map the "back" button to the "escape" key on Android devices
1026 if (key == KSYM_Back)
1030 HandleKeyModState(keymod, key_status);
1032 #if defined(TARGET_SDL2)
1034 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
1036 if (game_status == GAME_MODE_PLAYING ||
1037 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
1039 if (!checkTextInputKeyModState())
1040 HandleKey(key, key_status);
1042 HandleKey(key, key_status);
1046 void HandleFocusEvent(FocusChangeEvent *event)
1048 static int old_joystick_status = -1;
1050 if (event->type == EVENT_FOCUSOUT)
1052 KeyboardAutoRepeatOn();
1053 old_joystick_status = joystick.status;
1054 joystick.status = JOYSTICK_NOT_AVAILABLE;
1056 ClearPlayerAction();
1058 else if (event->type == EVENT_FOCUSIN)
1060 /* When there are two Rocks'n'Diamonds windows which overlap and
1061 the player moves the pointer from one game window to the other,
1062 a 'FocusOut' event is generated for the window the pointer is
1063 leaving and a 'FocusIn' event is generated for the window the
1064 pointer is entering. In some cases, it can happen that the
1065 'FocusIn' event is handled by the one game process before the
1066 'FocusOut' event by the other game process. In this case the
1067 X11 environment would end up with activated keyboard auto repeat,
1068 because unfortunately this is a global setting and not (which
1069 would be far better) set for each X11 window individually.
1070 The effect would be keyboard auto repeat while playing the game
1071 (game_status == GAME_MODE_PLAYING), which is not desired.
1072 To avoid this special case, we just wait 1/10 second before
1073 processing the 'FocusIn' event.
1076 if (game_status == GAME_MODE_PLAYING)
1079 KeyboardAutoRepeatOffUnlessAutoplay();
1082 if (old_joystick_status != -1)
1083 joystick.status = old_joystick_status;
1087 void HandleClientMessageEvent(ClientMessageEvent *event)
1089 if (CheckCloseWindowEvent(event))
1093 void HandleWindowManagerEvent(Event *event)
1095 #if defined(TARGET_SDL)
1096 SDLHandleWindowManagerEvent(event);
1100 void HandleButton(int mx, int my, int button, int button_nr)
1102 static int old_mx = 0, old_my = 0;
1116 #if defined(PLATFORM_ANDROID)
1117 if (game_status != GAME_MODE_PLAYING &&
1118 HandleGadgets(mx, my, button))
1120 /* do not handle this button event anymore */
1121 mx = my = -32; /* force mouse event to be outside screen tiles */
1124 if (HandleGadgets(mx, my, button))
1126 /* do not handle this button event anymore */
1127 mx = my = -32; /* force mouse event to be outside screen tiles */
1131 /* do not use scroll wheel button events for anything other than gadgets */
1132 if (IS_WHEEL_BUTTON(button_nr))
1136 Error(ERR_DEBUG, "::: game_status == %d", game_status);
1139 switch (game_status)
1141 case GAME_MODE_TITLE:
1142 HandleTitleScreen(mx, my, 0, 0, button);
1145 case GAME_MODE_MAIN:
1146 HandleMainMenu(mx, my, 0, 0, button);
1149 case GAME_MODE_PSEUDO_TYPENAME:
1150 HandleTypeName(0, KSYM_Return);
1153 case GAME_MODE_LEVELS:
1154 HandleChooseLevelSet(mx, my, 0, 0, button);
1157 case GAME_MODE_LEVELNR:
1158 HandleChooseLevelNr(mx, my, 0, 0, button);
1161 case GAME_MODE_SCORES:
1162 HandleHallOfFame(0, 0, 0, 0, button);
1165 case GAME_MODE_EDITOR:
1166 HandleLevelEditorIdle();
1169 case GAME_MODE_INFO:
1170 HandleInfoScreen(mx, my, 0, 0, button);
1173 case GAME_MODE_SETUP:
1174 HandleSetupScreen(mx, my, 0, 0, button);
1177 case GAME_MODE_PLAYING:
1179 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1180 DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
1181 LEVELY((my - SY) / TILESIZE_VAR));
1182 // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1191 static boolean is_string_suffix(char *string, char *suffix)
1193 int string_len = strlen(string);
1194 int suffix_len = strlen(suffix);
1196 if (suffix_len > string_len)
1199 return (strEqual(&string[string_len - suffix_len], suffix));
1202 #define MAX_CHEAT_INPUT_LEN 32
1204 static void HandleKeysSpecial(Key key)
1206 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1207 char letter = getCharFromKey(key);
1208 int cheat_input_len = strlen(cheat_input);
1214 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1216 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1217 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1219 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1222 cheat_input[cheat_input_len++] = letter;
1223 cheat_input[cheat_input_len] = '\0';
1226 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1229 if (game_status == GAME_MODE_MAIN)
1231 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1232 is_string_suffix(cheat_input, ":ist"))
1234 InsertSolutionTape();
1236 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1237 is_string_suffix(cheat_input, ":rg"))
1239 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1242 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1243 is_string_suffix(cheat_input, ":rs"))
1245 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1248 else if (is_string_suffix(cheat_input, ":reload-music") ||
1249 is_string_suffix(cheat_input, ":rm"))
1251 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1254 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1255 is_string_suffix(cheat_input, ":ra"))
1257 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1258 1 << ARTWORK_TYPE_SOUNDS |
1259 1 << ARTWORK_TYPE_MUSIC);
1262 else if (is_string_suffix(cheat_input, ":dump-level") ||
1263 is_string_suffix(cheat_input, ":dl"))
1267 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1268 is_string_suffix(cheat_input, ":dt"))
1272 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1273 is_string_suffix(cheat_input, ":ft"))
1275 /* fix single-player tapes that contain player input for more than one
1276 player (due to a bug in 3.3.1.2 and earlier versions), which results
1277 in playing levels with more than one player in multi-player mode,
1278 even though the tape was originally recorded in single-player mode */
1280 /* remove player input actions for all players but the first one */
1281 for (i = 1; i < MAX_PLAYERS; i++)
1282 tape.player_participates[i] = FALSE;
1284 tape.changed = TRUE;
1286 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1287 is_string_suffix(cheat_input, ":snl"))
1289 SaveNativeLevel(&level);
1292 else if (game_status == GAME_MODE_PLAYING)
1295 if (is_string_suffix(cheat_input, ".q"))
1296 DEBUG_SetMaximumDynamite();
1299 else if (game_status == GAME_MODE_EDITOR)
1301 if (is_string_suffix(cheat_input, ":dump-brush") ||
1302 is_string_suffix(cheat_input, ":DB"))
1306 else if (is_string_suffix(cheat_input, ":DDB"))
1313 void HandleKey(Key key, int key_status)
1315 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1316 static struct SetupKeyboardInfo ski;
1317 static struct SetupShortcutInfo ssi;
1326 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1327 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1328 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1329 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1330 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1331 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1336 if (game_status == GAME_MODE_PLAYING)
1338 /* only needed for single-step tape recording mode */
1339 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1340 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1341 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1342 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1345 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1347 byte key_action = 0;
1349 if (setup.input[pnr].use_joystick)
1352 ski = setup.input[pnr].key;
1354 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1355 if (key == *key_info[i].key_custom)
1356 key_action |= key_info[i].action;
1358 /* use combined snap+direction keys for the first player only */
1361 ssi = setup.shortcut;
1363 for (i = 0; i < NUM_DIRECTIONS; i++)
1364 if (key == *key_info[i].key_snap)
1365 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1368 /* clear delayed snap and drop actions in single step mode (see below) */
1369 if (tape.single_step)
1371 if (clear_snap_button[pnr])
1373 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1374 clear_snap_button[pnr] = FALSE;
1377 if (clear_drop_button[pnr])
1379 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1380 clear_drop_button[pnr] = FALSE;
1384 if (key_status == KEY_PRESSED)
1385 stored_player[pnr].action |= key_action;
1387 stored_player[pnr].action &= ~key_action;
1389 if (tape.single_step && tape.recording && tape.pausing)
1391 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1393 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1395 /* if snap key already pressed, don't snap when releasing (below) */
1396 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1397 element_snapped[pnr] = TRUE;
1399 /* if drop key already pressed, don't drop when releasing (below) */
1400 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1401 element_dropped[pnr] = TRUE;
1404 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1406 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1407 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1410 printf("::: drop key pressed\n");
1413 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1414 getRedDiskReleaseFlag_SP() == 0)
1415 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1417 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1421 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1423 if (key_action & KEY_BUTTON_SNAP)
1425 /* if snap key was released without moving (see above), snap now */
1426 if (!element_snapped[pnr])
1428 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1430 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1432 /* clear delayed snap button on next event */
1433 clear_snap_button[pnr] = TRUE;
1436 element_snapped[pnr] = FALSE;
1440 if (key_action & KEY_BUTTON_DROP &&
1441 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1443 /* if drop key was released without moving (see above), drop now */
1444 if (!element_dropped[pnr])
1446 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1448 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1449 getRedDiskReleaseFlag_SP() != 0)
1450 stored_player[pnr].action |= KEY_BUTTON_DROP;
1452 /* clear delayed drop button on next event */
1453 clear_drop_button[pnr] = TRUE;
1456 element_dropped[pnr] = FALSE;
1461 else if (tape.recording && tape.pausing)
1463 /* prevent key release events from un-pausing a paused game */
1464 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1465 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1471 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1472 if (key == key_info[i].key_default)
1473 joy |= key_info[i].action;
1478 if (key_status == KEY_PRESSED)
1479 key_joystick_mapping |= joy;
1481 key_joystick_mapping &= ~joy;
1486 if (game_status != GAME_MODE_PLAYING)
1487 key_joystick_mapping = 0;
1489 if (key_status == KEY_RELEASED)
1492 if ((key == KSYM_F11 ||
1493 ((key == KSYM_Return ||
1494 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1495 video.fullscreen_available)
1497 setup.fullscreen = !setup.fullscreen;
1500 printf("::: %d\n", setup.window_scaling_percent);
1503 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1505 if (game_status == GAME_MODE_SETUP)
1506 RedrawSetupScreenAfterFullscreenToggle();
1511 if ((key == KSYM_minus ||
1514 ((GetKeyModState() & KMOD_Control) ||
1515 (GetKeyModState() & KMOD_Alt)) &&
1516 video.window_scaling_available &&
1517 !video.fullscreen_enabled)
1520 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1522 setup.window_scaling_percent +=
1523 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1525 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1526 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1527 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1528 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1530 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1532 if (game_status == GAME_MODE_SETUP)
1533 RedrawSetupScreenAfterFullscreenToggle();
1539 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1540 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1542 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1543 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1551 if (game_status == GAME_MODE_MAIN &&
1552 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1554 StartGameActions(options.network, setup.autorecord, level.random_seed);
1559 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1561 if (key == setup.shortcut.save_game)
1563 else if (key == setup.shortcut.load_game)
1565 else if (key == setup.shortcut.toggle_pause)
1566 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1568 HandleTapeButtonKeys(key);
1569 HandleSoundButtonKeys(key);
1572 if (game_status == GAME_MODE_PLAYING && !network_playing)
1574 int centered_player_nr_next = -999;
1576 if (key == setup.shortcut.focus_player_all)
1577 centered_player_nr_next = -1;
1579 for (i = 0; i < MAX_PLAYERS; i++)
1580 if (key == setup.shortcut.focus_player[i])
1581 centered_player_nr_next = i;
1583 if (centered_player_nr_next != -999)
1585 game.centered_player_nr_next = centered_player_nr_next;
1586 game.set_centered_player = TRUE;
1590 tape.centered_player_nr_next = game.centered_player_nr_next;
1591 tape.set_centered_player = TRUE;
1596 HandleKeysSpecial(key);
1598 if (HandleGadgetsKeyInput(key))
1600 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1601 key = KSYM_UNDEFINED;
1604 switch (game_status)
1606 case GAME_MODE_PSEUDO_TYPENAME:
1607 HandleTypeName(0, key);
1610 case GAME_MODE_TITLE:
1611 case GAME_MODE_MAIN:
1612 case GAME_MODE_LEVELS:
1613 case GAME_MODE_LEVELNR:
1614 case GAME_MODE_SETUP:
1615 case GAME_MODE_INFO:
1616 case GAME_MODE_SCORES:
1621 if (game_status == GAME_MODE_TITLE)
1622 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1623 else if (game_status == GAME_MODE_MAIN)
1624 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1625 else if (game_status == GAME_MODE_LEVELS)
1626 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1627 else if (game_status == GAME_MODE_LEVELNR)
1628 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1629 else if (game_status == GAME_MODE_SETUP)
1630 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1631 else if (game_status == GAME_MODE_INFO)
1632 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1633 else if (game_status == GAME_MODE_SCORES)
1634 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1638 if (game_status != GAME_MODE_MAIN)
1639 FadeSkipNextFadeIn();
1641 if (game_status == GAME_MODE_TITLE)
1642 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1643 else if (game_status == GAME_MODE_LEVELS)
1644 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1645 else if (game_status == GAME_MODE_LEVELNR)
1646 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1647 else if (game_status == GAME_MODE_SETUP)
1648 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1649 else if (game_status == GAME_MODE_INFO)
1650 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1651 else if (game_status == GAME_MODE_SCORES)
1652 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1656 if (game_status == GAME_MODE_LEVELS)
1657 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1658 else if (game_status == GAME_MODE_LEVELNR)
1659 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1660 else if (game_status == GAME_MODE_SETUP)
1661 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1662 else if (game_status == GAME_MODE_INFO)
1663 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1664 else if (game_status == GAME_MODE_SCORES)
1665 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1668 case KSYM_Page_Down:
1669 if (game_status == GAME_MODE_LEVELS)
1670 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1671 else if (game_status == GAME_MODE_LEVELNR)
1672 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1673 else if (game_status == GAME_MODE_SETUP)
1674 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1675 else if (game_status == GAME_MODE_INFO)
1676 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1677 else if (game_status == GAME_MODE_SCORES)
1678 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1683 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1687 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1688 printf("Supaplex border elements %s\n",
1689 setup.sp_show_border_elements ? "enabled" : "disabled");
1698 case GAME_MODE_EDITOR:
1699 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1700 HandleLevelEditorKeyInput(key);
1703 case GAME_MODE_PLAYING:
1708 RequestQuitGame(setup.ask_on_escape);
1726 if (GameFrameDelay == 500)
1727 GameFrameDelay = GAME_FRAME_DELAY;
1729 GameFrameDelay = 500;
1732 GameFrameDelay = (key - KSYM_0) * 10;
1733 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1734 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1740 options.debug = FALSE;
1741 printf("debug mode disabled\n");
1745 options.debug = TRUE;
1746 printf("debug mode enabled\n");
1752 if (!global.fps_slowdown)
1754 global.fps_slowdown = TRUE;
1755 global.fps_slowdown_factor = 2;
1756 printf("fps slowdown enabled -- display only every 2nd frame\n");
1758 else if (global.fps_slowdown_factor == 2)
1760 global.fps_slowdown_factor = 4;
1761 printf("fps slowdown enabled -- display only every 4th frame\n");
1765 global.fps_slowdown = FALSE;
1766 global.fps_slowdown_factor = 1;
1767 printf("fps slowdown disabled\n");
1774 ScrollStepSize = TILEX / 8;
1775 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1779 ScrollStepSize = TILEX / 4;
1780 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1784 ScrollStepSize = TILEX / 2;
1785 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1789 ScrollStepSize = TILEX;
1790 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1795 printf("::: currently using game engine version %d\n",
1796 game.engine_version);
1807 if (key == KSYM_Escape)
1809 game_status = GAME_MODE_MAIN;
1817 void HandleNoEvent()
1819 if (button_status && game_status != GAME_MODE_PLAYING)
1821 HandleButton(0, 0, -button_status, button_status);
1832 #if defined(NETWORK_AVALIABLE)
1833 if (options.network)
1837 switch (game_status)
1839 case GAME_MODE_MAIN:
1840 DrawPreviewLevelAnimation();
1844 case GAME_MODE_LEVELS:
1845 case GAME_MODE_LEVELNR:
1846 case GAME_MODE_SETUP:
1847 case GAME_MODE_INFO:
1848 case GAME_MODE_SCORES:
1852 case GAME_MODE_EDITOR:
1853 HandleLevelEditorIdle();
1861 static int HandleJoystickForAllPlayers()
1866 for (i = 0; i < MAX_PLAYERS; i++)
1868 byte joy_action = 0;
1871 if (!setup.input[i].use_joystick)
1875 joy_action = Joystick(i);
1876 result |= joy_action;
1878 if (!setup.input[i].use_joystick)
1881 stored_player[i].action = joy_action;
1887 void HandleJoystick()
1889 int joystick = HandleJoystickForAllPlayers();
1890 int keyboard = key_joystick_mapping;
1891 int joy = (joystick | keyboard);
1892 int left = joy & JOY_LEFT;
1893 int right = joy & JOY_RIGHT;
1894 int up = joy & JOY_UP;
1895 int down = joy & JOY_DOWN;
1896 int button = joy & JOY_BUTTON;
1897 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1898 int dx = (left ? -1 : right ? 1 : 0);
1899 int dy = (up ? -1 : down ? 1 : 0);
1901 switch (game_status)
1903 case GAME_MODE_TITLE:
1904 case GAME_MODE_MAIN:
1905 case GAME_MODE_LEVELS:
1906 case GAME_MODE_LEVELNR:
1907 case GAME_MODE_SETUP:
1908 case GAME_MODE_INFO:
1910 static unsigned int joystickmove_delay = 0;
1912 if (joystick && !button &&
1913 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1914 newbutton = dx = dy = 0;
1916 if (game_status == GAME_MODE_TITLE)
1917 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1918 else if (game_status == GAME_MODE_MAIN)
1919 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1920 else if (game_status == GAME_MODE_LEVELS)
1921 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1922 else if (game_status == GAME_MODE_LEVELNR)
1923 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1924 else if (game_status == GAME_MODE_SETUP)
1925 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1926 else if (game_status == GAME_MODE_INFO)
1927 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1931 case GAME_MODE_SCORES:
1932 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1936 case GAME_MODE_EDITOR:
1937 HandleLevelEditorIdle();
1941 case GAME_MODE_PLAYING:
1942 if (tape.playing || keyboard)
1943 newbutton = ((joy & JOY_BUTTON) != 0);
1946 if (newbutton && local_player->LevelSolved_GameEnd)
1948 if (newbutton && AllPlayersGone)