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"
26 #define DEBUG_EVENTS 0
28 #define DEBUG_EVENTS_BUTTON (DEBUG_EVENTS * 0)
29 #define DEBUG_EVENTS_MOTION (DEBUG_EVENTS * 0)
30 #define DEBUG_EVENTS_WHEEL (DEBUG_EVENTS * 1)
31 #define DEBUG_EVENTS_WINDOW (DEBUG_EVENTS * 0)
32 #define DEBUG_EVENTS_FINGER (DEBUG_EVENTS * 0)
33 #define DEBUG_EVENTS_TEXT (DEBUG_EVENTS * 1)
34 #define DEBUG_EVENTS_KEY (DEBUG_EVENTS * 1)
37 static boolean cursor_inside_playfield = FALSE;
38 static int cursor_mode_last = CURSOR_DEFAULT;
39 static unsigned int special_cursor_delay = 0;
40 static unsigned int special_cursor_delay_value = 1000;
42 static boolean virtual_button_pressed = FALSE;
45 // forward declarations for internal use
46 static void HandleNoEvent(void);
47 static void HandleEventActions(void);
50 // event filter especially needed for SDL event filtering due to
51 // delay problems with lots of mouse motion events when mouse button
52 // not pressed (X11 can handle this with 'PointerMotionHintMask')
54 // event filter addition for SDL2: as SDL2 does not have a function to enable
55 // or disable keyboard auto-repeat, filter repeated keyboard events instead
57 static int FilterEvents(const Event *event)
61 // skip repeated key press events if keyboard auto-repeat is disabled
62 if (event->type == EVENT_KEYPRESS &&
67 if (event->type == EVENT_BUTTONPRESS ||
68 event->type == EVENT_BUTTONRELEASE)
70 ((ButtonEvent *)event)->x -= video.screen_xoffset;
71 ((ButtonEvent *)event)->y -= video.screen_yoffset;
73 else if (event->type == EVENT_MOTIONNOTIFY)
75 ((MotionEvent *)event)->x -= video.screen_xoffset;
76 ((MotionEvent *)event)->y -= video.screen_yoffset;
79 // non-motion events are directly passed to event handler functions
80 if (event->type != EVENT_MOTIONNOTIFY)
83 motion = (MotionEvent *)event;
84 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
85 motion->y >= SY && motion->y < SY + SYSIZE);
87 // do no reset mouse cursor before all pending events have been processed
88 if (gfx.cursor_mode == cursor_mode_last &&
89 ((game_status == GAME_MODE_TITLE &&
90 gfx.cursor_mode == CURSOR_NONE) ||
91 (game_status == GAME_MODE_PLAYING &&
92 gfx.cursor_mode == CURSOR_PLAYFIELD)))
94 SetMouseCursor(CURSOR_DEFAULT);
96 DelayReached(&special_cursor_delay, 0);
98 cursor_mode_last = CURSOR_DEFAULT;
101 // skip mouse motion events without pressed button outside level editor
102 if (button_status == MB_RELEASED &&
103 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
109 // to prevent delay problems, skip mouse motion events if the very next
110 // event is also a mouse motion event (and therefore effectively only
111 // handling the last of a row of mouse motion events in the event queue)
113 static boolean SkipPressedMouseMotionEvent(const Event *event)
115 // nothing to do if the current event is not a mouse motion event
116 if (event->type != EVENT_MOTIONNOTIFY)
119 // only skip motion events with pressed button outside the game
120 if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
127 PeekEvent(&next_event);
129 // if next event is also a mouse motion event, skip the current one
130 if (next_event.type == EVENT_MOTIONNOTIFY)
137 static boolean WaitValidEvent(Event *event)
141 if (!FilterEvents(event))
144 if (SkipPressedMouseMotionEvent(event))
150 /* this is especially needed for event modifications for the Android target:
151 if mouse coordinates should be modified in the event filter function,
152 using a properly installed SDL event filter does not work, because in
153 the event filter, mouse coordinates in the event structure are still
154 physical pixel positions, not logical (scaled) screen positions, so this
155 has to be handled at a later stage in the event processing functions
156 (when device pixel positions are already converted to screen positions) */
158 boolean NextValidEvent(Event *event)
160 while (PendingEvent())
161 if (WaitValidEvent(event))
167 static void HandleEvents(void)
170 unsigned int event_frame_delay = 0;
171 unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
173 ResetDelayCounter(&event_frame_delay);
175 while (NextValidEvent(&event))
179 case EVENT_BUTTONPRESS:
180 case EVENT_BUTTONRELEASE:
181 HandleButtonEvent((ButtonEvent *) &event);
184 case EVENT_MOTIONNOTIFY:
185 HandleMotionEvent((MotionEvent *) &event);
188 case EVENT_WHEELMOTION:
189 HandleWheelEvent((WheelEvent *) &event);
192 case SDL_WINDOWEVENT:
193 HandleWindowEvent((WindowEvent *) &event);
196 case EVENT_FINGERPRESS:
197 case EVENT_FINGERRELEASE:
198 case EVENT_FINGERMOTION:
199 HandleFingerEvent((FingerEvent *) &event);
202 case EVENT_TEXTINPUT:
203 HandleTextEvent((TextEvent *) &event);
206 case SDL_APP_WILLENTERBACKGROUND:
207 case SDL_APP_DIDENTERBACKGROUND:
208 case SDL_APP_WILLENTERFOREGROUND:
209 case SDL_APP_DIDENTERFOREGROUND:
210 HandlePauseResumeEvent((PauseResumeEvent *) &event);
214 case EVENT_KEYRELEASE:
215 HandleKeyEvent((KeyEvent *) &event);
219 HandleOtherEvents(&event);
223 // do not handle events for longer than standard frame delay period
224 if (DelayReached(&event_frame_delay, event_frame_delay_value))
229 void HandleOtherEvents(Event *event)
234 HandleExposeEvent((ExposeEvent *) event);
237 case EVENT_UNMAPNOTIFY:
239 // This causes the game to stop not only when iconified, but also
240 // when on another virtual desktop, which might be not desired.
241 SleepWhileUnmapped();
247 HandleFocusEvent((FocusChangeEvent *) event);
250 case EVENT_CLIENTMESSAGE:
251 HandleClientMessageEvent((ClientMessageEvent *) event);
254 case SDL_CONTROLLERBUTTONDOWN:
255 case SDL_CONTROLLERBUTTONUP:
256 // for any game controller button event, disable overlay buttons
257 SetOverlayEnabled(FALSE);
259 HandleSpecialGameControllerButtons(event);
262 case SDL_CONTROLLERDEVICEADDED:
263 case SDL_CONTROLLERDEVICEREMOVED:
264 case SDL_CONTROLLERAXISMOTION:
265 case SDL_JOYAXISMOTION:
266 case SDL_JOYBUTTONDOWN:
267 case SDL_JOYBUTTONUP:
268 HandleJoystickEvent(event);
272 HandleDropFileEvent(event);
276 HandleDropTextEvent(event);
284 static void HandleMouseCursor(void)
286 if (game_status == GAME_MODE_TITLE)
288 // when showing title screens, hide mouse pointer (if not moved)
290 if (gfx.cursor_mode != CURSOR_NONE &&
291 DelayReached(&special_cursor_delay, special_cursor_delay_value))
293 SetMouseCursor(CURSOR_NONE);
296 else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
299 // when playing, display a special mouse pointer inside the playfield
301 if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
302 cursor_inside_playfield &&
303 DelayReached(&special_cursor_delay, special_cursor_delay_value))
305 if (level.game_engine_type != GAME_ENGINE_TYPE_MM ||
307 SetMouseCursor(CURSOR_PLAYFIELD);
310 else if (gfx.cursor_mode != CURSOR_DEFAULT)
312 SetMouseCursor(CURSOR_DEFAULT);
315 // this is set after all pending events have been processed
316 cursor_mode_last = gfx.cursor_mode;
328 // execute event related actions after pending events have been processed
329 HandleEventActions();
331 // don't use all CPU time when idle; the main loop while playing
332 // has its own synchronization and is CPU friendly, too
334 if (game_status == GAME_MODE_PLAYING)
337 // always copy backbuffer to visible screen for every video frame
340 // reset video frame delay to default (may change again while playing)
341 SetVideoFrameDelay(MenuFrameDelay);
343 if (game_status == GAME_MODE_QUIT)
348 void ClearAutoRepeatKeyEvents(void)
350 while (PendingEvent())
354 PeekEvent(&next_event);
356 // if event is repeated key press event, remove it from event queue
357 if (next_event.type == EVENT_KEYPRESS &&
358 next_event.key.repeat)
359 WaitEvent(&next_event);
365 void ClearEventQueue(void)
369 while (NextValidEvent(&event))
373 case EVENT_BUTTONRELEASE:
374 button_status = MB_RELEASED;
377 case EVENT_KEYRELEASE:
381 case SDL_CONTROLLERBUTTONUP:
382 HandleJoystickEvent(&event);
387 HandleOtherEvents(&event);
393 static void ClearPlayerMouseAction(void)
395 local_player->mouse_action.lx = 0;
396 local_player->mouse_action.ly = 0;
397 local_player->mouse_action.button = 0;
400 void ClearPlayerAction(void)
404 // simulate key release events for still pressed keys
405 key_joystick_mapping = 0;
406 for (i = 0; i < MAX_PLAYERS; i++)
407 stored_player[i].action = 0;
409 ClearJoystickState();
410 ClearPlayerMouseAction();
413 static void SetPlayerMouseAction(int mx, int my, int button)
415 int lx = getLevelFromScreenX(mx);
416 int ly = getLevelFromScreenY(my);
417 int new_button = (!local_player->mouse_action.button && button);
419 if (local_player->mouse_action.button_hint)
420 button = local_player->mouse_action.button_hint;
422 ClearPlayerMouseAction();
424 if (!IN_GFX_FIELD_PLAY(mx, my) || !IN_LEV_FIELD(lx, ly))
427 local_player->mouse_action.lx = lx;
428 local_player->mouse_action.ly = ly;
429 local_player->mouse_action.button = button;
431 if (tape.recording && tape.pausing && tape.use_mouse)
433 // un-pause a paused game only if mouse button was newly pressed down
435 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
438 SetTileCursorXY(lx, ly);
441 void SleepWhileUnmapped(void)
443 boolean window_unmapped = TRUE;
445 KeyboardAutoRepeatOn();
447 while (window_unmapped)
451 if (!WaitValidEvent(&event))
456 case EVENT_BUTTONRELEASE:
457 button_status = MB_RELEASED;
460 case EVENT_KEYRELEASE:
461 key_joystick_mapping = 0;
464 case SDL_CONTROLLERBUTTONUP:
465 HandleJoystickEvent(&event);
466 key_joystick_mapping = 0;
469 case EVENT_MAPNOTIFY:
470 window_unmapped = FALSE;
473 case EVENT_UNMAPNOTIFY:
474 // this is only to surely prevent the 'should not happen' case
475 // of recursively looping between 'SleepWhileUnmapped()' and
476 // 'HandleOtherEvents()' which usually calls this funtion.
480 HandleOtherEvents(&event);
485 if (game_status == GAME_MODE_PLAYING)
486 KeyboardAutoRepeatOffUnlessAutoplay();
489 void HandleExposeEvent(ExposeEvent *event)
493 void HandleButtonEvent(ButtonEvent *event)
495 #if DEBUG_EVENTS_BUTTON
496 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
498 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
502 // for any mouse button event, disable playfield tile cursor
503 SetTileCursorEnabled(FALSE);
505 #if defined(HAS_SCREEN_KEYBOARD)
506 if (video.shifted_up)
507 event->y += video.shifted_up_pos;
510 motion_status = FALSE;
512 if (event->type == EVENT_BUTTONPRESS)
513 button_status = event->button;
515 button_status = MB_RELEASED;
517 HandleButton(event->x, event->y, button_status, event->button);
520 void HandleMotionEvent(MotionEvent *event)
522 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
525 motion_status = TRUE;
527 #if DEBUG_EVENTS_MOTION
528 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
529 button_status, event->x, event->y);
532 HandleButton(event->x, event->y, button_status, button_status);
535 void HandleWheelEvent(WheelEvent *event)
539 #if DEBUG_EVENTS_WHEEL
541 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
542 event->which, event->x, event->y);
544 // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
545 Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
546 event->which, event->x, event->y,
547 (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
548 "SDL_MOUSEWHEEL_FLIPPED"));
552 button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
553 event->x > 0 ? MB_WHEEL_RIGHT :
554 event->y < 0 ? MB_WHEEL_DOWN :
555 event->y > 0 ? MB_WHEEL_UP : 0);
557 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
558 // accelerated mouse wheel available on Mac and Windows
559 wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
561 // no accelerated mouse wheel available on Unix/Linux
562 wheel_steps = DEFAULT_WHEEL_STEPS;
565 motion_status = FALSE;
567 button_status = button_nr;
568 HandleButton(0, 0, button_status, -button_nr);
570 button_status = MB_RELEASED;
571 HandleButton(0, 0, button_status, -button_nr);
574 void HandleWindowEvent(WindowEvent *event)
576 #if DEBUG_EVENTS_WINDOW
577 int subtype = event->event;
580 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
581 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
582 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
583 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
584 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
585 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
586 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
587 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
588 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
589 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
590 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
591 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
592 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
593 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
596 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
597 event_name, event->data1, event->data2);
601 // (not needed, as the screen gets redrawn every 20 ms anyway)
602 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
603 event->event == SDL_WINDOWEVENT_RESIZED ||
604 event->event == SDL_WINDOWEVENT_EXPOSED)
608 if (event->event == SDL_WINDOWEVENT_RESIZED)
610 if (!video.fullscreen_enabled)
612 int new_window_width = event->data1;
613 int new_window_height = event->data2;
615 // if window size has changed after resizing, calculate new scaling factor
616 if (new_window_width != video.window_width ||
617 new_window_height != video.window_height)
619 int new_xpercent = 100.0 * new_window_width / video.screen_width + .5;
620 int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
622 // (extreme window scaling allowed, but cannot be saved permanently)
623 video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
624 setup.window_scaling_percent =
625 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
626 MAX_WINDOW_SCALING_PERCENT);
628 video.window_width = new_window_width;
629 video.window_height = new_window_height;
631 if (game_status == GAME_MODE_SETUP)
632 RedrawSetupScreenAfterFullscreenToggle();
637 #if defined(PLATFORM_ANDROID)
640 int new_display_width = event->data1;
641 int new_display_height = event->data2;
643 // if fullscreen display size has changed, device has been rotated
644 if (new_display_width != video.display_width ||
645 new_display_height != video.display_height)
647 int nr = GRID_ACTIVE_NR(); // previous screen orientation
649 video.display_width = new_display_width;
650 video.display_height = new_display_height;
652 SDLSetScreenProperties();
654 // check if screen orientation has changed (should always be true here)
655 if (nr != GRID_ACTIVE_NR())
659 if (game_status == GAME_MODE_SETUP)
660 RedrawSetupScreenAfterScreenRotation(nr);
662 nr = GRID_ACTIVE_NR();
664 overlay.grid_xsize = setup.touch.grid_xsize[nr];
665 overlay.grid_ysize = setup.touch.grid_ysize[nr];
667 for (x = 0; x < MAX_GRID_XSIZE; x++)
668 for (y = 0; y < MAX_GRID_YSIZE; y++)
669 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
677 #define NUM_TOUCH_FINGERS 3
682 SDL_FingerID finger_id;
685 } touch_info[NUM_TOUCH_FINGERS];
687 static void HandleFingerEvent_VirtualButtons(FingerEvent *event)
690 int x = event->x * overlay.grid_xsize;
691 int y = event->y * overlay.grid_ysize;
692 int grid_button = overlay.grid_button[x][y];
693 int grid_button_action = GET_ACTION_FROM_GRID_BUTTON(grid_button);
694 Key key = (grid_button == CHAR_GRID_BUTTON_LEFT ? setup.input[0].key.left :
695 grid_button == CHAR_GRID_BUTTON_RIGHT ? setup.input[0].key.right :
696 grid_button == CHAR_GRID_BUTTON_UP ? setup.input[0].key.up :
697 grid_button == CHAR_GRID_BUTTON_DOWN ? setup.input[0].key.down :
698 grid_button == CHAR_GRID_BUTTON_SNAP ? setup.input[0].key.snap :
699 grid_button == CHAR_GRID_BUTTON_DROP ? setup.input[0].key.drop :
702 float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
703 float event_x = (event->x);
704 float event_y = (event->y - ypos) / (1 - ypos);
705 Key key = (event_x > 0 && event_x < 1.0 / 6.0 &&
706 event_y > 2.0 / 3.0 && event_y < 1 ?
707 setup.input[0].key.snap :
708 event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
709 event_y > 2.0 / 3.0 && event_y < 1 ?
710 setup.input[0].key.drop :
711 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
712 event_y > 0 && event_y < 1.0 / 3.0 ?
713 setup.input[0].key.up :
714 event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
715 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
716 setup.input[0].key.left :
717 event_x > 8.0 / 9.0 && event_x < 1 &&
718 event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
719 setup.input[0].key.right :
720 event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
721 event_y > 2.0 / 3.0 && event_y < 1 ?
722 setup.input[0].key.down :
725 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
727 char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
731 virtual_button_pressed = (key_status == KEY_PRESSED && key != KSYM_UNDEFINED);
733 // for any touch input event, enable overlay buttons (if activated)
734 SetOverlayEnabled(TRUE);
736 Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
737 getKeyNameFromKey(key), key_status_name, event->fingerId);
739 if (key_status == KEY_PRESSED)
740 overlay.grid_button_action |= grid_button_action;
742 overlay.grid_button_action &= ~grid_button_action;
744 // check if we already know this touch event's finger id
745 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
747 if (touch_info[i].touched &&
748 touch_info[i].finger_id == event->fingerId)
750 // Error(ERR_DEBUG, "MARK 1: %d", i);
756 if (i >= NUM_TOUCH_FINGERS)
758 if (key_status == KEY_PRESSED)
760 int oldest_pos = 0, oldest_counter = touch_info[0].counter;
762 // unknown finger id -- get new, empty slot, if available
763 for (i = 0; i < NUM_TOUCH_FINGERS; i++)
765 if (touch_info[i].counter < oldest_counter)
768 oldest_counter = touch_info[i].counter;
770 // Error(ERR_DEBUG, "MARK 2: %d", i);
773 if (!touch_info[i].touched)
775 // Error(ERR_DEBUG, "MARK 3: %d", i);
781 if (i >= NUM_TOUCH_FINGERS)
783 // all slots allocated -- use oldest slot
786 // Error(ERR_DEBUG, "MARK 4: %d", i);
791 // release of previously unknown key (should not happen)
793 if (key != KSYM_UNDEFINED)
795 HandleKey(key, KEY_RELEASED);
797 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
798 getKeyNameFromKey(key), "KEY_RELEASED", i);
803 if (i < NUM_TOUCH_FINGERS)
805 if (key_status == KEY_PRESSED)
807 if (touch_info[i].key != key)
809 if (touch_info[i].key != KSYM_UNDEFINED)
811 HandleKey(touch_info[i].key, KEY_RELEASED);
813 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
814 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
817 if (key != KSYM_UNDEFINED)
819 HandleKey(key, KEY_PRESSED);
821 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
822 getKeyNameFromKey(key), "KEY_PRESSED", i);
826 touch_info[i].touched = TRUE;
827 touch_info[i].finger_id = event->fingerId;
828 touch_info[i].counter = Counter();
829 touch_info[i].key = key;
833 if (touch_info[i].key != KSYM_UNDEFINED)
835 HandleKey(touch_info[i].key, KEY_RELEASED);
837 Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
838 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
841 touch_info[i].touched = FALSE;
842 touch_info[i].finger_id = 0;
843 touch_info[i].counter = 0;
844 touch_info[i].key = 0;
849 static void HandleFingerEvent_WipeGestures(FingerEvent *event)
851 static Key motion_key_x = KSYM_UNDEFINED;
852 static Key motion_key_y = KSYM_UNDEFINED;
853 static Key button_key = KSYM_UNDEFINED;
854 static float motion_x1, motion_y1;
855 static float button_x1, button_y1;
856 static SDL_FingerID motion_id = -1;
857 static SDL_FingerID button_id = -1;
858 int move_trigger_distance_percent = setup.touch.move_distance;
859 int drop_trigger_distance_percent = setup.touch.drop_distance;
860 float move_trigger_distance = (float)move_trigger_distance_percent / 100;
861 float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
862 float event_x = event->x;
863 float event_y = event->y;
865 if (event->type == EVENT_FINGERPRESS)
867 if (event_x > 1.0 / 3.0)
871 motion_id = event->fingerId;
876 motion_key_x = KSYM_UNDEFINED;
877 motion_key_y = KSYM_UNDEFINED;
879 Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
885 button_id = event->fingerId;
890 button_key = setup.input[0].key.snap;
892 HandleKey(button_key, KEY_PRESSED);
894 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
897 else if (event->type == EVENT_FINGERRELEASE)
899 if (event->fingerId == motion_id)
903 if (motion_key_x != KSYM_UNDEFINED)
904 HandleKey(motion_key_x, KEY_RELEASED);
905 if (motion_key_y != KSYM_UNDEFINED)
906 HandleKey(motion_key_y, KEY_RELEASED);
908 motion_key_x = KSYM_UNDEFINED;
909 motion_key_y = KSYM_UNDEFINED;
911 Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
913 else if (event->fingerId == button_id)
917 if (button_key != KSYM_UNDEFINED)
918 HandleKey(button_key, KEY_RELEASED);
920 button_key = KSYM_UNDEFINED;
922 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
925 else if (event->type == EVENT_FINGERMOTION)
927 if (event->fingerId == motion_id)
929 float distance_x = ABS(event_x - motion_x1);
930 float distance_y = ABS(event_y - motion_y1);
931 Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
932 event_x > motion_x1 ? setup.input[0].key.right :
934 Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
935 event_y > motion_y1 ? setup.input[0].key.down :
938 if (distance_x < move_trigger_distance / 2 ||
939 distance_x < distance_y)
940 new_motion_key_x = KSYM_UNDEFINED;
942 if (distance_y < move_trigger_distance / 2 ||
943 distance_y < distance_x)
944 new_motion_key_y = KSYM_UNDEFINED;
946 if (distance_x > move_trigger_distance ||
947 distance_y > move_trigger_distance)
949 if (new_motion_key_x != motion_key_x)
951 if (motion_key_x != KSYM_UNDEFINED)
952 HandleKey(motion_key_x, KEY_RELEASED);
953 if (new_motion_key_x != KSYM_UNDEFINED)
954 HandleKey(new_motion_key_x, KEY_PRESSED);
957 if (new_motion_key_y != motion_key_y)
959 if (motion_key_y != KSYM_UNDEFINED)
960 HandleKey(motion_key_y, KEY_RELEASED);
961 if (new_motion_key_y != KSYM_UNDEFINED)
962 HandleKey(new_motion_key_y, KEY_PRESSED);
968 motion_key_x = new_motion_key_x;
969 motion_key_y = new_motion_key_y;
971 Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
974 else if (event->fingerId == button_id)
976 float distance_x = ABS(event_x - button_x1);
977 float distance_y = ABS(event_y - button_y1);
979 if (distance_x < drop_trigger_distance / 2 &&
980 distance_y > drop_trigger_distance)
982 if (button_key == setup.input[0].key.snap)
983 HandleKey(button_key, KEY_RELEASED);
988 button_key = setup.input[0].key.drop;
990 HandleKey(button_key, KEY_PRESSED);
992 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
998 void HandleFingerEvent(FingerEvent *event)
1000 #if DEBUG_EVENTS_FINGER
1001 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
1002 event->type == EVENT_FINGERPRESS ? "pressed" :
1003 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
1007 event->dx, event->dy,
1011 if (game_status != GAME_MODE_PLAYING)
1014 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1016 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_OFF))
1017 local_player->mouse_action.button_hint =
1018 (event->type == EVENT_FINGERRELEASE ? MB_NOT_PRESSED :
1019 event->x < 0.5 ? MB_LEFTBUTTON :
1020 event->x > 0.5 ? MB_RIGHTBUTTON :
1026 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1027 HandleFingerEvent_VirtualButtons(event);
1028 else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
1029 HandleFingerEvent_WipeGestures(event);
1032 static void HandleButtonOrFinger_WipeGestures_MM(int mx, int my, int button)
1034 static int old_mx = 0, old_my = 0;
1035 static int last_button = MB_LEFTBUTTON;
1036 static boolean touched = FALSE;
1037 static boolean tapped = FALSE;
1039 // screen tile was tapped (but finger not touching the screen anymore)
1040 // (this point will also be reached without receiving a touch event)
1041 if (tapped && !touched)
1043 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1048 // stop here if this function was not triggered by a touch event
1052 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
1054 // finger started touching the screen
1064 ClearPlayerMouseAction();
1066 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
1069 else if (button == MB_RELEASED && touched)
1071 // finger stopped touching the screen
1076 SetPlayerMouseAction(old_mx, old_my, last_button);
1078 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1080 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
1085 // finger moved while touching the screen
1087 int old_x = getLevelFromScreenX(old_mx);
1088 int old_y = getLevelFromScreenY(old_my);
1089 int new_x = getLevelFromScreenX(mx);
1090 int new_y = getLevelFromScreenY(my);
1092 if (new_x != old_x || new_y != old_y)
1097 // finger moved left or right from (horizontal) starting position
1099 int button_nr = (new_x < old_x ? MB_LEFTBUTTON : MB_RIGHTBUTTON);
1101 SetPlayerMouseAction(old_mx, old_my, button_nr);
1103 last_button = button_nr;
1105 Error(ERR_DEBUG, "---------- TOUCH ACTION: ROTATING ----------");
1109 // finger stays at or returned to (horizontal) starting position
1111 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1113 Error(ERR_DEBUG, "---------- TOUCH ACTION PAUSED ----------");
1118 static void HandleButtonOrFinger_FollowFinger_MM(int mx, int my, int button)
1120 static int old_mx = 0, old_my = 0;
1121 static int last_button = MB_LEFTBUTTON;
1122 static boolean touched = FALSE;
1123 static boolean tapped = FALSE;
1125 // screen tile was tapped (but finger not touching the screen anymore)
1126 // (this point will also be reached without receiving a touch event)
1127 if (tapped && !touched)
1129 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1134 // stop here if this function was not triggered by a touch event
1138 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
1140 // finger started touching the screen
1150 ClearPlayerMouseAction();
1152 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
1155 else if (button == MB_RELEASED && touched)
1157 // finger stopped touching the screen
1162 SetPlayerMouseAction(old_mx, old_my, last_button);
1164 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1166 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
1171 // finger moved while touching the screen
1173 int old_x = getLevelFromScreenX(old_mx);
1174 int old_y = getLevelFromScreenY(old_my);
1175 int new_x = getLevelFromScreenX(mx);
1176 int new_y = getLevelFromScreenY(my);
1178 if (new_x != old_x || new_y != old_y)
1180 // finger moved away from starting position
1182 int button_nr = getButtonFromTouchPosition(old_x, old_y, mx, my);
1184 // quickly alternate between clicking and releasing for maximum speed
1185 if (FrameCounter % 2 == 0)
1186 button_nr = MB_RELEASED;
1188 SetPlayerMouseAction(old_mx, old_my, button_nr);
1191 last_button = button_nr;
1195 Error(ERR_DEBUG, "---------- TOUCH ACTION: ROTATING ----------");
1199 // finger stays at or returned to starting position
1201 SetPlayerMouseAction(old_mx, old_my, MB_RELEASED);
1203 Error(ERR_DEBUG, "---------- TOUCH ACTION PAUSED ----------");
1208 static void HandleButtonOrFinger_FollowFinger(int mx, int my, int button)
1210 static int old_mx = 0, old_my = 0;
1211 static Key motion_key_x = KSYM_UNDEFINED;
1212 static Key motion_key_y = KSYM_UNDEFINED;
1213 static boolean touched = FALSE;
1214 static boolean started_on_player = FALSE;
1215 static boolean player_is_dropping = FALSE;
1216 static int player_drop_count = 0;
1217 static int last_player_x = -1;
1218 static int last_player_y = -1;
1220 if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
1229 started_on_player = FALSE;
1230 player_is_dropping = FALSE;
1231 player_drop_count = 0;
1235 motion_key_x = KSYM_UNDEFINED;
1236 motion_key_y = KSYM_UNDEFINED;
1238 Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
1241 else if (button == MB_RELEASED && touched)
1248 if (motion_key_x != KSYM_UNDEFINED)
1249 HandleKey(motion_key_x, KEY_RELEASED);
1250 if (motion_key_y != KSYM_UNDEFINED)
1251 HandleKey(motion_key_y, KEY_RELEASED);
1253 if (started_on_player)
1255 if (player_is_dropping)
1257 Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
1259 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1263 Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
1265 HandleKey(setup.input[0].key.snap, KEY_RELEASED);
1269 motion_key_x = KSYM_UNDEFINED;
1270 motion_key_y = KSYM_UNDEFINED;
1272 Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
1277 int src_x = local_player->jx;
1278 int src_y = local_player->jy;
1279 int dst_x = getLevelFromScreenX(old_mx);
1280 int dst_y = getLevelFromScreenY(old_my);
1281 int dx = dst_x - src_x;
1282 int dy = dst_y - src_y;
1283 Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
1284 dx > 0 ? setup.input[0].key.right :
1286 Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
1287 dy > 0 ? setup.input[0].key.down :
1290 if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1291 (last_player_x != local_player->jx ||
1292 last_player_y != local_player->jy))
1294 // in case of asymmetric diagonal movement, use "preferred" direction
1296 int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1298 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1299 level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1301 local_player->last_move_dir = last_move_dir;
1303 // (required to prevent accidentally forcing direction for next movement)
1304 last_player_x = local_player->jx;
1305 last_player_y = local_player->jy;
1308 if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1310 started_on_player = TRUE;
1311 player_drop_count = getPlayerInventorySize(0);
1312 player_is_dropping = (player_drop_count > 0);
1314 if (player_is_dropping)
1316 Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1318 HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1322 Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1324 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1327 else if (dx != 0 || dy != 0)
1329 if (player_is_dropping &&
1330 player_drop_count == getPlayerInventorySize(0))
1332 Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1334 HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1335 HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1337 player_is_dropping = FALSE;
1341 if (new_motion_key_x != motion_key_x)
1343 Error(ERR_DEBUG, "---------- %s %s ----------",
1344 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1345 dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1347 if (motion_key_x != KSYM_UNDEFINED)
1348 HandleKey(motion_key_x, KEY_RELEASED);
1349 if (new_motion_key_x != KSYM_UNDEFINED)
1350 HandleKey(new_motion_key_x, KEY_PRESSED);
1353 if (new_motion_key_y != motion_key_y)
1355 Error(ERR_DEBUG, "---------- %s %s ----------",
1356 started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1357 dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1359 if (motion_key_y != KSYM_UNDEFINED)
1360 HandleKey(motion_key_y, KEY_RELEASED);
1361 if (new_motion_key_y != KSYM_UNDEFINED)
1362 HandleKey(new_motion_key_y, KEY_PRESSED);
1365 motion_key_x = new_motion_key_x;
1366 motion_key_y = new_motion_key_y;
1370 static void HandleButtonOrFinger(int mx, int my, int button)
1372 if (game_status != GAME_MODE_PLAYING)
1375 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1377 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
1378 HandleButtonOrFinger_WipeGestures_MM(mx, my, button);
1379 else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
1380 HandleButtonOrFinger_FollowFinger_MM(mx, my, button);
1381 else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1382 SetPlayerMouseAction(mx, my, button); // special case
1386 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
1387 HandleButtonOrFinger_FollowFinger(mx, my, button);
1391 static boolean checkTextInputKeyModState(void)
1393 // when playing, only handle raw key events and ignore text input
1394 if (game_status == GAME_MODE_PLAYING)
1397 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1400 void HandleTextEvent(TextEvent *event)
1402 char *text = event->text;
1403 Key key = getKeyFromKeyName(text);
1405 #if DEBUG_EVENTS_TEXT
1406 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1409 text[0], (int)(text[0]),
1411 getKeyNameFromKey(key),
1415 #if !defined(HAS_SCREEN_KEYBOARD)
1416 // non-mobile devices: only handle key input with modifier keys pressed here
1417 // (every other key input is handled directly as physical key input event)
1418 if (!checkTextInputKeyModState())
1422 // process text input as "classic" (with uppercase etc.) key input event
1423 HandleKey(key, KEY_PRESSED);
1424 HandleKey(key, KEY_RELEASED);
1427 void HandlePauseResumeEvent(PauseResumeEvent *event)
1429 if (event->type == SDL_APP_WILLENTERBACKGROUND)
1433 else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1439 void HandleKeyEvent(KeyEvent *event)
1441 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1442 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1443 Key key = GetEventKey(event, with_modifiers);
1444 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1446 #if DEBUG_EVENTS_KEY
1447 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1448 event->type == EVENT_KEYPRESS ? "pressed" : "released",
1449 event->keysym.scancode,
1454 getKeyNameFromKey(key));
1457 #if defined(PLATFORM_ANDROID)
1458 if (key == KSYM_Back)
1460 // always map the "back" button to the "escape" key on Android devices
1463 else if (key == KSYM_Menu)
1465 // the "menu" button can be used to toggle displaying virtual buttons
1466 if (key_status == KEY_PRESSED)
1467 SetOverlayEnabled(!GetOverlayEnabled());
1471 // for any other "real" key event, disable virtual buttons
1472 SetOverlayEnabled(FALSE);
1476 HandleKeyModState(keymod, key_status);
1478 // only handle raw key input without text modifier keys pressed
1479 if (!checkTextInputKeyModState())
1480 HandleKey(key, key_status);
1483 void HandleFocusEvent(FocusChangeEvent *event)
1485 static int old_joystick_status = -1;
1487 if (event->type == EVENT_FOCUSOUT)
1489 KeyboardAutoRepeatOn();
1490 old_joystick_status = joystick.status;
1491 joystick.status = JOYSTICK_NOT_AVAILABLE;
1493 ClearPlayerAction();
1495 else if (event->type == EVENT_FOCUSIN)
1497 /* When there are two Rocks'n'Diamonds windows which overlap and
1498 the player moves the pointer from one game window to the other,
1499 a 'FocusOut' event is generated for the window the pointer is
1500 leaving and a 'FocusIn' event is generated for the window the
1501 pointer is entering. In some cases, it can happen that the
1502 'FocusIn' event is handled by the one game process before the
1503 'FocusOut' event by the other game process. In this case the
1504 X11 environment would end up with activated keyboard auto repeat,
1505 because unfortunately this is a global setting and not (which
1506 would be far better) set for each X11 window individually.
1507 The effect would be keyboard auto repeat while playing the game
1508 (game_status == GAME_MODE_PLAYING), which is not desired.
1509 To avoid this special case, we just wait 1/10 second before
1510 processing the 'FocusIn' event. */
1512 if (game_status == GAME_MODE_PLAYING)
1515 KeyboardAutoRepeatOffUnlessAutoplay();
1518 if (old_joystick_status != -1)
1519 joystick.status = old_joystick_status;
1523 void HandleClientMessageEvent(ClientMessageEvent *event)
1525 if (CheckCloseWindowEvent(event))
1529 static void HandleDropFileEventExt(char *filename)
1531 Error(ERR_DEBUG, "DROP FILE EVENT: '%s'", filename);
1534 static void HandleDropTextEventExt(char *text)
1536 Error(ERR_DEBUG, "DROP TEXT EVENT: '%s'", text);
1539 void HandleDropFileEvent(Event *event)
1541 HandleDropFileEventExt(event->drop.file);
1543 SDL_free(event->drop.file);
1546 void HandleDropTextEvent(Event *event)
1548 HandleDropTextEventExt(event->drop.file);
1550 SDL_free(event->drop.file);
1553 void HandleButton(int mx, int my, int button, int button_nr)
1555 static int old_mx = 0, old_my = 0;
1556 boolean button_hold = FALSE;
1557 boolean handle_gadgets = TRUE;
1563 button_nr = -button_nr;
1572 #if defined(PLATFORM_ANDROID)
1573 // when playing, only handle gadgets when using "follow finger" controls
1574 // or when using touch controls in combination with the MM game engine
1575 // or when using gadgets that do not overlap with virtual buttons
1577 (game_status != GAME_MODE_PLAYING ||
1578 level.game_engine_type == GAME_ENGINE_TYPE_MM ||
1579 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER) ||
1580 (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1581 !virtual_button_pressed));
1584 if (HandleGlobalAnimClicks(mx, my, button))
1586 // do not handle this button event anymore
1587 return; // force mouse event not to be handled at all
1590 if (handle_gadgets && HandleGadgets(mx, my, button))
1592 // do not handle this button event anymore
1593 mx = my = -32; // force mouse event to be outside screen tiles
1596 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1599 // do not use scroll wheel button events for anything other than gadgets
1600 if (IS_WHEEL_BUTTON(button_nr))
1603 switch (game_status)
1605 case GAME_MODE_TITLE:
1606 HandleTitleScreen(mx, my, 0, 0, button);
1609 case GAME_MODE_MAIN:
1610 HandleMainMenu(mx, my, 0, 0, button);
1613 case GAME_MODE_PSEUDO_TYPENAME:
1614 HandleTypeName(0, KSYM_Return);
1617 case GAME_MODE_LEVELS:
1618 HandleChooseLevelSet(mx, my, 0, 0, button);
1621 case GAME_MODE_LEVELNR:
1622 HandleChooseLevelNr(mx, my, 0, 0, button);
1625 case GAME_MODE_SCORES:
1626 HandleHallOfFame(0, 0, 0, 0, button);
1629 case GAME_MODE_EDITOR:
1630 HandleLevelEditorIdle();
1633 case GAME_MODE_INFO:
1634 HandleInfoScreen(mx, my, 0, 0, button);
1637 case GAME_MODE_SETUP:
1638 HandleSetupScreen(mx, my, 0, 0, button);
1641 case GAME_MODE_PLAYING:
1642 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_OFF))
1643 HandleButtonOrFinger(mx, my, button);
1645 SetPlayerMouseAction(mx, my, button);
1648 if (button == MB_PRESSED && !motion_status && !button_hold &&
1649 IN_GFX_FIELD_PLAY(mx, my) && GetKeyModState() & KMOD_Control)
1650 DumpTileFromScreen(mx, my);
1660 static boolean is_string_suffix(char *string, char *suffix)
1662 int string_len = strlen(string);
1663 int suffix_len = strlen(suffix);
1665 if (suffix_len > string_len)
1668 return (strEqual(&string[string_len - suffix_len], suffix));
1671 #define MAX_CHEAT_INPUT_LEN 32
1673 static void HandleKeysSpecial(Key key)
1675 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1676 char letter = getCharFromKey(key);
1677 int cheat_input_len = strlen(cheat_input);
1683 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1685 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1686 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1688 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1691 cheat_input[cheat_input_len++] = letter;
1692 cheat_input[cheat_input_len] = '\0';
1694 #if DEBUG_EVENTS_KEY
1695 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1698 if (game_status == GAME_MODE_MAIN)
1700 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1701 is_string_suffix(cheat_input, ":ist"))
1703 InsertSolutionTape();
1705 else if (is_string_suffix(cheat_input, ":play-solution-tape") ||
1706 is_string_suffix(cheat_input, ":pst"))
1710 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1711 is_string_suffix(cheat_input, ":rg"))
1713 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1716 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1717 is_string_suffix(cheat_input, ":rs"))
1719 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1722 else if (is_string_suffix(cheat_input, ":reload-music") ||
1723 is_string_suffix(cheat_input, ":rm"))
1725 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1728 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1729 is_string_suffix(cheat_input, ":ra"))
1731 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1732 1 << ARTWORK_TYPE_SOUNDS |
1733 1 << ARTWORK_TYPE_MUSIC);
1736 else if (is_string_suffix(cheat_input, ":dump-level") ||
1737 is_string_suffix(cheat_input, ":dl"))
1741 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1742 is_string_suffix(cheat_input, ":dt"))
1746 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1747 is_string_suffix(cheat_input, ":ft"))
1749 /* fix single-player tapes that contain player input for more than one
1750 player (due to a bug in 3.3.1.2 and earlier versions), which results
1751 in playing levels with more than one player in multi-player mode,
1752 even though the tape was originally recorded in single-player mode */
1754 // remove player input actions for all players but the first one
1755 for (i = 1; i < MAX_PLAYERS; i++)
1756 tape.player_participates[i] = FALSE;
1758 tape.changed = TRUE;
1760 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1761 is_string_suffix(cheat_input, ":snl"))
1763 SaveNativeLevel(&level);
1765 else if (is_string_suffix(cheat_input, ":frames-per-second") ||
1766 is_string_suffix(cheat_input, ":fps"))
1768 global.show_frames_per_second = !global.show_frames_per_second;
1771 else if (game_status == GAME_MODE_PLAYING)
1774 if (is_string_suffix(cheat_input, ".q"))
1775 DEBUG_SetMaximumDynamite();
1778 else if (game_status == GAME_MODE_EDITOR)
1780 if (is_string_suffix(cheat_input, ":dump-brush") ||
1781 is_string_suffix(cheat_input, ":DB"))
1785 else if (is_string_suffix(cheat_input, ":DDB"))
1790 if (GetKeyModState() & (KMOD_Control | KMOD_Meta))
1792 if (letter == 'x') // copy brush to clipboard (small size)
1794 CopyBrushToClipboard_Small();
1796 else if (letter == 'c') // copy brush to clipboard (normal size)
1798 CopyBrushToClipboard();
1800 else if (letter == 'v') // paste brush from Clipboard
1802 CopyClipboardToBrush();
1807 // special key shortcuts for all game modes
1808 if (is_string_suffix(cheat_input, ":dump-event-actions") ||
1809 is_string_suffix(cheat_input, ":dea") ||
1810 is_string_suffix(cheat_input, ":DEA"))
1812 DumpGadgetIdentifiers();
1813 DumpScreenIdentifiers();
1817 boolean HandleKeysDebug(Key key, int key_status)
1822 if (key_status != KEY_PRESSED)
1825 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1827 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
1829 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1831 if (key == setup.debug.frame_delay_key[i] &&
1832 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1834 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1835 setup.debug.frame_delay[i] : setup.game_frame_delay);
1837 if (!setup.debug.frame_delay_game_only)
1838 MenuFrameDelay = GameFrameDelay;
1840 SetVideoFrameDelay(GameFrameDelay);
1842 if (GameFrameDelay > ONE_SECOND_DELAY)
1843 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1844 else if (GameFrameDelay != 0)
1845 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1846 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1847 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1849 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1856 if (game_status == GAME_MODE_PLAYING)
1860 options.debug = !options.debug;
1862 Error(ERR_DEBUG, "debug mode %s",
1863 (options.debug ? "enabled" : "disabled"));
1867 else if (key == KSYM_v)
1869 Error(ERR_DEBUG, "currently using game engine version %d",
1870 game.engine_version);
1880 void HandleKey(Key key, int key_status)
1882 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1883 static boolean ignore_repeated_key = FALSE;
1884 static struct SetupKeyboardInfo ski;
1885 static struct SetupShortcutInfo ssi;
1894 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1895 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1896 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1897 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1898 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1899 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1904 if (HandleKeysDebug(key, key_status))
1905 return; // do not handle already processed keys again
1907 // map special keys (media keys / remote control buttons) to default keys
1908 if (key == KSYM_PlayPause)
1910 else if (key == KSYM_Select)
1913 HandleSpecialGameControllerKeys(key, key_status);
1915 if (game_status == GAME_MODE_PLAYING)
1917 // only needed for single-step tape recording mode
1918 static boolean has_snapped[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE };
1921 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1923 byte key_action = 0;
1925 if (setup.input[pnr].use_joystick)
1928 ski = setup.input[pnr].key;
1930 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1931 if (key == *key_info[i].key_custom)
1932 key_action |= key_info[i].action;
1934 // use combined snap+direction keys for the first player only
1937 ssi = setup.shortcut;
1939 for (i = 0; i < NUM_DIRECTIONS; i++)
1940 if (key == *key_info[i].key_snap)
1941 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1944 if (key_status == KEY_PRESSED)
1945 stored_player[pnr].action |= key_action;
1947 stored_player[pnr].action &= ~key_action;
1949 if (tape.single_step && tape.recording && tape.pausing && !tape.use_mouse)
1951 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1953 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1955 // if snap key already pressed, keep pause mode when releasing
1956 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1957 has_snapped[pnr] = TRUE;
1959 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1961 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1963 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1964 getRedDiskReleaseFlag_SP() == 0)
1966 // add a single inactive frame before dropping starts
1967 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1968 stored_player[pnr].force_dropping = TRUE;
1971 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON_SNAP)
1973 // if snap key was pressed without direction, leave pause mode
1974 if (!has_snapped[pnr])
1975 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1977 has_snapped[pnr] = FALSE;
1980 else if (tape.recording && tape.pausing && !tape.use_mouse)
1982 // prevent key release events from un-pausing a paused game
1983 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1984 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1987 // for MM style levels, handle in-game keyboard input in HandleJoystick()
1988 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1994 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1995 if (key == key_info[i].key_default)
1996 joy |= key_info[i].action;
2001 if (key_status == KEY_PRESSED)
2002 key_joystick_mapping |= joy;
2004 key_joystick_mapping &= ~joy;
2009 if (game_status != GAME_MODE_PLAYING)
2010 key_joystick_mapping = 0;
2012 if (key_status == KEY_RELEASED)
2014 // reset flag to ignore repeated "key pressed" events after key release
2015 ignore_repeated_key = FALSE;
2020 if ((key == KSYM_F11 ||
2021 ((key == KSYM_Return ||
2022 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
2023 video.fullscreen_available &&
2024 !ignore_repeated_key)
2026 setup.fullscreen = !setup.fullscreen;
2028 ToggleFullscreenOrChangeWindowScalingIfNeeded();
2030 if (game_status == GAME_MODE_SETUP)
2031 RedrawSetupScreenAfterFullscreenToggle();
2033 // set flag to ignore repeated "key pressed" events
2034 ignore_repeated_key = TRUE;
2039 if ((key == KSYM_0 || key == KSYM_KP_0 ||
2040 key == KSYM_minus || key == KSYM_KP_Subtract ||
2041 key == KSYM_plus || key == KSYM_KP_Add ||
2042 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
2043 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
2044 video.window_scaling_available &&
2045 !video.fullscreen_enabled)
2047 if (key == KSYM_0 || key == KSYM_KP_0)
2048 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
2049 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
2050 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
2052 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
2054 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
2055 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
2056 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
2057 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
2059 ToggleFullscreenOrChangeWindowScalingIfNeeded();
2061 if (game_status == GAME_MODE_SETUP)
2062 RedrawSetupScreenAfterFullscreenToggle();
2067 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
2068 key == KSYM_Return ||
2069 key == KSYM_Escape)))
2071 // do not handle this key event anymore
2072 if (key != KSYM_Escape) // always allow ESC key to be handled
2076 if (game_status == GAME_MODE_PLAYING && game.all_players_gone &&
2077 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
2084 if (game_status == GAME_MODE_MAIN &&
2085 (key == setup.shortcut.toggle_pause || key == KSYM_space))
2087 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
2092 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
2094 if (key == setup.shortcut.save_game)
2096 else if (key == setup.shortcut.load_game)
2098 else if (key == setup.shortcut.toggle_pause)
2099 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
2101 HandleTapeButtonKeys(key);
2102 HandleSoundButtonKeys(key);
2105 if (game_status == GAME_MODE_PLAYING && !network_playing)
2107 int centered_player_nr_next = -999;
2109 if (key == setup.shortcut.focus_player_all)
2110 centered_player_nr_next = -1;
2112 for (i = 0; i < MAX_PLAYERS; i++)
2113 if (key == setup.shortcut.focus_player[i])
2114 centered_player_nr_next = i;
2116 if (centered_player_nr_next != -999)
2118 game.centered_player_nr_next = centered_player_nr_next;
2119 game.set_centered_player = TRUE;
2123 tape.centered_player_nr_next = game.centered_player_nr_next;
2124 tape.set_centered_player = TRUE;
2129 HandleKeysSpecial(key);
2131 if (HandleGadgetsKeyInput(key))
2132 return; // do not handle already processed keys again
2134 switch (game_status)
2136 case GAME_MODE_PSEUDO_TYPENAME:
2137 HandleTypeName(0, key);
2140 case GAME_MODE_TITLE:
2141 case GAME_MODE_MAIN:
2142 case GAME_MODE_LEVELS:
2143 case GAME_MODE_LEVELNR:
2144 case GAME_MODE_SETUP:
2145 case GAME_MODE_INFO:
2146 case GAME_MODE_SCORES:
2148 if (anyTextGadgetActiveOrJustFinished && key != KSYM_Escape)
2155 if (game_status == GAME_MODE_TITLE)
2156 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2157 else if (game_status == GAME_MODE_MAIN)
2158 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
2159 else if (game_status == GAME_MODE_LEVELS)
2160 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
2161 else if (game_status == GAME_MODE_LEVELNR)
2162 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
2163 else if (game_status == GAME_MODE_SETUP)
2164 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2165 else if (game_status == GAME_MODE_INFO)
2166 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2167 else if (game_status == GAME_MODE_SCORES)
2168 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
2172 if (game_status != GAME_MODE_MAIN)
2173 FadeSkipNextFadeIn();
2175 if (game_status == GAME_MODE_TITLE)
2176 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2177 else if (game_status == GAME_MODE_LEVELS)
2178 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
2179 else if (game_status == GAME_MODE_LEVELNR)
2180 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
2181 else if (game_status == GAME_MODE_SETUP)
2182 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2183 else if (game_status == GAME_MODE_INFO)
2184 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2185 else if (game_status == GAME_MODE_SCORES)
2186 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
2190 if (game_status == GAME_MODE_LEVELS)
2191 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2192 else if (game_status == GAME_MODE_LEVELNR)
2193 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2194 else if (game_status == GAME_MODE_SETUP)
2195 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2196 else if (game_status == GAME_MODE_INFO)
2197 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2198 else if (game_status == GAME_MODE_SCORES)
2199 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2202 case KSYM_Page_Down:
2203 if (game_status == GAME_MODE_LEVELS)
2204 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2205 else if (game_status == GAME_MODE_LEVELNR)
2206 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2207 else if (game_status == GAME_MODE_SETUP)
2208 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2209 else if (game_status == GAME_MODE_INFO)
2210 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2211 else if (game_status == GAME_MODE_SCORES)
2212 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2220 case GAME_MODE_EDITOR:
2221 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
2222 HandleLevelEditorKeyInput(key);
2225 case GAME_MODE_PLAYING:
2230 RequestQuitGame(setup.ask_on_escape);
2240 if (key == KSYM_Escape)
2242 SetGameStatus(GAME_MODE_MAIN);
2251 void HandleNoEvent(void)
2253 HandleMouseCursor();
2255 switch (game_status)
2257 case GAME_MODE_PLAYING:
2258 HandleButtonOrFinger(-1, -1, -1);
2263 void HandleEventActions(void)
2265 // if (button_status && game_status != GAME_MODE_PLAYING)
2266 if (button_status && (game_status != GAME_MODE_PLAYING ||
2268 level.game_engine_type == GAME_ENGINE_TYPE_MM))
2270 HandleButton(0, 0, button_status, -button_status);
2277 if (network.enabled)
2280 switch (game_status)
2282 case GAME_MODE_MAIN:
2283 DrawPreviewLevelAnimation();
2286 case GAME_MODE_EDITOR:
2287 HandleLevelEditorIdle();
2295 static void HandleTileCursor(int dx, int dy, int button)
2298 ClearPlayerMouseAction();
2305 SetPlayerMouseAction(tile_cursor.x, tile_cursor.y,
2306 (dx < 0 ? MB_LEFTBUTTON :
2307 dx > 0 ? MB_RIGHTBUTTON : MB_RELEASED));
2309 else if (!tile_cursor.moving)
2311 int old_xpos = tile_cursor.xpos;
2312 int old_ypos = tile_cursor.ypos;
2313 int new_xpos = old_xpos;
2314 int new_ypos = old_ypos;
2316 if (IN_LEV_FIELD(old_xpos + dx, old_ypos))
2317 new_xpos = old_xpos + dx;
2319 if (IN_LEV_FIELD(old_xpos, old_ypos + dy))
2320 new_ypos = old_ypos + dy;
2322 SetTileCursorTargetXY(new_xpos, new_ypos);
2326 static int HandleJoystickForAllPlayers(void)
2330 boolean no_joysticks_configured = TRUE;
2331 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
2332 static byte joy_action_last[MAX_PLAYERS];
2334 for (i = 0; i < MAX_PLAYERS; i++)
2335 if (setup.input[i].use_joystick)
2336 no_joysticks_configured = FALSE;
2338 // if no joysticks configured, map connected joysticks to players
2339 if (no_joysticks_configured)
2340 use_as_joystick_nr = TRUE;
2342 for (i = 0; i < MAX_PLAYERS; i++)
2344 byte joy_action = 0;
2346 joy_action = JoystickExt(i, use_as_joystick_nr);
2347 result |= joy_action;
2349 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
2350 joy_action != joy_action_last[i])
2351 stored_player[i].action = joy_action;
2353 joy_action_last[i] = joy_action;
2359 void HandleJoystick(void)
2361 static unsigned int joytest_delay = 0;
2362 static unsigned int joytest_delay_value = GADGET_FRAME_DELAY;
2363 static int joytest_last = 0;
2364 int delay_value_first = GADGET_FRAME_DELAY_FIRST;
2365 int delay_value = GADGET_FRAME_DELAY;
2366 int joystick = HandleJoystickForAllPlayers();
2367 int keyboard = key_joystick_mapping;
2368 int joy = (joystick | keyboard);
2369 int joytest = joystick;
2370 int left = joy & JOY_LEFT;
2371 int right = joy & JOY_RIGHT;
2372 int up = joy & JOY_UP;
2373 int down = joy & JOY_DOWN;
2374 int button = joy & JOY_BUTTON;
2375 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
2376 int dx = (left ? -1 : right ? 1 : 0);
2377 int dy = (up ? -1 : down ? 1 : 0);
2378 boolean use_delay_value_first = (joytest != joytest_last);
2380 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2382 // do not handle this button event anymore
2386 if (newbutton && (game_status == GAME_MODE_PSEUDO_TYPENAME ||
2387 anyTextGadgetActive()))
2389 // leave name input in main menu or text input gadget
2390 HandleKey(KSYM_Escape, KEY_PRESSED);
2391 HandleKey(KSYM_Escape, KEY_RELEASED);
2396 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2398 if (game_status == GAME_MODE_PLAYING)
2400 // when playing MM style levels, also use delay for keyboard events
2401 joytest |= keyboard;
2403 // only use first delay value for new events, but not for changed events
2404 use_delay_value_first = (!joytest != !joytest_last);
2406 // only use delay after the initial keyboard event
2410 // for any joystick or keyboard event, enable playfield tile cursor
2411 if (dx || dy || button)
2412 SetTileCursorEnabled(TRUE);
2415 if (joytest && !button && !DelayReached(&joytest_delay, joytest_delay_value))
2417 // delay joystick/keyboard actions if axes/keys continually pressed
2418 newbutton = dx = dy = 0;
2422 // first start with longer delay, then continue with shorter delay
2423 joytest_delay_value =
2424 (use_delay_value_first ? delay_value_first : delay_value);
2427 joytest_last = joytest;
2429 switch (game_status)
2431 case GAME_MODE_TITLE:
2432 case GAME_MODE_MAIN:
2433 case GAME_MODE_LEVELS:
2434 case GAME_MODE_LEVELNR:
2435 case GAME_MODE_SETUP:
2436 case GAME_MODE_INFO:
2437 case GAME_MODE_SCORES:
2439 if (anyTextGadgetActive())
2442 if (game_status == GAME_MODE_TITLE)
2443 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2444 else if (game_status == GAME_MODE_MAIN)
2445 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2446 else if (game_status == GAME_MODE_LEVELS)
2447 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2448 else if (game_status == GAME_MODE_LEVELNR)
2449 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2450 else if (game_status == GAME_MODE_SETUP)
2451 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2452 else if (game_status == GAME_MODE_INFO)
2453 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2454 else if (game_status == GAME_MODE_SCORES)
2455 HandleHallOfFame(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2460 case GAME_MODE_PLAYING:
2462 // !!! causes immediate GameEnd() when solving MM level with keyboard !!!
2463 if (tape.playing || keyboard)
2464 newbutton = ((joy & JOY_BUTTON) != 0);
2467 if (newbutton && game.all_players_gone)
2474 if (tape.single_step && tape.recording && tape.pausing && !tape.use_mouse)
2476 if (joystick & JOY_ACTION)
2477 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
2479 else if (tape.recording && tape.pausing && !tape.use_mouse)
2481 if (joystick & JOY_ACTION)
2482 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2485 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2486 HandleTileCursor(dx, dy, button);
2495 void HandleSpecialGameControllerButtons(Event *event)
2500 switch (event->type)
2502 case SDL_CONTROLLERBUTTONDOWN:
2503 key_status = KEY_PRESSED;
2506 case SDL_CONTROLLERBUTTONUP:
2507 key_status = KEY_RELEASED;
2514 switch (event->cbutton.button)
2516 case SDL_CONTROLLER_BUTTON_START:
2520 case SDL_CONTROLLER_BUTTON_BACK:
2528 HandleKey(key, key_status);
2531 void HandleSpecialGameControllerKeys(Key key, int key_status)
2533 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2534 int button = SDL_CONTROLLER_BUTTON_INVALID;
2536 // map keys to joystick buttons (special hack for Amazon Fire TV remote)
2537 if (key == KSYM_Rewind)
2538 button = SDL_CONTROLLER_BUTTON_A;
2539 else if (key == KSYM_FastForward || key == KSYM_Menu)
2540 button = SDL_CONTROLLER_BUTTON_B;
2542 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2546 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2547 SDL_CONTROLLERBUTTONUP);
2549 event.cbutton.which = 0; // first joystick (Amazon Fire TV remote)
2550 event.cbutton.button = button;
2551 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2554 HandleJoystickEvent(&event);
2559 boolean DoKeysymAction(int keysym)
2563 Key key = (Key)(-keysym);
2565 HandleKey(key, KEY_PRESSED);
2566 HandleKey(key, KEY_RELEASED);