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 AddUserTreeSetToTreeInfo(top_dir, tree_type);
1552 // when adding new level set, select it as the new current level set
1553 if (tree_type == TREE_TYPE_LEVEL_DIR)
1555 // change current level set to newly added level set from zip file
1556 leveldir_current = getTreeInfoFromIdentifier(leveldir_first, top_dir);
1558 // change current level number to first level of newly added level set
1559 level_nr = leveldir_current->first_level;
1561 // when in main menu, redraw screen to reflect changed level set
1562 if (game_status == GAME_MODE_MAIN)
1569 static void HandleDropTextEventExt(char *text)
1571 Error(ERR_DEBUG, "DROP TEXT EVENT: '%s'", text);
1574 void HandleDropFileEvent(Event *event)
1576 HandleDropFileEventExt(event->drop.file);
1578 SDL_free(event->drop.file);
1581 void HandleDropTextEvent(Event *event)
1583 HandleDropTextEventExt(event->drop.file);
1585 SDL_free(event->drop.file);
1588 void HandleButton(int mx, int my, int button, int button_nr)
1590 static int old_mx = 0, old_my = 0;
1591 boolean button_hold = FALSE;
1592 boolean handle_gadgets = TRUE;
1598 button_nr = -button_nr;
1607 #if defined(PLATFORM_ANDROID)
1608 // when playing, only handle gadgets when using "follow finger" controls
1609 // or when using touch controls in combination with the MM game engine
1610 // or when using gadgets that do not overlap with virtual buttons
1612 (game_status != GAME_MODE_PLAYING ||
1613 level.game_engine_type == GAME_ENGINE_TYPE_MM ||
1614 strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER) ||
1615 (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1616 !virtual_button_pressed));
1619 if (HandleGlobalAnimClicks(mx, my, button))
1621 // do not handle this button event anymore
1622 return; // force mouse event not to be handled at all
1625 if (handle_gadgets && HandleGadgets(mx, my, button))
1627 // do not handle this button event anymore
1628 mx = my = -32; // force mouse event to be outside screen tiles
1631 if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1634 // do not use scroll wheel button events for anything other than gadgets
1635 if (IS_WHEEL_BUTTON(button_nr))
1638 switch (game_status)
1640 case GAME_MODE_TITLE:
1641 HandleTitleScreen(mx, my, 0, 0, button);
1644 case GAME_MODE_MAIN:
1645 HandleMainMenu(mx, my, 0, 0, button);
1648 case GAME_MODE_PSEUDO_TYPENAME:
1649 HandleTypeName(0, KSYM_Return);
1652 case GAME_MODE_LEVELS:
1653 HandleChooseLevelSet(mx, my, 0, 0, button);
1656 case GAME_MODE_LEVELNR:
1657 HandleChooseLevelNr(mx, my, 0, 0, button);
1660 case GAME_MODE_SCORES:
1661 HandleHallOfFame(0, 0, 0, 0, button);
1664 case GAME_MODE_EDITOR:
1665 HandleLevelEditorIdle();
1668 case GAME_MODE_INFO:
1669 HandleInfoScreen(mx, my, 0, 0, button);
1672 case GAME_MODE_SETUP:
1673 HandleSetupScreen(mx, my, 0, 0, button);
1676 case GAME_MODE_PLAYING:
1677 if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_OFF))
1678 HandleButtonOrFinger(mx, my, button);
1680 SetPlayerMouseAction(mx, my, button);
1683 if (button == MB_PRESSED && !motion_status && !button_hold &&
1684 IN_GFX_FIELD_PLAY(mx, my) && GetKeyModState() & KMOD_Control)
1685 DumpTileFromScreen(mx, my);
1695 static boolean is_string_suffix(char *string, char *suffix)
1697 int string_len = strlen(string);
1698 int suffix_len = strlen(suffix);
1700 if (suffix_len > string_len)
1703 return (strEqual(&string[string_len - suffix_len], suffix));
1706 #define MAX_CHEAT_INPUT_LEN 32
1708 static void HandleKeysSpecial(Key key)
1710 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1711 char letter = getCharFromKey(key);
1712 int cheat_input_len = strlen(cheat_input);
1718 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1720 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1721 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1723 cheat_input_len = MAX_CHEAT_INPUT_LEN;
1726 cheat_input[cheat_input_len++] = letter;
1727 cheat_input[cheat_input_len] = '\0';
1729 #if DEBUG_EVENTS_KEY
1730 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1733 if (game_status == GAME_MODE_MAIN)
1735 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1736 is_string_suffix(cheat_input, ":ist"))
1738 InsertSolutionTape();
1740 else if (is_string_suffix(cheat_input, ":play-solution-tape") ||
1741 is_string_suffix(cheat_input, ":pst"))
1745 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1746 is_string_suffix(cheat_input, ":rg"))
1748 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1751 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1752 is_string_suffix(cheat_input, ":rs"))
1754 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1757 else if (is_string_suffix(cheat_input, ":reload-music") ||
1758 is_string_suffix(cheat_input, ":rm"))
1760 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1763 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1764 is_string_suffix(cheat_input, ":ra"))
1766 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1767 1 << ARTWORK_TYPE_SOUNDS |
1768 1 << ARTWORK_TYPE_MUSIC);
1771 else if (is_string_suffix(cheat_input, ":dump-level") ||
1772 is_string_suffix(cheat_input, ":dl"))
1776 else if (is_string_suffix(cheat_input, ":dump-tape") ||
1777 is_string_suffix(cheat_input, ":dt"))
1781 else if (is_string_suffix(cheat_input, ":fix-tape") ||
1782 is_string_suffix(cheat_input, ":ft"))
1784 /* fix single-player tapes that contain player input for more than one
1785 player (due to a bug in 3.3.1.2 and earlier versions), which results
1786 in playing levels with more than one player in multi-player mode,
1787 even though the tape was originally recorded in single-player mode */
1789 // remove player input actions for all players but the first one
1790 for (i = 1; i < MAX_PLAYERS; i++)
1791 tape.player_participates[i] = FALSE;
1793 tape.changed = TRUE;
1795 else if (is_string_suffix(cheat_input, ":save-native-level") ||
1796 is_string_suffix(cheat_input, ":snl"))
1798 SaveNativeLevel(&level);
1800 else if (is_string_suffix(cheat_input, ":frames-per-second") ||
1801 is_string_suffix(cheat_input, ":fps"))
1803 global.show_frames_per_second = !global.show_frames_per_second;
1806 else if (game_status == GAME_MODE_PLAYING)
1809 if (is_string_suffix(cheat_input, ".q"))
1810 DEBUG_SetMaximumDynamite();
1813 else if (game_status == GAME_MODE_EDITOR)
1815 if (is_string_suffix(cheat_input, ":dump-brush") ||
1816 is_string_suffix(cheat_input, ":DB"))
1820 else if (is_string_suffix(cheat_input, ":DDB"))
1825 if (GetKeyModState() & (KMOD_Control | KMOD_Meta))
1827 if (letter == 'x') // copy brush to clipboard (small size)
1829 CopyBrushToClipboard_Small();
1831 else if (letter == 'c') // copy brush to clipboard (normal size)
1833 CopyBrushToClipboard();
1835 else if (letter == 'v') // paste brush from Clipboard
1837 CopyClipboardToBrush();
1842 // special key shortcuts for all game modes
1843 if (is_string_suffix(cheat_input, ":dump-event-actions") ||
1844 is_string_suffix(cheat_input, ":dea") ||
1845 is_string_suffix(cheat_input, ":DEA"))
1847 DumpGadgetIdentifiers();
1848 DumpScreenIdentifiers();
1852 boolean HandleKeysDebug(Key key, int key_status)
1857 if (key_status != KEY_PRESSED)
1860 if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1862 boolean mod_key_pressed = ((GetKeyModState() & KMOD_Valid) != KMOD_None);
1864 for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1866 if (key == setup.debug.frame_delay_key[i] &&
1867 (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1869 GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1870 setup.debug.frame_delay[i] : setup.game_frame_delay);
1872 if (!setup.debug.frame_delay_game_only)
1873 MenuFrameDelay = GameFrameDelay;
1875 SetVideoFrameDelay(GameFrameDelay);
1877 if (GameFrameDelay > ONE_SECOND_DELAY)
1878 Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1879 else if (GameFrameDelay != 0)
1880 Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1881 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1882 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1884 Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1891 if (game_status == GAME_MODE_PLAYING)
1895 options.debug = !options.debug;
1897 Error(ERR_DEBUG, "debug mode %s",
1898 (options.debug ? "enabled" : "disabled"));
1902 else if (key == KSYM_v)
1904 Error(ERR_DEBUG, "currently using game engine version %d",
1905 game.engine_version);
1915 void HandleKey(Key key, int key_status)
1917 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1918 static boolean ignore_repeated_key = FALSE;
1919 static struct SetupKeyboardInfo ski;
1920 static struct SetupShortcutInfo ssi;
1929 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
1930 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
1931 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
1932 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
1933 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
1934 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
1939 if (HandleKeysDebug(key, key_status))
1940 return; // do not handle already processed keys again
1942 // map special keys (media keys / remote control buttons) to default keys
1943 if (key == KSYM_PlayPause)
1945 else if (key == KSYM_Select)
1948 HandleSpecialGameControllerKeys(key, key_status);
1950 if (game_status == GAME_MODE_PLAYING)
1952 // only needed for single-step tape recording mode
1953 static boolean has_snapped[MAX_PLAYERS] = { FALSE, FALSE, FALSE, FALSE };
1956 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1958 byte key_action = 0;
1960 if (setup.input[pnr].use_joystick)
1963 ski = setup.input[pnr].key;
1965 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1966 if (key == *key_info[i].key_custom)
1967 key_action |= key_info[i].action;
1969 // use combined snap+direction keys for the first player only
1972 ssi = setup.shortcut;
1974 for (i = 0; i < NUM_DIRECTIONS; i++)
1975 if (key == *key_info[i].key_snap)
1976 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1979 if (key_status == KEY_PRESSED)
1980 stored_player[pnr].action |= key_action;
1982 stored_player[pnr].action &= ~key_action;
1984 if (tape.single_step && tape.recording && tape.pausing && !tape.use_mouse)
1986 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1988 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1990 // if snap key already pressed, keep pause mode when releasing
1991 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1992 has_snapped[pnr] = TRUE;
1994 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1996 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1998 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1999 getRedDiskReleaseFlag_SP() == 0)
2001 // add a single inactive frame before dropping starts
2002 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
2003 stored_player[pnr].force_dropping = TRUE;
2006 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON_SNAP)
2008 // if snap key was pressed without direction, leave pause mode
2009 if (!has_snapped[pnr])
2010 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
2012 has_snapped[pnr] = FALSE;
2015 else if (tape.recording && tape.pausing && !tape.use_mouse)
2017 // prevent key release events from un-pausing a paused game
2018 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
2019 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2022 // for MM style levels, handle in-game keyboard input in HandleJoystick()
2023 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2029 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
2030 if (key == key_info[i].key_default)
2031 joy |= key_info[i].action;
2036 if (key_status == KEY_PRESSED)
2037 key_joystick_mapping |= joy;
2039 key_joystick_mapping &= ~joy;
2044 if (game_status != GAME_MODE_PLAYING)
2045 key_joystick_mapping = 0;
2047 if (key_status == KEY_RELEASED)
2049 // reset flag to ignore repeated "key pressed" events after key release
2050 ignore_repeated_key = FALSE;
2055 if ((key == KSYM_F11 ||
2056 ((key == KSYM_Return ||
2057 key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
2058 video.fullscreen_available &&
2059 !ignore_repeated_key)
2061 setup.fullscreen = !setup.fullscreen;
2063 ToggleFullscreenOrChangeWindowScalingIfNeeded();
2065 if (game_status == GAME_MODE_SETUP)
2066 RedrawSetupScreenAfterFullscreenToggle();
2068 // set flag to ignore repeated "key pressed" events
2069 ignore_repeated_key = TRUE;
2074 if ((key == KSYM_0 || key == KSYM_KP_0 ||
2075 key == KSYM_minus || key == KSYM_KP_Subtract ||
2076 key == KSYM_plus || key == KSYM_KP_Add ||
2077 key == KSYM_equal) && // ("Shift-=" is "+" on US keyboards)
2078 (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
2079 video.window_scaling_available &&
2080 !video.fullscreen_enabled)
2082 if (key == KSYM_0 || key == KSYM_KP_0)
2083 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
2084 else if (key == KSYM_minus || key == KSYM_KP_Subtract)
2085 setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
2087 setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
2089 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
2090 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
2091 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
2092 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
2094 ToggleFullscreenOrChangeWindowScalingIfNeeded();
2096 if (game_status == GAME_MODE_SETUP)
2097 RedrawSetupScreenAfterFullscreenToggle();
2102 if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
2103 key == KSYM_Return ||
2104 key == KSYM_Escape)))
2106 // do not handle this key event anymore
2107 if (key != KSYM_Escape) // always allow ESC key to be handled
2111 if (game_status == GAME_MODE_PLAYING && game.all_players_gone &&
2112 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
2119 if (game_status == GAME_MODE_MAIN &&
2120 (key == setup.shortcut.toggle_pause || key == KSYM_space))
2122 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
2127 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
2129 if (key == setup.shortcut.save_game)
2131 else if (key == setup.shortcut.load_game)
2133 else if (key == setup.shortcut.toggle_pause)
2134 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
2136 HandleTapeButtonKeys(key);
2137 HandleSoundButtonKeys(key);
2140 if (game_status == GAME_MODE_PLAYING && !network_playing)
2142 int centered_player_nr_next = -999;
2144 if (key == setup.shortcut.focus_player_all)
2145 centered_player_nr_next = -1;
2147 for (i = 0; i < MAX_PLAYERS; i++)
2148 if (key == setup.shortcut.focus_player[i])
2149 centered_player_nr_next = i;
2151 if (centered_player_nr_next != -999)
2153 game.centered_player_nr_next = centered_player_nr_next;
2154 game.set_centered_player = TRUE;
2158 tape.centered_player_nr_next = game.centered_player_nr_next;
2159 tape.set_centered_player = TRUE;
2164 HandleKeysSpecial(key);
2166 if (HandleGadgetsKeyInput(key))
2167 return; // do not handle already processed keys again
2169 switch (game_status)
2171 case GAME_MODE_PSEUDO_TYPENAME:
2172 HandleTypeName(0, key);
2175 case GAME_MODE_TITLE:
2176 case GAME_MODE_MAIN:
2177 case GAME_MODE_LEVELS:
2178 case GAME_MODE_LEVELNR:
2179 case GAME_MODE_SETUP:
2180 case GAME_MODE_INFO:
2181 case GAME_MODE_SCORES:
2183 if (anyTextGadgetActiveOrJustFinished && key != KSYM_Escape)
2190 if (game_status == GAME_MODE_TITLE)
2191 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2192 else if (game_status == GAME_MODE_MAIN)
2193 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
2194 else if (game_status == GAME_MODE_LEVELS)
2195 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
2196 else if (game_status == GAME_MODE_LEVELNR)
2197 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
2198 else if (game_status == GAME_MODE_SETUP)
2199 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2200 else if (game_status == GAME_MODE_INFO)
2201 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
2202 else if (game_status == GAME_MODE_SCORES)
2203 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
2207 if (game_status != GAME_MODE_MAIN)
2208 FadeSkipNextFadeIn();
2210 if (game_status == GAME_MODE_TITLE)
2211 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2212 else if (game_status == GAME_MODE_LEVELS)
2213 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
2214 else if (game_status == GAME_MODE_LEVELNR)
2215 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
2216 else if (game_status == GAME_MODE_SETUP)
2217 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2218 else if (game_status == GAME_MODE_INFO)
2219 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
2220 else if (game_status == GAME_MODE_SCORES)
2221 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
2225 if (game_status == GAME_MODE_LEVELS)
2226 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2227 else if (game_status == GAME_MODE_LEVELNR)
2228 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2229 else if (game_status == GAME_MODE_SETUP)
2230 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2231 else if (game_status == GAME_MODE_INFO)
2232 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2233 else if (game_status == GAME_MODE_SCORES)
2234 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
2237 case KSYM_Page_Down:
2238 if (game_status == GAME_MODE_LEVELS)
2239 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2240 else if (game_status == GAME_MODE_LEVELNR)
2241 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2242 else if (game_status == GAME_MODE_SETUP)
2243 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2244 else if (game_status == GAME_MODE_INFO)
2245 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2246 else if (game_status == GAME_MODE_SCORES)
2247 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
2255 case GAME_MODE_EDITOR:
2256 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
2257 HandleLevelEditorKeyInput(key);
2260 case GAME_MODE_PLAYING:
2265 RequestQuitGame(setup.ask_on_escape);
2275 if (key == KSYM_Escape)
2277 SetGameStatus(GAME_MODE_MAIN);
2286 void HandleNoEvent(void)
2288 HandleMouseCursor();
2290 switch (game_status)
2292 case GAME_MODE_PLAYING:
2293 HandleButtonOrFinger(-1, -1, -1);
2298 void HandleEventActions(void)
2300 // if (button_status && game_status != GAME_MODE_PLAYING)
2301 if (button_status && (game_status != GAME_MODE_PLAYING ||
2303 level.game_engine_type == GAME_ENGINE_TYPE_MM))
2305 HandleButton(0, 0, button_status, -button_status);
2312 if (network.enabled)
2315 switch (game_status)
2317 case GAME_MODE_MAIN:
2318 DrawPreviewLevelAnimation();
2321 case GAME_MODE_EDITOR:
2322 HandleLevelEditorIdle();
2330 static void HandleTileCursor(int dx, int dy, int button)
2333 ClearPlayerMouseAction();
2340 SetPlayerMouseAction(tile_cursor.x, tile_cursor.y,
2341 (dx < 0 ? MB_LEFTBUTTON :
2342 dx > 0 ? MB_RIGHTBUTTON : MB_RELEASED));
2344 else if (!tile_cursor.moving)
2346 int old_xpos = tile_cursor.xpos;
2347 int old_ypos = tile_cursor.ypos;
2348 int new_xpos = old_xpos;
2349 int new_ypos = old_ypos;
2351 if (IN_LEV_FIELD(old_xpos + dx, old_ypos))
2352 new_xpos = old_xpos + dx;
2354 if (IN_LEV_FIELD(old_xpos, old_ypos + dy))
2355 new_ypos = old_ypos + dy;
2357 SetTileCursorTargetXY(new_xpos, new_ypos);
2361 static int HandleJoystickForAllPlayers(void)
2365 boolean no_joysticks_configured = TRUE;
2366 boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
2367 static byte joy_action_last[MAX_PLAYERS];
2369 for (i = 0; i < MAX_PLAYERS; i++)
2370 if (setup.input[i].use_joystick)
2371 no_joysticks_configured = FALSE;
2373 // if no joysticks configured, map connected joysticks to players
2374 if (no_joysticks_configured)
2375 use_as_joystick_nr = TRUE;
2377 for (i = 0; i < MAX_PLAYERS; i++)
2379 byte joy_action = 0;
2381 joy_action = JoystickExt(i, use_as_joystick_nr);
2382 result |= joy_action;
2384 if ((setup.input[i].use_joystick || no_joysticks_configured) &&
2385 joy_action != joy_action_last[i])
2386 stored_player[i].action = joy_action;
2388 joy_action_last[i] = joy_action;
2394 void HandleJoystick(void)
2396 static unsigned int joytest_delay = 0;
2397 static unsigned int joytest_delay_value = GADGET_FRAME_DELAY;
2398 static int joytest_last = 0;
2399 int delay_value_first = GADGET_FRAME_DELAY_FIRST;
2400 int delay_value = GADGET_FRAME_DELAY;
2401 int joystick = HandleJoystickForAllPlayers();
2402 int keyboard = key_joystick_mapping;
2403 int joy = (joystick | keyboard);
2404 int joytest = joystick;
2405 int left = joy & JOY_LEFT;
2406 int right = joy & JOY_RIGHT;
2407 int up = joy & JOY_UP;
2408 int down = joy & JOY_DOWN;
2409 int button = joy & JOY_BUTTON;
2410 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
2411 int dx = (left ? -1 : right ? 1 : 0);
2412 int dy = (up ? -1 : down ? 1 : 0);
2413 boolean use_delay_value_first = (joytest != joytest_last);
2415 if (HandleGlobalAnimClicks(-1, -1, newbutton))
2417 // do not handle this button event anymore
2421 if (newbutton && (game_status == GAME_MODE_PSEUDO_TYPENAME ||
2422 anyTextGadgetActive()))
2424 // leave name input in main menu or text input gadget
2425 HandleKey(KSYM_Escape, KEY_PRESSED);
2426 HandleKey(KSYM_Escape, KEY_RELEASED);
2431 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2433 if (game_status == GAME_MODE_PLAYING)
2435 // when playing MM style levels, also use delay for keyboard events
2436 joytest |= keyboard;
2438 // only use first delay value for new events, but not for changed events
2439 use_delay_value_first = (!joytest != !joytest_last);
2441 // only use delay after the initial keyboard event
2445 // for any joystick or keyboard event, enable playfield tile cursor
2446 if (dx || dy || button)
2447 SetTileCursorEnabled(TRUE);
2450 if (joytest && !button && !DelayReached(&joytest_delay, joytest_delay_value))
2452 // delay joystick/keyboard actions if axes/keys continually pressed
2453 newbutton = dx = dy = 0;
2457 // first start with longer delay, then continue with shorter delay
2458 joytest_delay_value =
2459 (use_delay_value_first ? delay_value_first : delay_value);
2462 joytest_last = joytest;
2464 switch (game_status)
2466 case GAME_MODE_TITLE:
2467 case GAME_MODE_MAIN:
2468 case GAME_MODE_LEVELS:
2469 case GAME_MODE_LEVELNR:
2470 case GAME_MODE_SETUP:
2471 case GAME_MODE_INFO:
2472 case GAME_MODE_SCORES:
2474 if (anyTextGadgetActive())
2477 if (game_status == GAME_MODE_TITLE)
2478 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2479 else if (game_status == GAME_MODE_MAIN)
2480 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2481 else if (game_status == GAME_MODE_LEVELS)
2482 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2483 else if (game_status == GAME_MODE_LEVELNR)
2484 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2485 else if (game_status == GAME_MODE_SETUP)
2486 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2487 else if (game_status == GAME_MODE_INFO)
2488 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2489 else if (game_status == GAME_MODE_SCORES)
2490 HandleHallOfFame(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2495 case GAME_MODE_PLAYING:
2497 // !!! causes immediate GameEnd() when solving MM level with keyboard !!!
2498 if (tape.playing || keyboard)
2499 newbutton = ((joy & JOY_BUTTON) != 0);
2502 if (newbutton && game.all_players_gone)
2509 if (tape.single_step && tape.recording && tape.pausing && !tape.use_mouse)
2511 if (joystick & JOY_ACTION)
2512 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
2514 else if (tape.recording && tape.pausing && !tape.use_mouse)
2516 if (joystick & JOY_ACTION)
2517 TapeTogglePause(TAPE_TOGGLE_MANUAL);
2520 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
2521 HandleTileCursor(dx, dy, button);
2530 void HandleSpecialGameControllerButtons(Event *event)
2535 switch (event->type)
2537 case SDL_CONTROLLERBUTTONDOWN:
2538 key_status = KEY_PRESSED;
2541 case SDL_CONTROLLERBUTTONUP:
2542 key_status = KEY_RELEASED;
2549 switch (event->cbutton.button)
2551 case SDL_CONTROLLER_BUTTON_START:
2555 case SDL_CONTROLLER_BUTTON_BACK:
2563 HandleKey(key, key_status);
2566 void HandleSpecialGameControllerKeys(Key key, int key_status)
2568 #if defined(KSYM_Rewind) && defined(KSYM_FastForward)
2569 int button = SDL_CONTROLLER_BUTTON_INVALID;
2571 // map keys to joystick buttons (special hack for Amazon Fire TV remote)
2572 if (key == KSYM_Rewind)
2573 button = SDL_CONTROLLER_BUTTON_A;
2574 else if (key == KSYM_FastForward || key == KSYM_Menu)
2575 button = SDL_CONTROLLER_BUTTON_B;
2577 if (button != SDL_CONTROLLER_BUTTON_INVALID)
2581 event.type = (key_status == KEY_PRESSED ? SDL_CONTROLLERBUTTONDOWN :
2582 SDL_CONTROLLERBUTTONUP);
2584 event.cbutton.which = 0; // first joystick (Amazon Fire TV remote)
2585 event.cbutton.button = button;
2586 event.cbutton.state = (key_status == KEY_PRESSED ? SDL_PRESSED :
2589 HandleJoystickEvent(&event);
2594 boolean DoKeysymAction(int keysym)
2598 Key key = (Key)(-keysym);
2600 HandleKey(key, KEY_PRESSED);
2601 HandleKey(key, KEY_RELEASED);