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);
1533 // check and extract dropped zip files into correct user data directory
1534 if (strSuffixLower(filename, ".zip"))
1536 int tree_type = GetZipFileTreeType(filename);
1537 char *directory = TREE_USERDIR(tree_type);
1539 if (directory == NULL)
1541 Error(ERR_WARN, "zip file '%s' has invalid content!", filename);
1546 char *top_dir = ExtractZipFileIntoDirectory(filename, directory, tree_type);
1548 if (top_dir != NULL)
1550 if (tree_type == TREE_TYPE_LEVEL_DIR)
1551 AddUserLevelSetToLevelInfo(top_dir);
1553 AddUserArtworkSetToArtworkInfo(top_dir, tree_type);
1558 static void HandleDropTextEventExt(char *text)
1560 Error(ERR_DEBUG, "DROP TEXT EVENT: '%s'", text);
1563 void HandleDropFileEvent(Event *event)
1565 HandleDropFileEventExt(event->drop.file);
1567 SDL_free(event->drop.file);
1570 void HandleDropTextEvent(Event *event)
1572 HandleDropTextEventExt(event->drop.file);
1574 SDL_free(event->drop.file);
1577 void HandleButton(int mx, int my, int button, int button_nr)
1579 static int old_mx = 0, old_my = 0;
1580 boolean button_hold = FALSE;
1581 boolean handle_gadgets = TRUE;
1587 button_nr = -button_nr;
1596 #if defined(PLATFORM_ANDROID)
1597 // when playing, only handle gadgets when using "follow finger" controls
1598 // or when using touch controls in combination with the MM game engine
1599 // or when using gadgets that do not overlap with virtual buttons
1601 (game_status != GAME_MODE_PLAYING ||
1602 level.game_engine_type == GAME_ENGINE_TYPE_MM ||
1603 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER) ||
1604 (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1605 !virtual_button_pressed));
1608 if (HandleGlobalAnimClicks(mx, my, button))
1610 // do not handle this button event anymore
1611 return; // force mouse event not to be handled at all
1614 if (handle_gadgets && HandleGadgets(mx, my, button))
1616 // do not handle this button event anymore
1617 mx = my = -32; // force mouse event to be outside screen tiles
1620 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1623 // do not use scroll wheel button events for anything other than gadgets
1624 if (IS_WHEEL_BUTTON(button_nr))
1627 switch (game_status)
1629 case GAME_MODE_TITLE:
1630 HandleTitleScreen(mx, my, 0, 0, button);
1633 case GAME_MODE_MAIN:
1634 HandleMainMenu(mx, my, 0, 0, button);
1637 case GAME_MODE_PSEUDO_TYPENAME:
1638 HandleTypeName(0, KSYM_Return);
1641 case GAME_MODE_LEVELS:
1642 HandleChooseLevelSet(mx, my, 0, 0, button);
1645 case GAME_MODE_LEVELNR:
1646 HandleChooseLevelNr(mx, my, 0, 0, button);
1649 case GAME_MODE_SCORES:
1650 HandleHallOfFame(0, 0, 0, 0, button);
1653 case GAME_MODE_EDITOR:
1654 HandleLevelEditorIdle();
1657 case GAME_MODE_INFO:
1658 HandleInfoScreen(mx, my, 0, 0, button);
1661 case GAME_MODE_SETUP:
1662 HandleSetupScreen(mx, my, 0, 0, button);
1665 case GAME_MODE_PLAYING:
1666 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_OFF))
1667 HandleButtonOrFinger(mx, my, button);
1669 SetPlayerMouseAction(mx, my, button);
1672 if (button == MB_PRESSED && !motion_status && !button_hold &&
1673 IN_GFX_FIELD_PLAY(mx, my) && GetKeyModState() & KMOD_Control)
1674 DumpTileFromScreen(mx, my);
1684 static boolean is_string_suffix(char *string, char *suffix)
1686 int string_len = strlen(string);
1687 int suffix_len = strlen(suffix);
1689 if (suffix_len > string_len)
1692 return (strEqual(&string[string_len - suffix_len], suffix));
1695 #define MAX_CHEAT_INPUT_LEN 32
1697 static void HandleKeysSpecial(Key key)
1699 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1700 char letter = getCharFromKey(key);
1701 int cheat_input_len = strlen(cheat_input);
1707 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1709 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1710 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1712 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1715 cheat_input[cheat_input_len++] = letter;
1716 cheat_input[cheat_input_len] = '\0';
1718 #if DEBUG_EVENTS_KEY
1719 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1722 if (game_status == GAME_MODE_MAIN)
1724 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1725 is_string_suffix(cheat_input, ":ist"))
1727 InsertSolutionTape();
1729 else if (is_string_suffix(cheat_input, ":play-solution-tape") ||
1730 is_string_suffix(cheat_input, ":pst"))
1734 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1735 is_string_suffix(cheat_input, ":rg"))
1737 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1740 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1741 is_string_suffix(cheat_input, ":rs"))
1743 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1746 else if (is_string_suffix(cheat_input, ":reload-music") ||
1747 is_string_suffix(cheat_input, ":rm"))
1749 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1752 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1753 is_string_suffix(cheat_input, ":ra"))
1755 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1756 1 << ARTWORK_TYPE_SOUNDS |
1757 1 << ARTWORK_TYPE_MUSIC);
1760 else if (is_string_suffix(cheat_input, ":dump-level") ||
1761 is_string_suffix(cheat_input, ":dl"))
1765 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1766 is_string_suffix(cheat_input, ":dt"))
1770 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1771 is_string_suffix(cheat_input, ":ft"))
1773 /* fix single-player tapes that contain player input for more than one
1774 player (due to a bug in 3.3.1.2 and earlier versions), which results
1775 in playing levels with more than one player in multi-player mode,
1776 even though the tape was originally recorded in single-player mode */
1778 // remove player input actions for all players but the first one
1779 for (i = 1; i < MAX_PLAYERS; i++)
1780 tape.player_participates[i] = FALSE;
1782 tape.changed = TRUE;
1784 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1785 is_string_suffix(cheat_input, ":snl"))
1787 SaveNativeLevel(&level);
1789 else if (is_string_suffix(cheat_input, ":frames-per-second") ||
1790 is_string_suffix(cheat_input, ":fps"))
1792 global.show_frames_per_second = !global.show_frames_per_second;
1795 else if (game_status == GAME_MODE_PLAYING)
1798 if (is_string_suffix(cheat_input, ".q"))
1799 DEBUG_SetMaximumDynamite();
1802 else if (game_status == GAME_MODE_EDITOR)
1804 if (is_string_suffix(cheat_input, ":dump-brush") ||
1805 is_string_suffix(cheat_input, ":DB"))
1809 else if (is_string_suffix(cheat_input, ":DDB"))
1814 if (GetKeyModState() & (KMOD_Control | KMOD_Meta))
1816 if (letter == 'x') // copy brush to clipboard (small size)
1818 CopyBrushToClipboard_Small();
1820 else if (letter == 'c') // copy brush to clipboard (normal size)
1822 CopyBrushToClipboard();
1824 else if (letter == 'v') // paste brush from Clipboard
1826 CopyClipboardToBrush();
1831 // special key shortcuts for all game modes
1832 if (is_string_suffix(cheat_input, ":dump-event-actions") ||
1833 is_string_suffix(cheat_input, ":dea") ||
1834 is_string_suffix(cheat_input, ":DEA"))
1836 DumpGadgetIdentifiers();
1837 DumpScreenIdentifiers();
1841 boolean HandleKeysDebug(Key key, int key_status)
1846 if (key_status != KEY_PRESSED)
1849 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1851 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
1853 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1855 if (key == setup.debug.frame_delay_key[i] &&
1856 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1858 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1859 setup.debug.frame_delay[i] : setup.game_frame_delay);
1861 if (!setup.debug.frame_delay_game_only)
1862 MenuFrameDelay = GameFrameDelay;
1864 SetVideoFrameDelay(GameFrameDelay);
1866 if (GameFrameDelay > ONE_SECOND_DELAY)
1867 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1868 else if (GameFrameDelay != 0)
1869 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1870 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1871 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1873 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1880 if (game_status == GAME_MODE_PLAYING)
1884 options.debug = !options.debug;
1886 Error(ERR_DEBUG, "debug mode %s",
1887 (options.debug ? "enabled" : "disabled"));
1891 else if (key == KSYM_v)
1893 Error(ERR_DEBUG, "currently using game engine version %d",
1894 game.engine_version);
1904 void HandleKey(Key key, int key_status)
1906 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1907 static boolean ignore_repeated_key = FALSE;
1908 static struct SetupKeyboardInfo ski;
1909 static struct SetupShortcutInfo ssi;
1918 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1919 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1920 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1921 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1922 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1923 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1928 if (HandleKeysDebug(key, key_status))
1929 return; // do not handle already processed keys again
1931 // map special keys (media keys / remote control buttons) to default keys
1932 if (key == KSYM_PlayPause)
1934 else if (key == KSYM_Select)
1937 HandleSpecialGameControllerKeys(key, key_status);
1939 if (game_status == GAME_MODE_PLAYING)
1941 // only needed for single-step tape recording mode
1942 static boolean has_snapped[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE };
1945 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1947 byte key_action = 0;
1949 if (setup.input[pnr].use_joystick)
1952 ski = setup.input[pnr].key;
1954 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1955 if (key == *key_info[i].key_custom)
1956 key_action |= key_info[i].action;
1958 // use combined snap+direction keys for the first player only
1961 ssi = setup.shortcut;
1963 for (i = 0; i < NUM_DIRECTIONS; i++)
1964 if (key == *key_info[i].key_snap)
1965 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1968 if (key_status == KEY_PRESSED)
1969 stored_player[pnr].action |= key_action;
1971 stored_player[pnr].action &= ~key_action;
1973 if (tape.single_step && tape.recording && tape.pausing && !tape.use_mouse)
1975 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1977 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1979 // if snap key already pressed, keep pause mode when releasing
1980 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1981 has_snapped[pnr] = TRUE;
1983 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1985 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1987 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1988 getRedDiskReleaseFlag_SP() == 0)
1990 // add a single inactive frame before dropping starts
1991 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1992 stored_player[pnr].force_dropping = TRUE;
1995 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON_SNAP)
1997 // if snap key was pressed without direction, leave pause mode
1998 if (!has_snapped[pnr])
1999 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
2001 has_snapped[pnr] = FALSE;
2004 else if (tape.recording && tape.pausing && !tape.use_mouse)
2006 // prevent key release events from un-pausing a paused game
2007 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
2008 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2011 // for MM style levels, handle in-game keyboard input in HandleJoystick()
2012 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2018 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
2019 if (key == key_info[i].key_default)
2020 joy |= key_info[i].action;
2025 if (key_status == KEY_PRESSED)
2026 key_joystick_mapping |= joy;
2028 key_joystick_mapping &= ~joy;
2033 if (game_status != GAME_MODE_PLAYING)
2034 key_joystick_mapping = 0;
2036 if (key_status == KEY_RELEASED)
2038 // reset flag to ignore repeated "key pressed" events after key release
2039 ignore_repeated_key = FALSE;
2044 if ((key == KSYM_F11 ||
2045 ((key == KSYM_Return ||
2046 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
2047 video.fullscreen_available &&
2048 !ignore_repeated_key)
2050 setup.fullscreen = !setup.fullscreen;
2052 ToggleFullscreenOrChangeWindowScalingIfNeeded();
2054 if (game_status == GAME_MODE_SETUP)
2055 RedrawSetupScreenAfterFullscreenToggle();
2057 // set flag to ignore repeated "key pressed" events
2058 ignore_repeated_key = TRUE;
2063 if ((key == KSYM_0 || key == KSYM_KP_0 ||
2064 key == KSYM_minus || key == KSYM_KP_Subtract ||
2065 key == KSYM_plus || key == KSYM_KP_Add ||
2066 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
2067 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
2068 video.window_scaling_available &&
2069 !video.fullscreen_enabled)
2071 if (key == KSYM_0 || key == KSYM_KP_0)
2072 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
2073 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
2074 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
2076 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
2078 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
2079 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
2080 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
2081 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
2083 ToggleFullscreenOrChangeWindowScalingIfNeeded();
2085 if (game_status == GAME_MODE_SETUP)
2086 RedrawSetupScreenAfterFullscreenToggle();
2091 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
2092 key == KSYM_Return ||
2093 key == KSYM_Escape)))
2095 // do not handle this key event anymore
2096 if (key != KSYM_Escape) // always allow ESC key to be handled
2100 if (game_status == GAME_MODE_PLAYING && game.all_players_gone &&
2101 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
2108 if (game_status == GAME_MODE_MAIN &&
2109 (key == setup.shortcut.toggle_pause || key == KSYM_space))
2111 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
2116 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
2118 if (key == setup.shortcut.save_game)
2120 else if (key == setup.shortcut.load_game)
2122 else if (key == setup.shortcut.toggle_pause)
2123 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
2125 HandleTapeButtonKeys(key);
2126 HandleSoundButtonKeys(key);
2129 if (game_status == GAME_MODE_PLAYING && !network_playing)
2131 int centered_player_nr_next = -999;
2133 if (key == setup.shortcut.focus_player_all)
2134 centered_player_nr_next = -1;
2136 for (i = 0; i < MAX_PLAYERS; i++)
2137 if (key == setup.shortcut.focus_player[i])
2138 centered_player_nr_next = i;
2140 if (centered_player_nr_next != -999)
2142 game.centered_player_nr_next = centered_player_nr_next;
2143 game.set_centered_player = TRUE;
2147 tape.centered_player_nr_next = game.centered_player_nr_next;
2148 tape.set_centered_player = TRUE;
2153 HandleKeysSpecial(key);
2155 if (HandleGadgetsKeyInput(key))
2156 return; // do not handle already processed keys again
2158 switch (game_status)
2160 case GAME_MODE_PSEUDO_TYPENAME:
2161 HandleTypeName(0, key);
2164 case GAME_MODE_TITLE:
2165 case GAME_MODE_MAIN:
2166 case GAME_MODE_LEVELS:
2167 case GAME_MODE_LEVELNR:
2168 case GAME_MODE_SETUP:
2169 case GAME_MODE_INFO:
2170 case GAME_MODE_SCORES:
2172 if (anyTextGadgetActiveOrJustFinished && key != KSYM_Escape)
2179 if (game_status == GAME_MODE_TITLE)
2180 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2181 else if (game_status == GAME_MODE_MAIN)
2182 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
2183 else if (game_status == GAME_MODE_LEVELS)
2184 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
2185 else if (game_status == GAME_MODE_LEVELNR)
2186 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
2187 else if (game_status == GAME_MODE_SETUP)
2188 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2189 else if (game_status == GAME_MODE_INFO)
2190 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2191 else if (game_status == GAME_MODE_SCORES)
2192 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
2196 if (game_status != GAME_MODE_MAIN)
2197 FadeSkipNextFadeIn();
2199 if (game_status == GAME_MODE_TITLE)
2200 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2201 else if (game_status == GAME_MODE_LEVELS)
2202 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
2203 else if (game_status == GAME_MODE_LEVELNR)
2204 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
2205 else if (game_status == GAME_MODE_SETUP)
2206 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2207 else if (game_status == GAME_MODE_INFO)
2208 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2209 else if (game_status == GAME_MODE_SCORES)
2210 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
2214 if (game_status == GAME_MODE_LEVELS)
2215 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2216 else if (game_status == GAME_MODE_LEVELNR)
2217 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2218 else if (game_status == GAME_MODE_SETUP)
2219 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2220 else if (game_status == GAME_MODE_INFO)
2221 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2222 else if (game_status == GAME_MODE_SCORES)
2223 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2226 case KSYM_Page_Down:
2227 if (game_status == GAME_MODE_LEVELS)
2228 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2229 else if (game_status == GAME_MODE_LEVELNR)
2230 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2231 else if (game_status == GAME_MODE_SETUP)
2232 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2233 else if (game_status == GAME_MODE_INFO)
2234 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2235 else if (game_status == GAME_MODE_SCORES)
2236 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2244 case GAME_MODE_EDITOR:
2245 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
2246 HandleLevelEditorKeyInput(key);
2249 case GAME_MODE_PLAYING:
2254 RequestQuitGame(setup.ask_on_escape);
2264 if (key == KSYM_Escape)
2266 SetGameStatus(GAME_MODE_MAIN);
2275 void HandleNoEvent(void)
2277 HandleMouseCursor();
2279 switch (game_status)
2281 case GAME_MODE_PLAYING:
2282 HandleButtonOrFinger(-1, -1, -1);
2287 void HandleEventActions(void)
2289 // if (button_status && game_status != GAME_MODE_PLAYING)
2290 if (button_status && (game_status != GAME_MODE_PLAYING ||
2292 level.game_engine_type == GAME_ENGINE_TYPE_MM))
2294 HandleButton(0, 0, button_status, -button_status);
2301 if (network.enabled)
2304 switch (game_status)
2306 case GAME_MODE_MAIN:
2307 DrawPreviewLevelAnimation();
2310 case GAME_MODE_EDITOR:
2311 HandleLevelEditorIdle();
2319 static void HandleTileCursor(int dx, int dy, int button)
2322 ClearPlayerMouseAction();
2329 SetPlayerMouseAction(tile_cursor.x, tile_cursor.y,
2330 (dx < 0 ? MB_LEFTBUTTON :
2331 dx > 0 ? MB_RIGHTBUTTON : MB_RELEASED));
2333 else if (!tile_cursor.moving)
2335 int old_xpos = tile_cursor.xpos;
2336 int old_ypos = tile_cursor.ypos;
2337 int new_xpos = old_xpos;
2338 int new_ypos = old_ypos;
2340 if (IN_LEV_FIELD(old_xpos + dx, old_ypos))
2341 new_xpos = old_xpos + dx;
2343 if (IN_LEV_FIELD(old_xpos, old_ypos + dy))
2344 new_ypos = old_ypos + dy;
2346 SetTileCursorTargetXY(new_xpos, new_ypos);
2350 static int HandleJoystickForAllPlayers(void)
2354 boolean no_joysticks_configured = TRUE;
2355 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
2356 static byte joy_action_last[MAX_PLAYERS];
2358 for (i = 0; i < MAX_PLAYERS; i++)
2359 if (setup.input[i].use_joystick)
2360 no_joysticks_configured = FALSE;
2362 // if no joysticks configured, map connected joysticks to players
2363 if (no_joysticks_configured)
2364 use_as_joystick_nr = TRUE;
2366 for (i = 0; i < MAX_PLAYERS; i++)
2368 byte joy_action = 0;
2370 joy_action = JoystickExt(i, use_as_joystick_nr);
2371 result |= joy_action;
2373 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
2374 joy_action != joy_action_last[i])
2375 stored_player[i].action = joy_action;
2377 joy_action_last[i] = joy_action;
2383 void HandleJoystick(void)
2385 static unsigned int joytest_delay = 0;
2386 static unsigned int joytest_delay_value = GADGET_FRAME_DELAY;
2387 static int joytest_last = 0;
2388 int delay_value_first = GADGET_FRAME_DELAY_FIRST;
2389 int delay_value = GADGET_FRAME_DELAY;
2390 int joystick = HandleJoystickForAllPlayers();
2391 int keyboard = key_joystick_mapping;
2392 int joy = (joystick | keyboard);
2393 int joytest = joystick;
2394 int left = joy & JOY_LEFT;
2395 int right = joy & JOY_RIGHT;
2396 int up = joy & JOY_UP;
2397 int down = joy & JOY_DOWN;
2398 int button = joy & JOY_BUTTON;
2399 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
2400 int dx = (left ? -1 : right ? 1 : 0);
2401 int dy = (up ? -1 : down ? 1 : 0);
2402 boolean use_delay_value_first = (joytest != joytest_last);
2404 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2406 // do not handle this button event anymore
2410 if (newbutton && (game_status == GAME_MODE_PSEUDO_TYPENAME ||
2411 anyTextGadgetActive()))
2413 // leave name input in main menu or text input gadget
2414 HandleKey(KSYM_Escape, KEY_PRESSED);
2415 HandleKey(KSYM_Escape, KEY_RELEASED);
2420 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2422 if (game_status == GAME_MODE_PLAYING)
2424 // when playing MM style levels, also use delay for keyboard events
2425 joytest |= keyboard;
2427 // only use first delay value for new events, but not for changed events
2428 use_delay_value_first = (!joytest != !joytest_last);
2430 // only use delay after the initial keyboard event
2434 // for any joystick or keyboard event, enable playfield tile cursor
2435 if (dx || dy || button)
2436 SetTileCursorEnabled(TRUE);
2439 if (joytest && !button && !DelayReached(&joytest_delay, joytest_delay_value))
2441 // delay joystick/keyboard actions if axes/keys continually pressed
2442 newbutton = dx = dy = 0;
2446 // first start with longer delay, then continue with shorter delay
2447 joytest_delay_value =
2448 (use_delay_value_first ? delay_value_first : delay_value);
2451 joytest_last = joytest;
2453 switch (game_status)
2455 case GAME_MODE_TITLE:
2456 case GAME_MODE_MAIN:
2457 case GAME_MODE_LEVELS:
2458 case GAME_MODE_LEVELNR:
2459 case GAME_MODE_SETUP:
2460 case GAME_MODE_INFO:
2461 case GAME_MODE_SCORES:
2463 if (anyTextGadgetActive())
2466 if (game_status == GAME_MODE_TITLE)
2467 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2468 else if (game_status == GAME_MODE_MAIN)
2469 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2470 else if (game_status == GAME_MODE_LEVELS)
2471 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2472 else if (game_status == GAME_MODE_LEVELNR)
2473 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2474 else if (game_status == GAME_MODE_SETUP)
2475 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2476 else if (game_status == GAME_MODE_INFO)
2477 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2478 else if (game_status == GAME_MODE_SCORES)
2479 HandleHallOfFame(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2484 case GAME_MODE_PLAYING:
2486 // !!! causes immediate GameEnd() when solving MM level with keyboard !!!
2487 if (tape.playing || keyboard)
2488 newbutton = ((joy & JOY_BUTTON) != 0);
2491 if (newbutton && game.all_players_gone)
2498 if (tape.single_step && tape.recording && tape.pausing && !tape.use_mouse)
2500 if (joystick & JOY_ACTION)
2501 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
2503 else if (tape.recording && tape.pausing && !tape.use_mouse)
2505 if (joystick & JOY_ACTION)
2506 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2509 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2510 HandleTileCursor(dx, dy, button);
2519 void HandleSpecialGameControllerButtons(Event *event)
2524 switch (event->type)
2526 case SDL_CONTROLLERBUTTONDOWN:
2527 key_status = KEY_PRESSED;
2530 case SDL_CONTROLLERBUTTONUP:
2531 key_status = KEY_RELEASED;
2538 switch (event->cbutton.button)
2540 case SDL_CONTROLLER_BUTTON_START:
2544 case SDL_CONTROLLER_BUTTON_BACK:
2552 HandleKey(key, key_status);
2555 void HandleSpecialGameControllerKeys(Key key, int key_status)
2557 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2558 int button = SDL_CONTROLLER_BUTTON_INVALID;
2560 // map keys to joystick buttons (special hack for Amazon Fire TV remote)
2561 if (key == KSYM_Rewind)
2562 button = SDL_CONTROLLER_BUTTON_A;
2563 else if (key == KSYM_FastForward || key == KSYM_Menu)
2564 button = SDL_CONTROLLER_BUTTON_B;
2566 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2570 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2571 SDL_CONTROLLERBUTTONUP);
2573 event.cbutton.which = 0; // first joystick (Amazon Fire TV remote)
2574 event.cbutton.button = button;
2575 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2578 HandleJoystickEvent(&event);
2583 boolean DoKeysymAction(int keysym)
2587 Key key = (Key)(-keysym);
2589 HandleKey(key, KEY_PRESSED);
2590 HandleKey(key, KEY_RELEASED);