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
27 #define DEBUG_EVENTS_BUTTON (DEBUG_EVENTS * 0)
28 #define DEBUG_EVENTS_MOTION (DEBUG_EVENTS * 0)
29 #define DEBUG_EVENTS_WINDOW (DEBUG_EVENTS * 0)
30 #define DEBUG_EVENTS_FINGER (DEBUG_EVENTS * 0)
31 #define DEBUG_EVENTS_TEXT (DEBUG_EVENTS * 1)
32 #define DEBUG_EVENTS_KEY (DEBUG_EVENTS * 1)
35 static boolean cursor_inside_playfield = FALSE;
36 static int cursor_mode_last = CURSOR_DEFAULT;
37 static unsigned int special_cursor_delay = 0;
38 static unsigned int special_cursor_delay_value = 1000;
40 /* event filter especially needed for SDL event filtering due to
41 delay problems with lots of mouse motion events when mouse button
42 not pressed (X11 can handle this with 'PointerMotionHintMask') */
44 /* event filter addition for SDL2: as SDL2 does not have a function to enable
45 or disable keyboard auto-repeat, filter repeated keyboard events instead */
47 static int FilterEventsExt(const Event *event)
51 #if defined(TARGET_SDL2)
52 /* skip repeated key press events if keyboard auto-repeat is disabled */
53 if (event->type == EVENT_KEYPRESS &&
59 /* non-motion events are directly passed to event handler functions */
60 if (event->type != EVENT_MOTIONNOTIFY)
63 motion = (MotionEvent *)event;
64 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
65 motion->y >= SY && motion->y < SY + SYSIZE);
67 /* do no reset mouse cursor before all pending events have been processed */
68 if (gfx.cursor_mode == cursor_mode_last &&
69 ((effectiveGameStatus() == GAME_MODE_TITLE &&
70 gfx.cursor_mode == CURSOR_NONE) ||
71 (game_status == GAME_MODE_PLAYING &&
72 gfx.cursor_mode == CURSOR_PLAYFIELD)))
74 SetMouseCursor(CURSOR_DEFAULT);
76 DelayReached(&special_cursor_delay, 0);
78 cursor_mode_last = CURSOR_DEFAULT;
81 /* skip mouse motion events without pressed button outside level editor */
82 if (button_status == MB_RELEASED &&
83 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
89 #if defined(TARGET_SDL2)
90 int FilterEvents(void *userdata, Event *event)
92 return FilterEventsExt(event);
95 int FilterEvents(const Event *event)
97 return FilterEventsExt(event);
101 /* to prevent delay problems, skip mouse motion events if the very next
102 event is also a mouse motion event (and therefore effectively only
103 handling the last of a row of mouse motion events in the event queue) */
105 boolean SkipPressedMouseMotionEvent(const Event *event)
107 /* nothing to do if the current event is not a mouse motion event */
108 if (event->type != EVENT_MOTIONNOTIFY)
111 /* only skip motion events with pressed button outside the game */
112 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
119 PeekEvent(&next_event);
121 /* if next event is also a mouse motion event, skip the current one */
122 if (next_event.type == EVENT_MOTIONNOTIFY)
129 /* this is only really needed for non-SDL targets to filter unwanted events;
130 when using SDL with properly installed event filter, this function can be
131 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
133 boolean NextValidEvent(Event *event)
135 while (PendingEvent())
137 boolean handle_this_event = FALSE;
141 if (FilterEventsExt(event))
142 handle_this_event = TRUE;
144 if (SkipPressedMouseMotionEvent(event))
145 handle_this_event = FALSE;
147 if (handle_this_event)
158 if (PendingEvent()) /* got event */
162 while (NextValidEvent(&event))
166 case EVENT_BUTTONPRESS:
167 case EVENT_BUTTONRELEASE:
168 HandleButtonEvent((ButtonEvent *) &event);
171 case EVENT_MOTIONNOTIFY:
172 HandleMotionEvent((MotionEvent *) &event);
175 #if defined(TARGET_SDL2)
176 case SDL_WINDOWEVENT:
177 HandleWindowEvent((WindowEvent *) &event);
180 case EVENT_FINGERPRESS:
181 case EVENT_FINGERRELEASE:
182 case EVENT_FINGERMOTION:
183 HandleFingerEvent((FingerEvent *) &event);
186 case EVENT_TEXTINPUT:
187 HandleTextEvent((TextEvent *) &event);
190 case SDL_APP_WILLENTERBACKGROUND:
191 case SDL_APP_DIDENTERBACKGROUND:
192 case SDL_APP_WILLENTERFOREGROUND:
193 case SDL_APP_DIDENTERFOREGROUND:
194 HandlePauseResumeEvent((PauseResumeEvent *) &event);
199 case EVENT_KEYRELEASE:
200 HandleKeyEvent((KeyEvent *) &event);
204 HandleOtherEvents(&event);
211 if (effectiveGameStatus() == GAME_MODE_TITLE)
213 /* when showing title screens, hide mouse pointer (if not moved) */
215 if (gfx.cursor_mode != CURSOR_NONE &&
216 DelayReached(&special_cursor_delay, special_cursor_delay_value))
218 SetMouseCursor(CURSOR_NONE);
221 else if (game_status == GAME_MODE_PLAYING && !tape.pausing)
223 /* when playing, display a special mouse pointer inside the playfield */
225 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
226 cursor_inside_playfield &&
227 DelayReached(&special_cursor_delay, special_cursor_delay_value))
229 SetMouseCursor(CURSOR_PLAYFIELD);
232 else if (gfx.cursor_mode != CURSOR_DEFAULT)
234 SetMouseCursor(CURSOR_DEFAULT);
237 /* this is set after all pending events have been processed */
238 cursor_mode_last = gfx.cursor_mode;
241 /* also execute after pending events have been processed before */
244 /* don't use all CPU time when idle; the main loop while playing
245 has its own synchronization and is CPU friendly, too */
247 if (game_status == GAME_MODE_PLAYING)
253 if (!PendingEvent()) /* delay only if no pending events */
257 /* refresh window contents from drawing buffer, if needed */
260 if (game_status == GAME_MODE_QUIT)
265 void HandleOtherEvents(Event *event)
270 HandleExposeEvent((ExposeEvent *) event);
273 case EVENT_UNMAPNOTIFY:
275 /* This causes the game to stop not only when iconified, but also
276 when on another virtual desktop, which might be not desired. */
277 SleepWhileUnmapped();
283 HandleFocusEvent((FocusChangeEvent *) event);
286 case EVENT_CLIENTMESSAGE:
287 HandleClientMessageEvent((ClientMessageEvent *) event);
290 #if defined(TARGET_SDL)
291 case SDL_JOYAXISMOTION:
292 case SDL_JOYBUTTONDOWN:
293 case SDL_JOYBUTTONUP:
294 HandleJoystickEvent(event);
298 HandleWindowManagerEvent(event);
307 void ClearEventQueue()
309 while (PendingEvent())
317 case EVENT_BUTTONRELEASE:
318 button_status = MB_RELEASED;
321 case EVENT_KEYRELEASE:
326 HandleOtherEvents(&event);
332 void ClearPlayerAction()
336 /* simulate key release events for still pressed keys */
337 key_joystick_mapping = 0;
338 for (i = 0; i < MAX_PLAYERS; i++)
339 stored_player[i].action = 0;
342 void SleepWhileUnmapped()
344 boolean window_unmapped = TRUE;
346 KeyboardAutoRepeatOn();
348 while (window_unmapped)
356 case EVENT_BUTTONRELEASE:
357 button_status = MB_RELEASED;
360 case EVENT_KEYRELEASE:
361 key_joystick_mapping = 0;
364 case EVENT_MAPNOTIFY:
365 window_unmapped = FALSE;
368 case EVENT_UNMAPNOTIFY:
369 /* this is only to surely prevent the 'should not happen' case
370 * of recursively looping between 'SleepWhileUnmapped()' and
371 * 'HandleOtherEvents()' which usually calls this funtion.
376 HandleOtherEvents(&event);
381 if (game_status == GAME_MODE_PLAYING)
382 KeyboardAutoRepeatOffUnlessAutoplay();
385 void HandleExposeEvent(ExposeEvent *event)
389 void HandleButtonEvent(ButtonEvent *event)
391 #if DEBUG_EVENTS_BUTTON
392 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
394 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
398 motion_status = FALSE;
400 if (event->type == EVENT_BUTTONPRESS)
401 button_status = event->button;
403 button_status = MB_RELEASED;
405 HandleButton(event->x, event->y, button_status, event->button);
408 void HandleMotionEvent(MotionEvent *event)
410 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
413 motion_status = TRUE;
415 #if DEBUG_EVENTS_MOTION
416 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
417 button_status, event->x, event->y);
420 HandleButton(event->x, event->y, button_status, button_status);
423 #if defined(TARGET_SDL2)
425 void HandleWindowEvent(WindowEvent *event)
427 #if DEBUG_EVENTS_WINDOW
428 int subtype = event->event;
431 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
432 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
433 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
434 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
435 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
436 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
437 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
438 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
439 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
440 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
441 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
442 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
443 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
444 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
447 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
448 event_name, event->data1, event->data2);
451 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
452 event->event == SDL_WINDOWEVENT_RESIZED ||
453 event->event == SDL_WINDOWEVENT_EXPOSED)
456 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);
468 setup.window_scaling_percent = video.window_scaling_percent =
469 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
470 MAX_WINDOW_SCALING_PERCENT);
472 video.window_width = new_window_width;
473 video.window_height = new_window_height;
475 if (game_status == GAME_MODE_SETUP)
476 RedrawSetupScreenAfterFullscreenToggle();
483 #define NUM_TOUCH_FINGERS 3
488 SDL_FingerID finger_id;
491 } touch_info[NUM_TOUCH_FINGERS];
493 void HandleFingerEvent(FingerEvent *event)
495 static Key motion_key_x = KSYM_UNDEFINED;
496 static Key motion_key_y = KSYM_UNDEFINED;
497 static Key button_key = KSYM_UNDEFINED;
498 static float motion_x1, motion_y1;
499 static float button_x1, button_y1;
500 static SDL_FingerID motion_id = -1;
501 static SDL_FingerID button_id = -1;
502 int move_trigger_distance_percent = 2; // percent of touchpad width/height
503 int drop_trigger_distance_percent = 5; // percent of touchpad width/height
504 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
505 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
506 float event_x = event->x;
507 float event_y = event->y;
509 #if DEBUG_EVENTS_FINGER
510 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
511 event->type == EVENT_FINGERPRESS ? "pressed" :
512 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
516 event->dx, event->dy,
520 if (game_status != GAME_MODE_PLAYING)
523 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
525 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
527 Key key = (event->x < 1.0 / 3.0 ?
528 (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
529 setup.input[0].key.drop) :
530 event->x > 2.0 / 3.0 ?
531 (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
532 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
533 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
534 setup.input[0].key.right) :
536 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
540 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
541 getKeyNameFromKey(key), key_status_name, event->fingerId);
543 // check if we already know this touch event's finger id
544 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
546 if (touch_info[i].touched &&
547 touch_info[i].finger_id == event->fingerId)
549 // Error(ERR_DEBUG, "MARK 1: %d", i);
555 if (i >= NUM_TOUCH_FINGERS)
557 if (key_status == KEY_PRESSED)
559 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
561 // unknown finger id -- get new, empty slot, if available
562 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
564 if (touch_info[i].counter < oldest_counter)
567 oldest_counter = touch_info[i].counter;
569 // Error(ERR_DEBUG, "MARK 2: %d", i);
572 if (!touch_info[i].touched)
574 // Error(ERR_DEBUG, "MARK 3: %d", i);
580 if (i >= NUM_TOUCH_FINGERS)
582 // all slots allocated -- use oldest slot
585 // Error(ERR_DEBUG, "MARK 4: %d", i);
590 // release of previously unknown key (should not happen)
592 if (key != KSYM_UNDEFINED)
594 HandleKey(key, KEY_RELEASED);
596 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
597 getKeyNameFromKey(key), "KEY_RELEASED", i);
602 if (i < NUM_TOUCH_FINGERS)
604 if (key_status == KEY_PRESSED)
606 if (touch_info[i].key != key)
608 if (touch_info[i].key != KSYM_UNDEFINED)
610 HandleKey(touch_info[i].key, KEY_RELEASED);
612 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
613 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
616 if (key != KSYM_UNDEFINED)
618 HandleKey(key, KEY_PRESSED);
620 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
621 getKeyNameFromKey(key), "KEY_PRESSED", i);
625 touch_info[i].touched = TRUE;
626 touch_info[i].finger_id = event->fingerId;
627 touch_info[i].counter = Counter();
628 touch_info[i].key = key;
632 if (touch_info[i].key != KSYM_UNDEFINED)
634 HandleKey(touch_info[i].key, KEY_RELEASED);
636 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
637 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
640 touch_info[i].touched = FALSE;
641 touch_info[i].finger_id = 0;
642 touch_info[i].counter = 0;
643 touch_info[i].key = 0;
650 // use touch direction control
652 if (event->type == EVENT_FINGERPRESS)
654 if (event_x > 1.0 / 3.0)
658 motion_id = event->fingerId;
663 motion_key_x = KSYM_UNDEFINED;
664 motion_key_y = KSYM_UNDEFINED;
666 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
672 button_id = event->fingerId;
677 button_key = setup.input[0].key.snap;
679 HandleKey(button_key, KEY_PRESSED);
681 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
684 else if (event->type == EVENT_FINGERRELEASE)
686 if (event->fingerId == motion_id)
690 if (motion_key_x != KSYM_UNDEFINED)
691 HandleKey(motion_key_x, KEY_RELEASED);
692 if (motion_key_y != KSYM_UNDEFINED)
693 HandleKey(motion_key_y, KEY_RELEASED);
695 motion_key_x = KSYM_UNDEFINED;
696 motion_key_y = KSYM_UNDEFINED;
698 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
700 else if (event->fingerId == button_id)
704 if (button_key != KSYM_UNDEFINED)
705 HandleKey(button_key, KEY_RELEASED);
707 button_key = KSYM_UNDEFINED;
709 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
712 else if (event->type == EVENT_FINGERMOTION)
714 if (event->fingerId == motion_id)
716 float distance_x = ABS(event_x - motion_x1);
717 float distance_y = ABS(event_y - motion_y1);
718 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
719 event_x > motion_x1 ? setup.input[0].key.right :
721 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
722 event_y > motion_y1 ? setup.input[0].key.down :
725 if (distance_x < move_trigger_distance / 2 ||
726 distance_x < distance_y)
727 new_motion_key_x = KSYM_UNDEFINED;
729 if (distance_y < move_trigger_distance / 2 ||
730 distance_y < distance_x)
731 new_motion_key_y = KSYM_UNDEFINED;
733 if (distance_x > move_trigger_distance ||
734 distance_y > move_trigger_distance)
736 if (new_motion_key_x != motion_key_x)
738 if (motion_key_x != KSYM_UNDEFINED)
739 HandleKey(motion_key_x, KEY_RELEASED);
740 if (new_motion_key_x != KSYM_UNDEFINED)
741 HandleKey(new_motion_key_x, KEY_PRESSED);
744 if (new_motion_key_y != motion_key_y)
746 if (motion_key_y != KSYM_UNDEFINED)
747 HandleKey(motion_key_y, KEY_RELEASED);
748 if (new_motion_key_y != KSYM_UNDEFINED)
749 HandleKey(new_motion_key_y, KEY_PRESSED);
755 motion_key_x = new_motion_key_x;
756 motion_key_y = new_motion_key_y;
758 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
761 else if (event->fingerId == button_id)
763 float distance_x = ABS(event_x - button_x1);
764 float distance_y = ABS(event_y - button_y1);
766 if (distance_x < drop_trigger_distance / 2 &&
767 distance_y > drop_trigger_distance)
769 if (button_key == setup.input[0].key.snap)
770 HandleKey(button_key, KEY_RELEASED);
775 button_key = setup.input[0].key.drop;
777 HandleKey(button_key, KEY_PRESSED);
779 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
785 static boolean checkTextInputKeyModState()
787 // when playing, only handle raw key events and ignore text input
788 if (game_status == GAME_MODE_PLAYING)
791 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
794 void HandleTextEvent(TextEvent *event)
796 char *text = event->text;
797 Key key = getKeyFromKeyName(text);
799 #if DEBUG_EVENTS_TEXT
800 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
803 text[0], (int)(text[0]),
805 getKeyNameFromKey(key),
809 #if defined(PLATFORM_ANDROID)
810 if (game_status == GAME_MODE_PSEUDO_TYPENAME)
812 HandleTypeName(0, key);
818 // only handle key input with text modifier keys pressed
819 if (checkTextInputKeyModState())
821 HandleKey(key, KEY_PRESSED);
822 HandleKey(key, KEY_RELEASED);
826 void HandlePauseResumeEvent(PauseResumeEvent *event)
828 if (event->type == SDL_APP_WILLENTERBACKGROUND)
832 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
840 void HandleKeyEvent(KeyEvent *event)
842 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
843 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
844 Key key = GetEventKey(event, with_modifiers);
845 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
848 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
849 event->type == EVENT_KEYPRESS ? "pressed" : "released",
850 event->keysym.scancode,
855 getKeyNameFromKey(key));
858 #if defined(PLATFORM_ANDROID)
859 // always map the "back" button to the "escape" key on Android devices
860 if (key == KSYM_Back)
864 HandleKeyModState(keymod, key_status);
866 #if defined(TARGET_SDL2)
867 // only handle raw key input without text modifier keys pressed
868 if (!checkTextInputKeyModState())
869 HandleKey(key, key_status);
871 HandleKey(key, key_status);
875 void HandleFocusEvent(FocusChangeEvent *event)
877 static int old_joystick_status = -1;
879 if (event->type == EVENT_FOCUSOUT)
881 KeyboardAutoRepeatOn();
882 old_joystick_status = joystick.status;
883 joystick.status = JOYSTICK_NOT_AVAILABLE;
887 else if (event->type == EVENT_FOCUSIN)
889 /* When there are two Rocks'n'Diamonds windows which overlap and
890 the player moves the pointer from one game window to the other,
891 a 'FocusOut' event is generated for the window the pointer is
892 leaving and a 'FocusIn' event is generated for the window the
893 pointer is entering. In some cases, it can happen that the
894 'FocusIn' event is handled by the one game process before the
895 'FocusOut' event by the other game process. In this case the
896 X11 environment would end up with activated keyboard auto repeat,
897 because unfortunately this is a global setting and not (which
898 would be far better) set for each X11 window individually.
899 The effect would be keyboard auto repeat while playing the game
900 (game_status == GAME_MODE_PLAYING), which is not desired.
901 To avoid this special case, we just wait 1/10 second before
902 processing the 'FocusIn' event.
905 if (game_status == GAME_MODE_PLAYING)
908 KeyboardAutoRepeatOffUnlessAutoplay();
911 if (old_joystick_status != -1)
912 joystick.status = old_joystick_status;
916 void HandleClientMessageEvent(ClientMessageEvent *event)
918 if (CheckCloseWindowEvent(event))
922 void HandleWindowManagerEvent(Event *event)
924 #if defined(TARGET_SDL)
925 SDLHandleWindowManagerEvent(event);
929 void HandleButton(int mx, int my, int button, int button_nr)
931 static int old_mx = 0, old_my = 0;
932 boolean button_hold = FALSE;
947 #if defined(PLATFORM_ANDROID)
948 // !!! for now, do not handle gadgets when playing -- maybe fix this !!!
949 if (game_status != GAME_MODE_PLAYING &&
950 HandleGadgets(mx, my, button))
952 /* do not handle this button event anymore */
953 mx = my = -32; /* force mouse event to be outside screen tiles */
956 if (HandleGadgets(mx, my, button))
958 /* do not handle this button event anymore */
959 mx = my = -32; /* force mouse event to be outside screen tiles */
963 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
966 /* do not use scroll wheel button events for anything other than gadgets */
967 if (IS_WHEEL_BUTTON(button_nr))
972 case GAME_MODE_TITLE:
973 HandleTitleScreen(mx, my, 0, 0, button);
977 HandleMainMenu(mx, my, 0, 0, button);
980 case GAME_MODE_PSEUDO_TYPENAME:
981 HandleTypeName(0, KSYM_Return);
984 case GAME_MODE_LEVELS:
985 HandleChooseLevelSet(mx, my, 0, 0, button);
988 case GAME_MODE_LEVELNR:
989 HandleChooseLevelNr(mx, my, 0, 0, button);
992 case GAME_MODE_SCORES:
993 HandleHallOfFame(0, 0, 0, 0, button);
996 case GAME_MODE_EDITOR:
997 HandleLevelEditorIdle();
1000 case GAME_MODE_INFO:
1001 HandleInfoScreen(mx, my, 0, 0, button);
1004 case GAME_MODE_SETUP:
1005 HandleSetupScreen(mx, my, 0, 0, button);
1008 case GAME_MODE_PLAYING:
1010 if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1011 DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
1012 LEVELY((my - SY) / TILESIZE_VAR));
1013 // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1022 static boolean is_string_suffix(char *string, char *suffix)
1024 int string_len = strlen(string);
1025 int suffix_len = strlen(suffix);
1027 if (suffix_len > string_len)
1030 return (strEqual(&string[string_len - suffix_len], suffix));
1033 #define MAX_CHEAT_INPUT_LEN 32
1035 static void HandleKeysSpecial(Key key)
1037 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1038 char letter = getCharFromKey(key);
1039 int cheat_input_len = strlen(cheat_input);
1045 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1047 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1048 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1050 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1053 cheat_input[cheat_input_len++] = letter;
1054 cheat_input[cheat_input_len] = '\0';
1056 #if DEBUG_EVENTS_KEY
1057 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1060 if (game_status == GAME_MODE_MAIN)
1062 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1063 is_string_suffix(cheat_input, ":ist"))
1065 InsertSolutionTape();
1067 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1068 is_string_suffix(cheat_input, ":rg"))
1070 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1073 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1074 is_string_suffix(cheat_input, ":rs"))
1076 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1079 else if (is_string_suffix(cheat_input, ":reload-music") ||
1080 is_string_suffix(cheat_input, ":rm"))
1082 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1085 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1086 is_string_suffix(cheat_input, ":ra"))
1088 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1089 1 << ARTWORK_TYPE_SOUNDS |
1090 1 << ARTWORK_TYPE_MUSIC);
1093 else if (is_string_suffix(cheat_input, ":dump-level") ||
1094 is_string_suffix(cheat_input, ":dl"))
1098 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1099 is_string_suffix(cheat_input, ":dt"))
1103 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1104 is_string_suffix(cheat_input, ":ft"))
1106 /* fix single-player tapes that contain player input for more than one
1107 player (due to a bug in 3.3.1.2 and earlier versions), which results
1108 in playing levels with more than one player in multi-player mode,
1109 even though the tape was originally recorded in single-player mode */
1111 /* remove player input actions for all players but the first one */
1112 for (i = 1; i < MAX_PLAYERS; i++)
1113 tape.player_participates[i] = FALSE;
1115 tape.changed = TRUE;
1117 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1118 is_string_suffix(cheat_input, ":snl"))
1120 SaveNativeLevel(&level);
1123 else if (game_status == GAME_MODE_PLAYING)
1126 if (is_string_suffix(cheat_input, ".q"))
1127 DEBUG_SetMaximumDynamite();
1130 else if (game_status == GAME_MODE_EDITOR)
1132 if (is_string_suffix(cheat_input, ":dump-brush") ||
1133 is_string_suffix(cheat_input, ":DB"))
1137 else if (is_string_suffix(cheat_input, ":DDB"))
1144 void HandleKey(Key key, int key_status)
1146 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1147 static struct SetupKeyboardInfo ski;
1148 static struct SetupShortcutInfo ssi;
1157 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1158 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1159 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1160 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1161 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1162 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1167 if (game_status == GAME_MODE_PLAYING)
1169 /* only needed for single-step tape recording mode */
1170 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1171 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1172 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1173 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1176 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1178 byte key_action = 0;
1180 if (setup.input[pnr].use_joystick)
1183 ski = setup.input[pnr].key;
1185 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1186 if (key == *key_info[i].key_custom)
1187 key_action |= key_info[i].action;
1189 /* use combined snap+direction keys for the first player only */
1192 ssi = setup.shortcut;
1194 for (i = 0; i < NUM_DIRECTIONS; i++)
1195 if (key == *key_info[i].key_snap)
1196 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1199 /* clear delayed snap and drop actions in single step mode (see below) */
1200 if (tape.single_step)
1202 if (clear_snap_button[pnr])
1204 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1205 clear_snap_button[pnr] = FALSE;
1208 if (clear_drop_button[pnr])
1210 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1211 clear_drop_button[pnr] = FALSE;
1215 if (key_status == KEY_PRESSED)
1216 stored_player[pnr].action |= key_action;
1218 stored_player[pnr].action &= ~key_action;
1220 if (tape.single_step && tape.recording && tape.pausing)
1222 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1224 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1226 /* if snap key already pressed, don't snap when releasing (below) */
1227 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1228 element_snapped[pnr] = TRUE;
1230 /* if drop key already pressed, don't drop when releasing (below) */
1231 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1232 element_dropped[pnr] = TRUE;
1234 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1236 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1237 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1241 getRedDiskReleaseFlag_SP() == 0)
1242 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1244 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1247 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1249 if (key_action & KEY_BUTTON_SNAP)
1251 /* if snap key was released without moving (see above), snap now */
1252 if (!element_snapped[pnr])
1254 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1256 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1258 /* clear delayed snap button on next event */
1259 clear_snap_button[pnr] = TRUE;
1262 element_snapped[pnr] = FALSE;
1265 if (key_action & KEY_BUTTON_DROP &&
1266 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1268 /* if drop key was released without moving (see above), drop now */
1269 if (!element_dropped[pnr])
1271 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1273 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1274 getRedDiskReleaseFlag_SP() != 0)
1275 stored_player[pnr].action |= KEY_BUTTON_DROP;
1277 /* clear delayed drop button on next event */
1278 clear_drop_button[pnr] = TRUE;
1281 element_dropped[pnr] = FALSE;
1285 else if (tape.recording && tape.pausing)
1287 /* prevent key release events from un-pausing a paused game */
1288 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1289 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1295 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1296 if (key == key_info[i].key_default)
1297 joy |= key_info[i].action;
1302 if (key_status == KEY_PRESSED)
1303 key_joystick_mapping |= joy;
1305 key_joystick_mapping &= ~joy;
1310 if (game_status != GAME_MODE_PLAYING)
1311 key_joystick_mapping = 0;
1313 if (key_status == KEY_RELEASED)
1316 if ((key == KSYM_F11 ||
1317 ((key == KSYM_Return ||
1318 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1319 video.fullscreen_available)
1321 setup.fullscreen = !setup.fullscreen;
1323 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1325 if (game_status == GAME_MODE_SETUP)
1326 RedrawSetupScreenAfterFullscreenToggle();
1331 if ((key == KSYM_minus ||
1334 ((GetKeyModState() & KMOD_Control) ||
1335 (GetKeyModState() & KMOD_Alt)) &&
1336 video.window_scaling_available &&
1337 !video.fullscreen_enabled)
1340 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1342 setup.window_scaling_percent +=
1343 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1345 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1346 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1347 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1348 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1350 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1352 if (game_status == GAME_MODE_SETUP)
1353 RedrawSetupScreenAfterFullscreenToggle();
1358 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1359 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1366 if (game_status == GAME_MODE_MAIN &&
1367 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1369 StartGameActions(options.network, setup.autorecord, level.random_seed);
1374 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1376 if (key == setup.shortcut.save_game)
1378 else if (key == setup.shortcut.load_game)
1380 else if (key == setup.shortcut.toggle_pause)
1381 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1383 HandleTapeButtonKeys(key);
1384 HandleSoundButtonKeys(key);
1387 if (game_status == GAME_MODE_PLAYING && !network_playing)
1389 int centered_player_nr_next = -999;
1391 if (key == setup.shortcut.focus_player_all)
1392 centered_player_nr_next = -1;
1394 for (i = 0; i < MAX_PLAYERS; i++)
1395 if (key == setup.shortcut.focus_player[i])
1396 centered_player_nr_next = i;
1398 if (centered_player_nr_next != -999)
1400 game.centered_player_nr_next = centered_player_nr_next;
1401 game.set_centered_player = TRUE;
1405 tape.centered_player_nr_next = game.centered_player_nr_next;
1406 tape.set_centered_player = TRUE;
1411 HandleKeysSpecial(key);
1413 if (HandleGadgetsKeyInput(key))
1415 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1416 key = KSYM_UNDEFINED;
1419 switch (game_status)
1421 case GAME_MODE_PSEUDO_TYPENAME:
1422 HandleTypeName(0, key);
1425 case GAME_MODE_TITLE:
1426 case GAME_MODE_MAIN:
1427 case GAME_MODE_LEVELS:
1428 case GAME_MODE_LEVELNR:
1429 case GAME_MODE_SETUP:
1430 case GAME_MODE_INFO:
1431 case GAME_MODE_SCORES:
1436 if (game_status == GAME_MODE_TITLE)
1437 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1438 else if (game_status == GAME_MODE_MAIN)
1439 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1440 else if (game_status == GAME_MODE_LEVELS)
1441 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1442 else if (game_status == GAME_MODE_LEVELNR)
1443 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1444 else if (game_status == GAME_MODE_SETUP)
1445 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1446 else if (game_status == GAME_MODE_INFO)
1447 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1448 else if (game_status == GAME_MODE_SCORES)
1449 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1453 if (game_status != GAME_MODE_MAIN)
1454 FadeSkipNextFadeIn();
1456 if (game_status == GAME_MODE_TITLE)
1457 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1458 else if (game_status == GAME_MODE_LEVELS)
1459 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1460 else if (game_status == GAME_MODE_LEVELNR)
1461 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1462 else if (game_status == GAME_MODE_SETUP)
1463 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1464 else if (game_status == GAME_MODE_INFO)
1465 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1466 else if (game_status == GAME_MODE_SCORES)
1467 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1471 if (game_status == GAME_MODE_LEVELS)
1472 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1473 else if (game_status == GAME_MODE_LEVELNR)
1474 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1475 else if (game_status == GAME_MODE_SETUP)
1476 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1477 else if (game_status == GAME_MODE_INFO)
1478 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1479 else if (game_status == GAME_MODE_SCORES)
1480 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1483 case KSYM_Page_Down:
1484 if (game_status == GAME_MODE_LEVELS)
1485 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1486 else if (game_status == GAME_MODE_LEVELNR)
1487 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1488 else if (game_status == GAME_MODE_SETUP)
1489 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1490 else if (game_status == GAME_MODE_INFO)
1491 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1492 else if (game_status == GAME_MODE_SCORES)
1493 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1498 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1502 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1503 printf("Supaplex border elements %s\n",
1504 setup.sp_show_border_elements ? "enabled" : "disabled");
1513 case GAME_MODE_EDITOR:
1514 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1515 HandleLevelEditorKeyInput(key);
1518 case GAME_MODE_PLAYING:
1523 RequestQuitGame(setup.ask_on_escape);
1530 if (GameFrameDelay == 500)
1531 GameFrameDelay = GAME_FRAME_DELAY;
1533 GameFrameDelay = 500;
1536 GameFrameDelay = (key - KSYM_0) * 10;
1537 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1538 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1544 options.debug = FALSE;
1545 printf("debug mode disabled\n");
1549 options.debug = TRUE;
1550 printf("debug mode enabled\n");
1555 printf("::: currently using game engine version %d\n",
1556 game.engine_version);
1567 if (key == KSYM_Escape)
1569 game_status = GAME_MODE_MAIN;
1577 void HandleNoEvent()
1579 // if (button_status && game_status != GAME_MODE_PLAYING)
1580 if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1582 HandleButton(0, 0, -button_status, button_status);
1589 #if defined(NETWORK_AVALIABLE)
1590 if (options.network)
1594 switch (game_status)
1596 case GAME_MODE_MAIN:
1597 DrawPreviewLevelAnimation();
1601 case GAME_MODE_LEVELS:
1602 case GAME_MODE_LEVELNR:
1603 case GAME_MODE_SETUP:
1604 case GAME_MODE_INFO:
1605 case GAME_MODE_SCORES:
1609 case GAME_MODE_EDITOR:
1610 HandleLevelEditorIdle();
1618 static int HandleJoystickForAllPlayers()
1623 for (i = 0; i < MAX_PLAYERS; i++)
1625 byte joy_action = 0;
1628 if (!setup.input[i].use_joystick)
1632 joy_action = Joystick(i);
1633 result |= joy_action;
1635 if (!setup.input[i].use_joystick)
1638 stored_player[i].action = joy_action;
1644 void HandleJoystick()
1646 int joystick = HandleJoystickForAllPlayers();
1647 int keyboard = key_joystick_mapping;
1648 int joy = (joystick | keyboard);
1649 int left = joy & JOY_LEFT;
1650 int right = joy & JOY_RIGHT;
1651 int up = joy & JOY_UP;
1652 int down = joy & JOY_DOWN;
1653 int button = joy & JOY_BUTTON;
1654 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1655 int dx = (left ? -1 : right ? 1 : 0);
1656 int dy = (up ? -1 : down ? 1 : 0);
1658 switch (game_status)
1660 case GAME_MODE_TITLE:
1661 case GAME_MODE_MAIN:
1662 case GAME_MODE_LEVELS:
1663 case GAME_MODE_LEVELNR:
1664 case GAME_MODE_SETUP:
1665 case GAME_MODE_INFO:
1667 static unsigned int joystickmove_delay = 0;
1669 if (joystick && !button &&
1670 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1671 newbutton = dx = dy = 0;
1673 if (game_status == GAME_MODE_TITLE)
1674 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1675 else if (game_status == GAME_MODE_MAIN)
1676 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1677 else if (game_status == GAME_MODE_LEVELS)
1678 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1679 else if (game_status == GAME_MODE_LEVELNR)
1680 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1681 else if (game_status == GAME_MODE_SETUP)
1682 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1683 else if (game_status == GAME_MODE_INFO)
1684 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1688 case GAME_MODE_SCORES:
1689 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1692 case GAME_MODE_PLAYING:
1693 if (tape.playing || keyboard)
1694 newbutton = ((joy & JOY_BUTTON) != 0);
1696 if (newbutton && AllPlayersGone)