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);
179 case SDL_APP_WILLENTERBACKGROUND:
180 case SDL_APP_DIDENTERBACKGROUND:
181 case SDL_APP_WILLENTERFOREGROUND:
182 case SDL_APP_DIDENTERFOREGROUND:
183 HandlePauseResumeEvent((PauseResumeEvent *) &event);
188 case EVENT_KEYRELEASE:
189 HandleKeyEvent((KeyEvent *) &event);
193 HandleOtherEvents(&event);
200 /* when playing, display a special mouse pointer inside the playfield */
201 if (game_status == GAME_MODE_PLAYING && !tape.pausing)
203 if (!playfield_cursor_set && cursor_inside_playfield &&
204 DelayReached(&playfield_cursor_delay, 1000))
206 SetMouseCursor(CURSOR_PLAYFIELD);
207 playfield_cursor_set = TRUE;
210 else if (playfield_cursor_set)
212 SetMouseCursor(CURSOR_DEFAULT);
213 playfield_cursor_set = FALSE;
225 /* don't use all CPU time when idle; the main loop while playing
226 has its own synchronization and is CPU friendly, too */
228 if (game_status == GAME_MODE_PLAYING)
235 if (!PendingEvent()) /* delay only if no pending events */
239 /* refresh window contents from drawing buffer, if needed */
242 if (game_status == GAME_MODE_QUIT)
247 void HandleOtherEvents(Event *event)
252 HandleExposeEvent((ExposeEvent *) event);
255 case EVENT_UNMAPNOTIFY:
257 /* This causes the game to stop not only when iconified, but also
258 when on another virtual desktop, which might be not desired. */
259 SleepWhileUnmapped();
265 HandleFocusEvent((FocusChangeEvent *) event);
268 case EVENT_CLIENTMESSAGE:
269 HandleClientMessageEvent((ClientMessageEvent *) event);
272 #if defined(TARGET_SDL)
273 case SDL_JOYAXISMOTION:
274 case SDL_JOYBUTTONDOWN:
275 case SDL_JOYBUTTONUP:
276 HandleJoystickEvent(event);
280 HandleWindowManagerEvent(event);
289 void ClearEventQueue()
291 while (PendingEvent())
299 case EVENT_BUTTONRELEASE:
300 button_status = MB_RELEASED;
303 case EVENT_KEYRELEASE:
307 key_joystick_mapping = 0;
312 HandleOtherEvents(&event);
318 void ClearPlayerAction()
322 /* simulate key release events for still pressed keys */
323 key_joystick_mapping = 0;
324 for (i = 0; i < MAX_PLAYERS; i++)
325 stored_player[i].action = 0;
328 void SleepWhileUnmapped()
330 boolean window_unmapped = TRUE;
332 KeyboardAutoRepeatOn();
334 while (window_unmapped)
342 case EVENT_BUTTONRELEASE:
343 button_status = MB_RELEASED;
346 case EVENT_KEYRELEASE:
347 key_joystick_mapping = 0;
350 case EVENT_MAPNOTIFY:
351 window_unmapped = FALSE;
354 case EVENT_UNMAPNOTIFY:
355 /* this is only to surely prevent the 'should not happen' case
356 * of recursively looping between 'SleepWhileUnmapped()' and
357 * 'HandleOtherEvents()' which usually calls this funtion.
362 HandleOtherEvents(&event);
367 if (game_status == GAME_MODE_PLAYING)
368 KeyboardAutoRepeatOffUnlessAutoplay();
371 void HandleExposeEvent(ExposeEvent *event)
375 void HandleButtonEvent(ButtonEvent *event)
378 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
380 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
384 motion_status = FALSE;
386 if (event->type == EVENT_BUTTONPRESS)
387 button_status = event->button;
389 button_status = MB_RELEASED;
391 HandleButton(event->x, event->y, button_status, event->button);
394 void HandleMotionEvent(MotionEvent *event)
396 if (!PointerInWindow(window))
397 return; /* window and pointer are on different screens */
399 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
402 motion_status = TRUE;
405 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
406 button_status, event->x, event->y);
409 HandleButton(event->x, event->y, button_status, button_status);
412 #if defined(TARGET_SDL2)
413 void HandleWindowEvent(WindowEvent *event)
416 int subtype = event->event;
419 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
420 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
421 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
422 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
423 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
424 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
425 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
426 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
427 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
428 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
429 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
430 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
431 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
432 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
435 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
436 event_name, event->data1, event->data2);
439 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
440 event->event == SDL_WINDOWEVENT_RESIZED ||
441 event->event == SDL_WINDOWEVENT_EXPOSED)
445 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
447 // if game started in fullscreen mode, window will also get fullscreen size
448 if (!video.fullscreen_enabled && video.fullscreen_initial)
450 SDLSetWindowScaling(setup.window_scaling_percent);
452 // only do this correction once
453 video.fullscreen_initial = FALSE;
458 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
461 int new_window_width = event->data1;
462 int new_window_height = event->data2;
464 // if window size has changed after resizing, calculate new scaling factor
465 if (new_window_width != video.window_width ||
466 new_window_height != video.window_height)
468 int new_xpercent = (100 * new_window_width / video.width);
469 int new_ypercent = (100 * new_window_height / video.height);
472 printf("::: RESIZED from %d, %d to %d, %d\n",
473 video.window_width, video.window_height,
474 new_window_width, new_window_height);
477 setup.window_scaling_percent = video.window_scaling_percent =
478 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
479 MAX_WINDOW_SCALING_PERCENT);
481 video.window_width = new_window_width;
482 video.window_height = new_window_height;
485 printf("::: setup.window_scaling_percent set to %d\n",
486 setup.window_scaling_percent);
489 if (game_status == GAME_MODE_SETUP)
490 RedrawSetupScreenAfterFullscreenToggle();
495 // prevent slightly wrong scaling factor due to rounding differences
496 float scaling_factor = (float)setup.window_scaling_percent / 100;
497 int old_xsize = (int)(scaling_factor * video.width);
498 int old_ysize = (int)(scaling_factor * video.height);
499 int new_xsize = event->data1;
500 int new_ysize = event->data2;
502 // window size is unchanged when going from fullscreen to window mode,
503 // but reverse calculation of scaling factor might result in a scaling
504 // factor that is slightly different due to rounding differences;
505 // therefore compare old/new window size and not old/new scaling factor
506 if (old_xsize != new_xsize ||
507 old_ysize != new_ysize)
509 int new_xpercent = (100 * new_xsize / video.width);
510 int new_ypercent = (100 * new_ysize / video.height);
512 setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
514 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
515 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
516 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
517 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
519 printf("::: setup.window_scaling_percent set to %d\n",
520 setup.window_scaling_percent);
528 #define NUM_TOUCH_FINGERS 3
533 SDL_FingerID finger_id;
536 } touch_info[NUM_TOUCH_FINGERS];
538 void HandleFingerEvent(FingerEvent *event)
540 static Key motion_key_x = KSYM_UNDEFINED;
541 static Key motion_key_y = KSYM_UNDEFINED;
542 static Key button_key = KSYM_UNDEFINED;
543 static float motion_x1, motion_y1;
544 static float button_x1, button_y1;
545 static SDL_FingerID motion_id = -1;
546 static SDL_FingerID button_id = -1;
547 int move_trigger_distance_percent = 2; // percent of touchpad width/height
548 int drop_trigger_distance_percent = 5; // percent of touchpad width/height
549 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
550 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
551 float event_x = event->x;
552 float event_y = event->y;
556 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
557 event->type == EVENT_FINGERPRESS ? "pressed" :
558 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
562 event->dx, event->dy,
567 if (game_status != GAME_MODE_PLAYING)
571 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
573 boolean use_virtual_button_control = FALSE;
575 if (use_virtual_button_control)
578 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
581 Key key = (event->x < 1.0 / 3.0 ?
582 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
583 setup.input[0].key.drop) :
584 event->x > 2.0 / 3.0 ?
585 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
586 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
587 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
588 setup.input[0].key.right) :
591 char *key_name = (key == setup.input[0].key.snap ? "SNAP" :
592 key == setup.input[0].key.drop ? "DROP" :
593 key == setup.input[0].key.up ? "UP" :
594 key == setup.input[0].key.down ? "DOWN" :
595 key == setup.input[0].key.left ? "LEFT" :
596 key == setup.input[0].key.right ? "RIGHT" : "(unknown)");
598 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
601 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
602 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
603 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
604 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
605 setup.input[0].key.snap);
609 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
610 getKeyNameFromKey(key), key_status_name, event->fingerId);
612 // check if we already know this touch event's finger id
613 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
615 if (touch_info[i].touched &&
616 touch_info[i].finger_id == event->fingerId)
618 // Error(ERR_DEBUG, "MARK 1: %d", i);
624 if (i >= NUM_TOUCH_FINGERS)
626 if (key_status == KEY_PRESSED)
628 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
630 // unknown finger id -- get new, empty slot, if available
631 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
633 if (touch_info[i].counter < oldest_counter)
636 oldest_counter = touch_info[i].counter;
638 // Error(ERR_DEBUG, "MARK 2: %d", i);
641 if (!touch_info[i].touched)
643 // Error(ERR_DEBUG, "MARK 3: %d", i);
649 if (i >= NUM_TOUCH_FINGERS)
651 // all slots allocated -- use oldest slot
654 // Error(ERR_DEBUG, "MARK 4: %d", i);
659 // release of previously unknown key (should not happen)
661 if (key != KSYM_UNDEFINED)
663 HandleKey(key, KEY_RELEASED);
665 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
666 getKeyNameFromKey(key), "KEY_RELEASED", i);
671 if (i < NUM_TOUCH_FINGERS)
673 if (key_status == KEY_PRESSED)
675 if (touch_info[i].key != key)
677 if (touch_info[i].key != KSYM_UNDEFINED)
679 HandleKey(touch_info[i].key, KEY_RELEASED);
681 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
682 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
685 if (key != KSYM_UNDEFINED)
687 HandleKey(key, KEY_PRESSED);
689 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
690 getKeyNameFromKey(key), "KEY_PRESSED", i);
694 touch_info[i].touched = TRUE;
695 touch_info[i].finger_id = event->fingerId;
696 touch_info[i].counter = Counter();
697 touch_info[i].key = key;
701 if (touch_info[i].key != KSYM_UNDEFINED)
703 HandleKey(touch_info[i].key, KEY_RELEASED);
705 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
706 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
709 touch_info[i].touched = FALSE;
710 touch_info[i].finger_id = 0;
711 touch_info[i].counter = 0;
712 touch_info[i].key = 0;
718 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d]",
719 key_name, key_status_name, i);
721 Error(ERR_DEBUG, "=> key == %d, key_status == %d [%d]", key, key_status, i);
728 // use touch direction control
730 if (event->type == EVENT_FINGERPRESS)
732 if (event_x > 1.0 / 3.0)
736 motion_id = event->fingerId;
741 motion_key_x = KSYM_UNDEFINED;
742 motion_key_y = KSYM_UNDEFINED;
744 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
750 button_id = event->fingerId;
755 button_key = setup.input[0].key.snap;
757 HandleKey(button_key, KEY_PRESSED);
759 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
762 else if (event->type == EVENT_FINGERRELEASE)
764 if (event->fingerId == motion_id)
768 if (motion_key_x != KSYM_UNDEFINED)
769 HandleKey(motion_key_x, KEY_RELEASED);
770 if (motion_key_y != KSYM_UNDEFINED)
771 HandleKey(motion_key_y, KEY_RELEASED);
773 motion_key_x = KSYM_UNDEFINED;
774 motion_key_y = KSYM_UNDEFINED;
776 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
778 else if (event->fingerId == button_id)
782 if (button_key != KSYM_UNDEFINED)
783 HandleKey(button_key, KEY_RELEASED);
785 button_key = KSYM_UNDEFINED;
787 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
790 else if (event->type == EVENT_FINGERMOTION)
792 if (event->fingerId == motion_id)
794 float distance_x = ABS(event_x - motion_x1);
795 float distance_y = ABS(event_y - motion_y1);
796 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
797 event_x > motion_x1 ? setup.input[0].key.right :
799 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
800 event_y > motion_y1 ? setup.input[0].key.down :
803 if (distance_x < move_trigger_distance / 2 ||
804 distance_x < distance_y)
805 new_motion_key_x = KSYM_UNDEFINED;
807 if (distance_y < move_trigger_distance / 2 ||
808 distance_y < distance_x)
809 new_motion_key_y = KSYM_UNDEFINED;
811 if (distance_x > move_trigger_distance ||
812 distance_y > move_trigger_distance)
814 if (new_motion_key_x != motion_key_x)
816 if (motion_key_x != KSYM_UNDEFINED)
817 HandleKey(motion_key_x, KEY_RELEASED);
818 if (new_motion_key_x != KSYM_UNDEFINED)
819 HandleKey(new_motion_key_x, KEY_PRESSED);
822 if (new_motion_key_y != motion_key_y)
824 if (motion_key_y != KSYM_UNDEFINED)
825 HandleKey(motion_key_y, KEY_RELEASED);
826 if (new_motion_key_y != KSYM_UNDEFINED)
827 HandleKey(new_motion_key_y, KEY_PRESSED);
833 motion_key_x = new_motion_key_x;
834 motion_key_y = new_motion_key_y;
836 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
839 else if (event->fingerId == button_id)
841 float distance_x = ABS(event_x - button_x1);
842 float distance_y = ABS(event_y - button_y1);
844 if (distance_x < drop_trigger_distance / 2 &&
845 distance_y > drop_trigger_distance)
847 if (button_key == setup.input[0].key.snap)
848 HandleKey(button_key, KEY_RELEASED);
853 button_key = setup.input[0].key.drop;
855 HandleKey(button_key, KEY_PRESSED);
857 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
865 void HandleFingerEvent(FingerEvent *event)
868 static int num_events = 0;
874 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
875 event->type == EVENT_FINGERPRESS ? "pressed" :
876 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
880 event->dx, event->dy,
886 int x = (int)(event->x * video.width);
887 int y = (int)(event->y * video.height);
888 int button = MB_LEFTBUTTON;
890 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
894 if (++num_events >= max_events)
900 if (event->type == EVENT_FINGERPRESS ||
901 event->type == EVENT_FINGERMOTION)
902 button_status = button;
904 button_status = MB_RELEASED;
906 int max_x = SX + SXSIZE;
907 int max_y = SY + SYSIZE;
911 if (game_status == GAME_MODE_PLAYING)
913 if (game_status == GAME_MODE_PLAYING &&
917 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
920 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
921 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
922 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
923 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
924 setup.input[0].key.drop);
926 Key key = (y < max_y / 3 ? setup.input[0].key.up :
927 y > 2 * max_y / 3 ? setup.input[0].key.down :
928 x < max_x / 3 ? setup.input[0].key.left :
929 x > 2 * max_x / 3 ? setup.input[0].key.right :
930 setup.input[0].key.drop);
933 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
935 HandleKey(key, key_status);
940 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
941 button_status, button);
943 HandleButton(x, y, button_status, button);
951 static boolean checkTextInputKeyModState()
953 // when playing, only handle raw key events and ignore text input
954 if (game_status == GAME_MODE_PLAYING)
957 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
960 void HandleTextEvent(TextEvent *event)
962 char *text = event->text;
963 Key key = getKeyFromKeyName(text);
966 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
969 text[0], (int)(text[0]),
971 getKeyNameFromKey(key),
975 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
977 if (game_status != GAME_MODE_PLAYING &&
978 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
980 if (checkTextInputKeyModState())
982 HandleKey(key, KEY_PRESSED);
983 HandleKey(key, KEY_RELEASED);
987 void HandlePauseResumeEvent(PauseResumeEvent *event)
989 if (event->type == SDL_APP_WILLENTERBACKGROUND)
993 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1001 void HandleKeyEvent(KeyEvent *event)
1003 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1004 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1005 Key key = GetEventKey(event, with_modifiers);
1006 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1009 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1010 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1011 event->keysym.scancode,
1016 getKeyNameFromKey(key));
1020 if (key == KSYM_Menu)
1021 Error(ERR_DEBUG, "menu key pressed");
1022 else if (key == KSYM_Back)
1023 Error(ERR_DEBUG, "back key pressed");
1026 #if defined(PLATFORM_ANDROID)
1027 // always map the "back" button to the "escape" key on Android devices
1028 if (key == KSYM_Back)
1032 HandleKeyModState(keymod, key_status);
1034 #if defined(TARGET_SDL2)
1036 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
1038 if (game_status == GAME_MODE_PLAYING ||
1039 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
1041 if (!checkTextInputKeyModState())
1042 HandleKey(key, key_status);
1044 HandleKey(key, key_status);
1048 void HandleFocusEvent(FocusChangeEvent *event)
1050 static int old_joystick_status = -1;
1052 if (event->type == EVENT_FOCUSOUT)
1054 KeyboardAutoRepeatOn();
1055 old_joystick_status = joystick.status;
1056 joystick.status = JOYSTICK_NOT_AVAILABLE;
1058 ClearPlayerAction();
1060 else if (event->type == EVENT_FOCUSIN)
1062 /* When there are two Rocks'n'Diamonds windows which overlap and
1063 the player moves the pointer from one game window to the other,
1064 a 'FocusOut' event is generated for the window the pointer is
1065 leaving and a 'FocusIn' event is generated for the window the
1066 pointer is entering. In some cases, it can happen that the
1067 'FocusIn' event is handled by the one game process before the
1068 'FocusOut' event by the other game process. In this case the
1069 X11 environment would end up with activated keyboard auto repeat,
1070 because unfortunately this is a global setting and not (which
1071 would be far better) set for each X11 window individually.
1072 The effect would be keyboard auto repeat while playing the game
1073 (game_status == GAME_MODE_PLAYING), which is not desired.
1074 To avoid this special case, we just wait 1/10 second before
1075 processing the 'FocusIn' event.
1078 if (game_status == GAME_MODE_PLAYING)
1081 KeyboardAutoRepeatOffUnlessAutoplay();
1084 if (old_joystick_status != -1)
1085 joystick.status = old_joystick_status;
1089 void HandleClientMessageEvent(ClientMessageEvent *event)
1091 if (CheckCloseWindowEvent(event))
1095 void HandleWindowManagerEvent(Event *event)
1097 #if defined(TARGET_SDL)
1098 SDLHandleWindowManagerEvent(event);
1102 void HandleButton(int mx, int my, int button, int button_nr)
1104 static int old_mx = 0, old_my = 0;
1118 #if defined(PLATFORM_ANDROID)
1119 if (game_status != GAME_MODE_PLAYING &&
1120 HandleGadgets(mx, my, button))
1122 /* do not handle this button event anymore */
1123 mx = my = -32; /* force mouse event to be outside screen tiles */
1126 if (HandleGadgets(mx, my, button))
1128 /* do not handle this button event anymore */
1129 mx = my = -32; /* force mouse event to be outside screen tiles */
1133 /* do not use scroll wheel button events for anything other than gadgets */
1134 if (IS_WHEEL_BUTTON(button_nr))
1138 Error(ERR_DEBUG, "::: game_status == %d", game_status);
1141 switch (game_status)
1143 case GAME_MODE_TITLE:
1144 HandleTitleScreen(mx, my, 0, 0, button);
1147 case GAME_MODE_MAIN:
1148 HandleMainMenu(mx, my, 0, 0, button);
1151 case GAME_MODE_PSEUDO_TYPENAME:
1152 HandleTypeName(0, KSYM_Return);
1155 case GAME_MODE_LEVELS:
1156 HandleChooseLevelSet(mx, my, 0, 0, button);
1159 case GAME_MODE_LEVELNR:
1160 HandleChooseLevelNr(mx, my, 0, 0, button);
1163 case GAME_MODE_SCORES:
1164 HandleHallOfFame(0, 0, 0, 0, button);
1167 case GAME_MODE_EDITOR:
1168 HandleLevelEditorIdle();
1171 case GAME_MODE_INFO:
1172 HandleInfoScreen(mx, my, 0, 0, button);
1175 case GAME_MODE_SETUP:
1176 HandleSetupScreen(mx, my, 0, 0, button);
1179 case GAME_MODE_PLAYING:
1181 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1182 DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
1183 LEVELY((my - SY) / TILESIZE_VAR));
1184 // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1193 static boolean is_string_suffix(char *string, char *suffix)
1195 int string_len = strlen(string);
1196 int suffix_len = strlen(suffix);
1198 if (suffix_len > string_len)
1201 return (strEqual(&string[string_len - suffix_len], suffix));
1204 #define MAX_CHEAT_INPUT_LEN 32
1206 static void HandleKeysSpecial(Key key)
1208 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1209 char letter = getCharFromKey(key);
1210 int cheat_input_len = strlen(cheat_input);
1216 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1218 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1219 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1221 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1224 cheat_input[cheat_input_len++] = letter;
1225 cheat_input[cheat_input_len] = '\0';
1228 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1231 if (game_status == GAME_MODE_MAIN)
1233 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1234 is_string_suffix(cheat_input, ":ist"))
1236 InsertSolutionTape();
1238 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1239 is_string_suffix(cheat_input, ":rg"))
1241 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1244 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1245 is_string_suffix(cheat_input, ":rs"))
1247 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1250 else if (is_string_suffix(cheat_input, ":reload-music") ||
1251 is_string_suffix(cheat_input, ":rm"))
1253 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1256 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1257 is_string_suffix(cheat_input, ":ra"))
1259 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1260 1 << ARTWORK_TYPE_SOUNDS |
1261 1 << ARTWORK_TYPE_MUSIC);
1264 else if (is_string_suffix(cheat_input, ":dump-level") ||
1265 is_string_suffix(cheat_input, ":dl"))
1269 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1270 is_string_suffix(cheat_input, ":dt"))
1274 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1275 is_string_suffix(cheat_input, ":ft"))
1277 /* fix single-player tapes that contain player input for more than one
1278 player (due to a bug in 3.3.1.2 and earlier versions), which results
1279 in playing levels with more than one player in multi-player mode,
1280 even though the tape was originally recorded in single-player mode */
1282 /* remove player input actions for all players but the first one */
1283 for (i = 1; i < MAX_PLAYERS; i++)
1284 tape.player_participates[i] = FALSE;
1286 tape.changed = TRUE;
1288 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1289 is_string_suffix(cheat_input, ":snl"))
1291 SaveNativeLevel(&level);
1294 else if (game_status == GAME_MODE_PLAYING)
1297 if (is_string_suffix(cheat_input, ".q"))
1298 DEBUG_SetMaximumDynamite();
1301 else if (game_status == GAME_MODE_EDITOR)
1303 if (is_string_suffix(cheat_input, ":dump-brush") ||
1304 is_string_suffix(cheat_input, ":DB"))
1308 else if (is_string_suffix(cheat_input, ":DDB"))
1315 void HandleKey(Key key, int key_status)
1317 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1318 static struct SetupKeyboardInfo ski;
1319 static struct SetupShortcutInfo ssi;
1328 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1329 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1330 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1331 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1332 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1333 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1338 if (game_status == GAME_MODE_PLAYING)
1340 /* only needed for single-step tape recording mode */
1341 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1342 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1343 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1344 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1347 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1349 byte key_action = 0;
1351 if (setup.input[pnr].use_joystick)
1354 ski = setup.input[pnr].key;
1356 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1357 if (key == *key_info[i].key_custom)
1358 key_action |= key_info[i].action;
1360 /* use combined snap+direction keys for the first player only */
1363 ssi = setup.shortcut;
1365 for (i = 0; i < NUM_DIRECTIONS; i++)
1366 if (key == *key_info[i].key_snap)
1367 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1370 /* clear delayed snap and drop actions in single step mode (see below) */
1371 if (tape.single_step)
1373 if (clear_snap_button[pnr])
1375 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1376 clear_snap_button[pnr] = FALSE;
1379 if (clear_drop_button[pnr])
1381 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1382 clear_drop_button[pnr] = FALSE;
1386 if (key_status == KEY_PRESSED)
1387 stored_player[pnr].action |= key_action;
1389 stored_player[pnr].action &= ~key_action;
1391 if (tape.single_step && tape.recording && tape.pausing)
1393 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1395 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1397 /* if snap key already pressed, don't snap when releasing (below) */
1398 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1399 element_snapped[pnr] = TRUE;
1401 /* if drop key already pressed, don't drop when releasing (below) */
1402 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1403 element_dropped[pnr] = TRUE;
1406 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1408 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1409 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1412 printf("::: drop key pressed\n");
1415 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1416 getRedDiskReleaseFlag_SP() == 0)
1417 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1419 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1423 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1425 if (key_action & KEY_BUTTON_SNAP)
1427 /* if snap key was released without moving (see above), snap now */
1428 if (!element_snapped[pnr])
1430 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1432 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1434 /* clear delayed snap button on next event */
1435 clear_snap_button[pnr] = TRUE;
1438 element_snapped[pnr] = FALSE;
1442 if (key_action & KEY_BUTTON_DROP &&
1443 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1445 /* if drop key was released without moving (see above), drop now */
1446 if (!element_dropped[pnr])
1448 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1450 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1451 getRedDiskReleaseFlag_SP() != 0)
1452 stored_player[pnr].action |= KEY_BUTTON_DROP;
1454 /* clear delayed drop button on next event */
1455 clear_drop_button[pnr] = TRUE;
1458 element_dropped[pnr] = FALSE;
1463 else if (tape.recording && tape.pausing)
1465 /* prevent key release events from un-pausing a paused game */
1466 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1467 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1473 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1474 if (key == key_info[i].key_default)
1475 joy |= key_info[i].action;
1480 if (key_status == KEY_PRESSED)
1481 key_joystick_mapping |= joy;
1483 key_joystick_mapping &= ~joy;
1488 if (game_status != GAME_MODE_PLAYING)
1489 key_joystick_mapping = 0;
1491 if (key_status == KEY_RELEASED)
1494 if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1495 (GetKeyModState() & KMOD_Alt) && 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 || key == KSYM_plus || key == KSYM_0) &&
1512 (GetKeyModState() & KMOD_Alt) && video.window_scaling_available &&
1513 !video.fullscreen_enabled)
1516 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1518 setup.window_scaling_percent +=
1519 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1521 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1522 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1523 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1524 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1526 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1528 if (game_status == GAME_MODE_SETUP)
1529 RedrawSetupScreenAfterFullscreenToggle();
1535 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1536 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1538 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1539 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1547 if (game_status == GAME_MODE_MAIN &&
1548 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1550 StartGameActions(options.network, setup.autorecord, level.random_seed);
1555 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1557 if (key == setup.shortcut.save_game)
1559 else if (key == setup.shortcut.load_game)
1561 else if (key == setup.shortcut.toggle_pause)
1562 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1564 HandleTapeButtonKeys(key);
1565 HandleSoundButtonKeys(key);
1568 if (game_status == GAME_MODE_PLAYING && !network_playing)
1570 int centered_player_nr_next = -999;
1572 if (key == setup.shortcut.focus_player_all)
1573 centered_player_nr_next = -1;
1575 for (i = 0; i < MAX_PLAYERS; i++)
1576 if (key == setup.shortcut.focus_player[i])
1577 centered_player_nr_next = i;
1579 if (centered_player_nr_next != -999)
1581 game.centered_player_nr_next = centered_player_nr_next;
1582 game.set_centered_player = TRUE;
1586 tape.centered_player_nr_next = game.centered_player_nr_next;
1587 tape.set_centered_player = TRUE;
1592 HandleKeysSpecial(key);
1594 if (HandleGadgetsKeyInput(key))
1596 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1597 key = KSYM_UNDEFINED;
1600 switch (game_status)
1602 case GAME_MODE_PSEUDO_TYPENAME:
1603 HandleTypeName(0, key);
1606 case GAME_MODE_TITLE:
1607 case GAME_MODE_MAIN:
1608 case GAME_MODE_LEVELS:
1609 case GAME_MODE_LEVELNR:
1610 case GAME_MODE_SETUP:
1611 case GAME_MODE_INFO:
1612 case GAME_MODE_SCORES:
1617 if (game_status == GAME_MODE_TITLE)
1618 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1619 else if (game_status == GAME_MODE_MAIN)
1620 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1621 else if (game_status == GAME_MODE_LEVELS)
1622 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1623 else if (game_status == GAME_MODE_LEVELNR)
1624 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1625 else if (game_status == GAME_MODE_SETUP)
1626 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1627 else if (game_status == GAME_MODE_INFO)
1628 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1629 else if (game_status == GAME_MODE_SCORES)
1630 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1634 if (game_status != GAME_MODE_MAIN)
1635 FadeSkipNextFadeIn();
1637 if (game_status == GAME_MODE_TITLE)
1638 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1639 else if (game_status == GAME_MODE_LEVELS)
1640 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1641 else if (game_status == GAME_MODE_LEVELNR)
1642 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1643 else if (game_status == GAME_MODE_SETUP)
1644 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1645 else if (game_status == GAME_MODE_INFO)
1646 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1647 else if (game_status == GAME_MODE_SCORES)
1648 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1652 if (game_status == GAME_MODE_LEVELS)
1653 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1654 else if (game_status == GAME_MODE_LEVELNR)
1655 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1656 else if (game_status == GAME_MODE_SETUP)
1657 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1658 else if (game_status == GAME_MODE_INFO)
1659 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1660 else if (game_status == GAME_MODE_SCORES)
1661 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1664 case KSYM_Page_Down:
1665 if (game_status == GAME_MODE_LEVELS)
1666 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1667 else if (game_status == GAME_MODE_LEVELNR)
1668 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1669 else if (game_status == GAME_MODE_SETUP)
1670 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1671 else if (game_status == GAME_MODE_INFO)
1672 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1673 else if (game_status == GAME_MODE_SCORES)
1674 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1679 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1683 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1684 printf("Supaplex border elements %s\n",
1685 setup.sp_show_border_elements ? "enabled" : "disabled");
1694 case GAME_MODE_EDITOR:
1695 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1696 HandleLevelEditorKeyInput(key);
1699 case GAME_MODE_PLAYING:
1704 RequestQuitGame(setup.ask_on_escape);
1722 if (GameFrameDelay == 500)
1723 GameFrameDelay = GAME_FRAME_DELAY;
1725 GameFrameDelay = 500;
1728 GameFrameDelay = (key - KSYM_0) * 10;
1729 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1730 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1736 options.debug = FALSE;
1737 printf("debug mode disabled\n");
1741 options.debug = TRUE;
1742 printf("debug mode enabled\n");
1748 if (!global.fps_slowdown)
1750 global.fps_slowdown = TRUE;
1751 global.fps_slowdown_factor = 2;
1752 printf("fps slowdown enabled -- display only every 2nd frame\n");
1754 else if (global.fps_slowdown_factor == 2)
1756 global.fps_slowdown_factor = 4;
1757 printf("fps slowdown enabled -- display only every 4th frame\n");
1761 global.fps_slowdown = FALSE;
1762 global.fps_slowdown_factor = 1;
1763 printf("fps slowdown disabled\n");
1770 ScrollStepSize = TILEX / 8;
1771 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1775 ScrollStepSize = TILEX / 4;
1776 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1780 ScrollStepSize = TILEX / 2;
1781 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1785 ScrollStepSize = TILEX;
1786 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1791 printf("::: currently using game engine version %d\n",
1792 game.engine_version);
1803 if (key == KSYM_Escape)
1805 game_status = GAME_MODE_MAIN;
1813 void HandleNoEvent()
1815 if (button_status && game_status != GAME_MODE_PLAYING)
1817 HandleButton(0, 0, -button_status, button_status);
1828 #if defined(NETWORK_AVALIABLE)
1829 if (options.network)
1833 switch (game_status)
1835 case GAME_MODE_MAIN:
1836 DrawPreviewLevelAnimation();
1840 case GAME_MODE_LEVELS:
1841 case GAME_MODE_LEVELNR:
1842 case GAME_MODE_SETUP:
1843 case GAME_MODE_INFO:
1844 case GAME_MODE_SCORES:
1848 case GAME_MODE_EDITOR:
1849 HandleLevelEditorIdle();
1857 static int HandleJoystickForAllPlayers()
1862 for (i = 0; i < MAX_PLAYERS; i++)
1864 byte joy_action = 0;
1867 if (!setup.input[i].use_joystick)
1871 joy_action = Joystick(i);
1872 result |= joy_action;
1874 if (!setup.input[i].use_joystick)
1877 stored_player[i].action = joy_action;
1883 void HandleJoystick()
1885 int joystick = HandleJoystickForAllPlayers();
1886 int keyboard = key_joystick_mapping;
1887 int joy = (joystick | keyboard);
1888 int left = joy & JOY_LEFT;
1889 int right = joy & JOY_RIGHT;
1890 int up = joy & JOY_UP;
1891 int down = joy & JOY_DOWN;
1892 int button = joy & JOY_BUTTON;
1893 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1894 int dx = (left ? -1 : right ? 1 : 0);
1895 int dy = (up ? -1 : down ? 1 : 0);
1897 switch (game_status)
1899 case GAME_MODE_TITLE:
1900 case GAME_MODE_MAIN:
1901 case GAME_MODE_LEVELS:
1902 case GAME_MODE_LEVELNR:
1903 case GAME_MODE_SETUP:
1904 case GAME_MODE_INFO:
1906 static unsigned int joystickmove_delay = 0;
1908 if (joystick && !button &&
1909 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1910 newbutton = dx = dy = 0;
1912 if (game_status == GAME_MODE_TITLE)
1913 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1914 else if (game_status == GAME_MODE_MAIN)
1915 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1916 else if (game_status == GAME_MODE_LEVELS)
1917 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1918 else if (game_status == GAME_MODE_LEVELNR)
1919 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1920 else if (game_status == GAME_MODE_SETUP)
1921 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1922 else if (game_status == GAME_MODE_INFO)
1923 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1927 case GAME_MODE_SCORES:
1928 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1932 case GAME_MODE_EDITOR:
1933 HandleLevelEditorIdle();
1937 case GAME_MODE_PLAYING:
1938 if (tape.playing || keyboard)
1939 newbutton = ((joy & JOY_BUTTON) != 0);
1942 if (newbutton && local_player->LevelSolved_GameEnd)
1944 if (newbutton && AllPlayersGone)