added handling 'back' and 'start' buttons on game controllers
[rocksndiamonds.git] / src / events.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // events.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "events.h"
15 #include "init.h"
16 #include "screens.h"
17 #include "tools.h"
18 #include "game.h"
19 #include "editor.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "anim.h"
23 #include "network.h"
24
25
26 #define DEBUG_EVENTS            0
27
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)
35
36
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;
41
42 /* event filter especially needed for SDL event filtering due to
43    delay problems with lots of mouse motion events when mouse button
44    not pressed (X11 can handle this with 'PointerMotionHintMask') */
45
46 /* event filter addition for SDL2: as SDL2 does not have a function to enable
47    or disable keyboard auto-repeat, filter repeated keyboard events instead */
48
49 static int FilterEvents(const Event *event)
50 {
51   MotionEvent *motion;
52
53 #if defined(TARGET_SDL2)
54   /* skip repeated key press events if keyboard auto-repeat is disabled */
55   if (event->type == EVENT_KEYPRESS &&
56       event->key.repeat &&
57       !keyrepeat_status)
58     return 0;
59 #endif
60
61   if (event->type == EVENT_BUTTONPRESS ||
62       event->type == EVENT_BUTTONRELEASE)
63   {
64     ((ButtonEvent *)event)->x -= video.screen_xoffset;
65     ((ButtonEvent *)event)->y -= video.screen_yoffset;
66   }
67   else if (event->type == EVENT_MOTIONNOTIFY)
68   {
69     ((MotionEvent *)event)->x -= video.screen_xoffset;
70     ((MotionEvent *)event)->y -= video.screen_yoffset;
71   }
72
73   /* non-motion events are directly passed to event handler functions */
74   if (event->type != EVENT_MOTIONNOTIFY)
75     return 1;
76
77   motion = (MotionEvent *)event;
78   cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
79                              motion->y >= SY && motion->y < SY + SYSIZE);
80
81   /* do no reset mouse cursor before all pending events have been processed */
82   if (gfx.cursor_mode == cursor_mode_last &&
83       ((game_status == GAME_MODE_TITLE &&
84         gfx.cursor_mode == CURSOR_NONE) ||
85        (game_status == GAME_MODE_PLAYING &&
86         gfx.cursor_mode == CURSOR_PLAYFIELD)))
87   {
88     SetMouseCursor(CURSOR_DEFAULT);
89
90     DelayReached(&special_cursor_delay, 0);
91
92     cursor_mode_last = CURSOR_DEFAULT;
93   }
94
95   /* skip mouse motion events without pressed button outside level editor */
96   if (button_status == MB_RELEASED &&
97       game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
98     return 0;
99
100   return 1;
101 }
102
103 /* to prevent delay problems, skip mouse motion events if the very next
104    event is also a mouse motion event (and therefore effectively only
105    handling the last of a row of mouse motion events in the event queue) */
106
107 static boolean SkipPressedMouseMotionEvent(const Event *event)
108 {
109   /* nothing to do if the current event is not a mouse motion event */
110   if (event->type != EVENT_MOTIONNOTIFY)
111     return FALSE;
112
113   /* only skip motion events with pressed button outside the game */
114   if (button_status == MB_RELEASED || game_status == GAME_MODE_PLAYING)
115     return FALSE;
116
117   if (PendingEvent())
118   {
119     Event next_event;
120
121     PeekEvent(&next_event);
122
123     /* if next event is also a mouse motion event, skip the current one */
124     if (next_event.type == EVENT_MOTIONNOTIFY)
125       return TRUE;
126   }
127
128   return FALSE;
129 }
130
131 /* this is especially needed for event modifications for the Android target:
132    if mouse coordinates should be modified in the event filter function,
133    using a properly installed SDL event filter does not work, because in
134    the event filter, mouse coordinates in the event structure are still
135    physical pixel positions, not logical (scaled) screen positions, so this
136    has to be handled at a later stage in the event processing functions
137    (when device pixel positions are already converted to screen positions) */
138
139 boolean NextValidEvent(Event *event)
140 {
141   while (PendingEvent())
142   {
143     boolean handle_this_event = FALSE;
144
145     NextEvent(event);
146
147     if (FilterEvents(event))
148       handle_this_event = TRUE;
149
150     if (SkipPressedMouseMotionEvent(event))
151       handle_this_event = FALSE;
152
153     if (handle_this_event)
154       return TRUE;
155   }
156
157   return FALSE;
158 }
159
160 void HandleEvents()
161 {
162   Event event;
163   unsigned int event_frame_delay = 0;
164   unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
165
166   ResetDelayCounter(&event_frame_delay);
167
168   while (NextValidEvent(&event))
169   {
170     switch (event.type)
171     {
172       case EVENT_BUTTONPRESS:
173       case EVENT_BUTTONRELEASE:
174         HandleButtonEvent((ButtonEvent *) &event);
175         break;
176
177       case EVENT_MOTIONNOTIFY:
178         HandleMotionEvent((MotionEvent *) &event);
179         break;
180
181 #if defined(TARGET_SDL2)
182       case EVENT_WHEELMOTION:
183         HandleWheelEvent((WheelEvent *) &event);
184         break;
185
186       case SDL_WINDOWEVENT:
187         HandleWindowEvent((WindowEvent *) &event);
188         break;
189
190       case EVENT_FINGERPRESS:
191       case EVENT_FINGERRELEASE:
192       case EVENT_FINGERMOTION:
193         HandleFingerEvent((FingerEvent *) &event);
194         break;
195
196       case EVENT_TEXTINPUT:
197         HandleTextEvent((TextEvent *) &event);
198         break;
199
200       case SDL_APP_WILLENTERBACKGROUND:
201       case SDL_APP_DIDENTERBACKGROUND:
202       case SDL_APP_WILLENTERFOREGROUND:
203       case SDL_APP_DIDENTERFOREGROUND:
204         HandlePauseResumeEvent((PauseResumeEvent *) &event);
205         break;
206 #endif
207
208       case EVENT_KEYPRESS:
209       case EVENT_KEYRELEASE:
210         HandleKeyEvent((KeyEvent *) &event);
211         break;
212
213       default:
214         HandleOtherEvents(&event);
215         break;
216     }
217
218     // do not handle events for longer than standard frame delay period
219     if (DelayReached(&event_frame_delay, event_frame_delay_value))
220       break;
221   }
222 }
223
224 void HandleOtherEvents(Event *event)
225 {
226   switch (event->type)
227   {
228     case EVENT_EXPOSE:
229       HandleExposeEvent((ExposeEvent *) event);
230       break;
231
232     case EVENT_UNMAPNOTIFY:
233 #if 0
234       /* This causes the game to stop not only when iconified, but also
235          when on another virtual desktop, which might be not desired. */
236       SleepWhileUnmapped();
237 #endif
238       break;
239
240     case EVENT_FOCUSIN:
241     case EVENT_FOCUSOUT:
242       HandleFocusEvent((FocusChangeEvent *) event);
243       break;
244
245     case EVENT_CLIENTMESSAGE:
246       HandleClientMessageEvent((ClientMessageEvent *) event);
247       break;
248
249 #if defined(TARGET_SDL)
250 #if defined(TARGET_SDL2)
251     case SDL_CONTROLLERBUTTONDOWN:
252     case SDL_CONTROLLERBUTTONUP:
253       HandleSpecialGameControllerButtons(event);
254       /* FALL THROUGH */
255     case SDL_CONTROLLERDEVICEADDED:
256     case SDL_CONTROLLERDEVICEREMOVED:
257     case SDL_CONTROLLERAXISMOTION:
258 #endif
259     case SDL_JOYAXISMOTION:
260     case SDL_JOYBUTTONDOWN:
261     case SDL_JOYBUTTONUP:
262       HandleJoystickEvent(event);
263       break;
264
265     case SDL_SYSWMEVENT:
266       HandleWindowManagerEvent(event);
267       break;
268 #endif
269
270     default:
271       break;
272   }
273 }
274
275 void HandleMouseCursor()
276 {
277   if (game_status == GAME_MODE_TITLE)
278   {
279     /* when showing title screens, hide mouse pointer (if not moved) */
280
281     if (gfx.cursor_mode != CURSOR_NONE &&
282         DelayReached(&special_cursor_delay, special_cursor_delay_value))
283     {
284       SetMouseCursor(CURSOR_NONE);
285     }
286   }
287   else if (game_status == GAME_MODE_PLAYING && (!tape.pausing ||
288                                                 tape.single_step))
289   {
290     /* when playing, display a special mouse pointer inside the playfield */
291
292     if (gfx.cursor_mode != CURSOR_PLAYFIELD &&
293         cursor_inside_playfield &&
294         DelayReached(&special_cursor_delay, special_cursor_delay_value))
295     {
296       SetMouseCursor(CURSOR_PLAYFIELD);
297     }
298   }
299   else if (gfx.cursor_mode != CURSOR_DEFAULT)
300   {
301     SetMouseCursor(CURSOR_DEFAULT);
302   }
303
304   /* this is set after all pending events have been processed */
305   cursor_mode_last = gfx.cursor_mode;
306 }
307
308 void EventLoop(void)
309 {
310   while (1)
311   {
312     if (PendingEvent())
313       HandleEvents();
314     else
315       HandleMouseCursor();
316
317     /* also execute after pending events have been processed before */
318     HandleNoEvent();
319
320     /* don't use all CPU time when idle; the main loop while playing
321        has its own synchronization and is CPU friendly, too */
322
323     if (game_status == GAME_MODE_PLAYING)
324       HandleGameActions();
325
326     /* always copy backbuffer to visible screen for every video frame */
327     BackToFront();
328
329     /* reset video frame delay to default (may change again while playing) */
330     SetVideoFrameDelay(MenuFrameDelay);
331
332     if (game_status == GAME_MODE_QUIT)
333       return;
334   }
335 }
336
337 void ClearEventQueue()
338 {
339   while (PendingEvent())
340   {
341     Event event;
342
343     NextEvent(&event);
344
345     switch (event.type)
346     {
347       case EVENT_BUTTONRELEASE:
348         button_status = MB_RELEASED;
349         break;
350
351       case EVENT_KEYRELEASE:
352         ClearPlayerAction();
353         break;
354
355 #if defined(TARGET_SDL2)
356       case SDL_CONTROLLERBUTTONUP:
357         ClearPlayerAction();
358         break;
359 #endif
360
361       default:
362         HandleOtherEvents(&event);
363         break;
364     }
365   }
366 }
367
368 void ClearPlayerAction()
369 {
370   int i;
371
372   /* simulate key release events for still pressed keys */
373   key_joystick_mapping = 0;
374   for (i = 0; i < MAX_PLAYERS; i++)
375     stored_player[i].action = 0;
376 }
377
378 void SleepWhileUnmapped()
379 {
380   boolean window_unmapped = TRUE;
381
382   KeyboardAutoRepeatOn();
383
384   while (window_unmapped)
385   {
386     Event event;
387
388     NextEvent(&event);
389
390     switch (event.type)
391     {
392       case EVENT_BUTTONRELEASE:
393         button_status = MB_RELEASED;
394         break;
395
396       case EVENT_KEYRELEASE:
397         key_joystick_mapping = 0;
398         break;
399
400 #if defined(TARGET_SDL2)
401       case SDL_CONTROLLERBUTTONUP:
402         key_joystick_mapping = 0;
403         break;
404 #endif
405
406       case EVENT_MAPNOTIFY:
407         window_unmapped = FALSE;
408         break;
409
410       case EVENT_UNMAPNOTIFY:
411         /* this is only to surely prevent the 'should not happen' case
412          * of recursively looping between 'SleepWhileUnmapped()' and
413          * 'HandleOtherEvents()' which usually calls this funtion.
414          */
415         break;
416
417       default:
418         HandleOtherEvents(&event);
419         break;
420     }
421   }
422
423   if (game_status == GAME_MODE_PLAYING)
424     KeyboardAutoRepeatOffUnlessAutoplay();
425 }
426
427 void HandleExposeEvent(ExposeEvent *event)
428 {
429 }
430
431 void HandleButtonEvent(ButtonEvent *event)
432 {
433 #if DEBUG_EVENTS_BUTTON
434   Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
435         event->button,
436         event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
437         event->x, event->y);
438 #endif
439
440 #if defined(HAS_SCREEN_KEYBOARD)
441   if (video.shifted_up)
442     event->y += video.shifted_up_pos;
443 #endif
444
445   motion_status = FALSE;
446
447   if (event->type == EVENT_BUTTONPRESS)
448     button_status = event->button;
449   else
450     button_status = MB_RELEASED;
451
452   HandleButton(event->x, event->y, button_status, event->button);
453 }
454
455 void HandleMotionEvent(MotionEvent *event)
456 {
457   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
458     return;
459
460   motion_status = TRUE;
461
462 #if DEBUG_EVENTS_MOTION
463   Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
464         button_status, event->x, event->y);
465 #endif
466
467   HandleButton(event->x, event->y, button_status, button_status);
468 }
469
470 #if defined(TARGET_SDL2)
471
472 void HandleWheelEvent(WheelEvent *event)
473 {
474   int button_nr;
475
476 #if DEBUG_EVENTS_WHEEL
477 #if 1
478   Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d\n",
479         event->which, event->x, event->y);
480 #else
481   // (SDL_MOUSEWHEEL_NORMAL/SDL_MOUSEWHEEL_FLIPPED needs SDL 2.0.4 or newer)
482   Error(ERR_DEBUG, "WHEEL EVENT: mouse == %d, x/y == %d/%d, direction == %s\n",
483         event->which, event->x, event->y,
484         (event->direction == SDL_MOUSEWHEEL_NORMAL ? "SDL_MOUSEWHEEL_NORMAL" :
485          "SDL_MOUSEWHEEL_FLIPPED"));
486 #endif
487 #endif
488
489   button_nr = (event->x < 0 ? MB_WHEEL_LEFT :
490                event->x > 0 ? MB_WHEEL_RIGHT :
491                event->y < 0 ? MB_WHEEL_DOWN :
492                event->y > 0 ? MB_WHEEL_UP : 0);
493
494 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MACOSX)
495   // accelerated mouse wheel available on Mac and Windows
496   wheel_steps = (event->x ? ABS(event->x) : ABS(event->y));
497 #else
498   // no accelerated mouse wheel available on Unix/Linux
499   wheel_steps = DEFAULT_WHEEL_STEPS;
500 #endif
501
502   motion_status = FALSE;
503
504   button_status = button_nr;
505   HandleButton(0, 0, button_status, -button_nr);
506
507   button_status = MB_RELEASED;
508   HandleButton(0, 0, button_status, -button_nr);
509 }
510
511 void HandleWindowEvent(WindowEvent *event)
512 {
513 #if DEBUG_EVENTS_WINDOW
514   int subtype = event->event;
515
516   char *event_name =
517     (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
518      subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
519      subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
520      subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
521      subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
522      subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
523      subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
524      subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
525      subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
526      subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
527      subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
528      subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
529      subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
530      subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
531      "(UNKNOWN)");
532
533   Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
534         event_name, event->data1, event->data2);
535 #endif
536
537 #if 0
538   // (not needed, as the screen gets redrawn every 20 ms anyway)
539   if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
540       event->event == SDL_WINDOWEVENT_RESIZED ||
541       event->event == SDL_WINDOWEVENT_EXPOSED)
542     SDLRedrawWindow();
543 #endif
544
545   if (event->event == SDL_WINDOWEVENT_RESIZED)
546   {
547     if (!video.fullscreen_enabled)
548     {
549       int new_window_width  = event->data1;
550       int new_window_height = event->data2;
551
552       // if window size has changed after resizing, calculate new scaling factor
553       if (new_window_width  != video.window_width ||
554           new_window_height != video.window_height)
555       {
556         int new_xpercent = 100.0 * new_window_width  / video.screen_width  + .5;
557         int new_ypercent = 100.0 * new_window_height / video.screen_height + .5;
558
559         // (extreme window scaling allowed, but cannot be saved permanently)
560         video.window_scaling_percent = MIN(new_xpercent, new_ypercent);
561         setup.window_scaling_percent =
562           MIN(MAX(MIN_WINDOW_SCALING_PERCENT, video.window_scaling_percent),
563               MAX_WINDOW_SCALING_PERCENT);
564
565         video.window_width  = new_window_width;
566         video.window_height = new_window_height;
567
568         if (game_status == GAME_MODE_SETUP)
569           RedrawSetupScreenAfterFullscreenToggle();
570
571         SetWindowTitle();
572       }
573     }
574 #if defined(PLATFORM_ANDROID)
575     else
576     {
577       int new_display_width  = event->data1;
578       int new_display_height = event->data2;
579
580       // if fullscreen display size has changed, device has been rotated
581       if (new_display_width  != video.display_width ||
582           new_display_height != video.display_height)
583       {
584         video.display_width  = new_display_width;
585         video.display_height = new_display_height;
586
587         SDLSetScreenProperties();
588       }
589     }
590 #endif
591   }
592 }
593
594 #define NUM_TOUCH_FINGERS               3
595
596 static struct
597 {
598   boolean touched;
599   SDL_FingerID finger_id;
600   int counter;
601   Key key;
602 } touch_info[NUM_TOUCH_FINGERS];
603
604 void HandleFingerEvent(FingerEvent *event)
605 {
606   static Key motion_key_x = KSYM_UNDEFINED;
607   static Key motion_key_y = KSYM_UNDEFINED;
608   static Key button_key = KSYM_UNDEFINED;
609   static float motion_x1, motion_y1;
610   static float button_x1, button_y1;
611   static SDL_FingerID motion_id = -1;
612   static SDL_FingerID button_id = -1;
613   int move_trigger_distance_percent = setup.touch.move_distance;
614   int drop_trigger_distance_percent = setup.touch.drop_distance;
615   float move_trigger_distance = (float)move_trigger_distance_percent / 100;
616   float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
617   float event_x = event->x;
618   float event_y = event->y;
619
620 #if DEBUG_EVENTS_FINGER
621   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
622         event->type == EVENT_FINGERPRESS ? "pressed" :
623         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
624         event->touchId,
625         event->fingerId,
626         event->x, event->y,
627         event->dx, event->dy,
628         event->pressure);
629 #endif
630
631   if (game_status != GAME_MODE_PLAYING)
632     return;
633
634   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
635     return;
636
637   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
638   {
639     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
640                       KEY_PRESSED);
641     float ypos = 1.0 - 1.0 / 3.0 * video.display_width / video.display_height;
642
643     event_y = (event_y - ypos) / (1 - ypos);
644
645     Key key = (event_x > 0         && event_x < 1.0 / 6.0 &&
646                event_y > 2.0 / 3.0 && event_y < 1 ?
647                setup.input[0].key.snap :
648                event_x > 1.0 / 6.0 && event_x < 1.0 / 3.0 &&
649                event_y > 2.0 / 3.0 && event_y < 1 ?
650                setup.input[0].key.drop :
651                event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
652                event_y > 0         && event_y < 1.0 / 3.0 ?
653                setup.input[0].key.up :
654                event_x > 6.0 / 9.0 && event_x < 7.0 / 9.0 &&
655                event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
656                setup.input[0].key.left :
657                event_x > 8.0 / 9.0 && event_x < 1 &&
658                event_y > 1.0 / 3.0 && event_y < 2.0 / 3.0 ?
659                setup.input[0].key.right :
660                event_x > 7.0 / 9.0 && event_x < 8.0 / 9.0 &&
661                event_y > 2.0 / 3.0 && event_y < 1 ?
662                setup.input[0].key.down :
663                KSYM_UNDEFINED);
664
665     char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
666                              "KEY_PRESSED");
667     int i;
668
669     Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
670           getKeyNameFromKey(key), key_status_name, event->fingerId);
671
672     // check if we already know this touch event's finger id
673     for (i = 0; i < NUM_TOUCH_FINGERS; i++)
674     {
675       if (touch_info[i].touched &&
676           touch_info[i].finger_id == event->fingerId)
677       {
678         // Error(ERR_DEBUG, "MARK 1: %d", i);
679
680         break;
681       }
682     }
683
684     if (i >= NUM_TOUCH_FINGERS)
685     {
686       if (key_status == KEY_PRESSED)
687       {
688         int oldest_pos = 0, oldest_counter = touch_info[0].counter;
689
690         // unknown finger id -- get new, empty slot, if available
691         for (i = 0; i < NUM_TOUCH_FINGERS; i++)
692         {
693           if (touch_info[i].counter < oldest_counter)
694           {
695             oldest_pos = i;
696             oldest_counter = touch_info[i].counter;
697
698             // Error(ERR_DEBUG, "MARK 2: %d", i);
699           }
700
701           if (!touch_info[i].touched)
702           {
703             // Error(ERR_DEBUG, "MARK 3: %d", i);
704
705             break;
706           }
707         }
708
709         if (i >= NUM_TOUCH_FINGERS)
710         {
711           // all slots allocated -- use oldest slot
712           i = oldest_pos;
713
714           // Error(ERR_DEBUG, "MARK 4: %d", i);
715         }
716       }
717       else
718       {
719         // release of previously unknown key (should not happen)
720
721         if (key != KSYM_UNDEFINED)
722         {
723           HandleKey(key, KEY_RELEASED);
724
725           Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
726                 getKeyNameFromKey(key), "KEY_RELEASED", i);
727         }
728       }
729     }
730
731     if (i < NUM_TOUCH_FINGERS)
732     {
733       if (key_status == KEY_PRESSED)
734       {
735         if (touch_info[i].key != key)
736         {
737           if (touch_info[i].key != KSYM_UNDEFINED)
738           {
739             HandleKey(touch_info[i].key, KEY_RELEASED);
740
741             Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
742                   getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
743           }
744
745           if (key != KSYM_UNDEFINED)
746           {
747             HandleKey(key, KEY_PRESSED);
748
749             Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
750                   getKeyNameFromKey(key), "KEY_PRESSED", i);
751           }
752         }
753
754         touch_info[i].touched = TRUE;
755         touch_info[i].finger_id = event->fingerId;
756         touch_info[i].counter = Counter();
757         touch_info[i].key = key;
758       }
759       else
760       {
761         if (touch_info[i].key != KSYM_UNDEFINED)
762         {
763           HandleKey(touch_info[i].key, KEY_RELEASED);
764
765           Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
766                 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
767         }
768
769         touch_info[i].touched = FALSE;
770         touch_info[i].finger_id = 0;
771         touch_info[i].counter = 0;
772         touch_info[i].key = 0;
773       }
774     }
775
776     return;
777   }
778
779   // use touch direction control
780
781   if (event->type == EVENT_FINGERPRESS)
782   {
783     if (event_x > 1.0 / 3.0)
784     {
785       // motion area
786
787       motion_id = event->fingerId;
788
789       motion_x1 = event_x;
790       motion_y1 = event_y;
791
792       motion_key_x = KSYM_UNDEFINED;
793       motion_key_y = KSYM_UNDEFINED;
794
795       Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
796     }
797     else
798     {
799       // button area
800
801       button_id = event->fingerId;
802
803       button_x1 = event_x;
804       button_y1 = event_y;
805
806       button_key = setup.input[0].key.snap;
807
808       HandleKey(button_key, KEY_PRESSED);
809
810       Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
811     }
812   }
813   else if (event->type == EVENT_FINGERRELEASE)
814   {
815     if (event->fingerId == motion_id)
816     {
817       motion_id = -1;
818
819       if (motion_key_x != KSYM_UNDEFINED)
820         HandleKey(motion_key_x, KEY_RELEASED);
821       if (motion_key_y != KSYM_UNDEFINED)
822         HandleKey(motion_key_y, KEY_RELEASED);
823
824       motion_key_x = KSYM_UNDEFINED;
825       motion_key_y = KSYM_UNDEFINED;
826
827       Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
828     }
829     else if (event->fingerId == button_id)
830     {
831       button_id = -1;
832
833       if (button_key != KSYM_UNDEFINED)
834         HandleKey(button_key, KEY_RELEASED);
835
836       button_key = KSYM_UNDEFINED;
837
838       Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
839     }
840   }
841   else if (event->type == EVENT_FINGERMOTION)
842   {
843     if (event->fingerId == motion_id)
844     {
845       float distance_x = ABS(event_x - motion_x1);
846       float distance_y = ABS(event_y - motion_y1);
847       Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
848                               event_x > motion_x1 ? setup.input[0].key.right :
849                               KSYM_UNDEFINED);
850       Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
851                               event_y > motion_y1 ? setup.input[0].key.down :
852                               KSYM_UNDEFINED);
853
854       if (distance_x < move_trigger_distance / 2 ||
855           distance_x < distance_y)
856         new_motion_key_x = KSYM_UNDEFINED;
857
858       if (distance_y < move_trigger_distance / 2 ||
859           distance_y < distance_x)
860         new_motion_key_y = KSYM_UNDEFINED;
861
862       if (distance_x > move_trigger_distance ||
863           distance_y > move_trigger_distance)
864       {
865         if (new_motion_key_x != motion_key_x)
866         {
867           if (motion_key_x != KSYM_UNDEFINED)
868             HandleKey(motion_key_x, KEY_RELEASED);
869           if (new_motion_key_x != KSYM_UNDEFINED)
870             HandleKey(new_motion_key_x, KEY_PRESSED);
871         }
872
873         if (new_motion_key_y != motion_key_y)
874         {
875           if (motion_key_y != KSYM_UNDEFINED)
876             HandleKey(motion_key_y, KEY_RELEASED);
877           if (new_motion_key_y != KSYM_UNDEFINED)
878             HandleKey(new_motion_key_y, KEY_PRESSED);
879         }
880
881         motion_x1 = event_x;
882         motion_y1 = event_y;
883
884         motion_key_x = new_motion_key_x;
885         motion_key_y = new_motion_key_y;
886
887         Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
888       }
889     }
890     else if (event->fingerId == button_id)
891     {
892       float distance_x = ABS(event_x - button_x1);
893       float distance_y = ABS(event_y - button_y1);
894
895       if (distance_x < drop_trigger_distance / 2 &&
896           distance_y > drop_trigger_distance)
897       {
898         if (button_key == setup.input[0].key.snap)
899           HandleKey(button_key, KEY_RELEASED);
900
901         button_x1 = event_x;
902         button_y1 = event_y;
903
904         button_key = setup.input[0].key.drop;
905
906         HandleKey(button_key, KEY_PRESSED);
907
908         Error(ERR_DEBUG, "---------- DROP STARTED ----------");
909       }
910     }
911   }
912 }
913
914 static void HandleFollowFinger(int mx, int my, int button)
915 {
916   static int old_mx = 0, old_my = 0;
917   static Key motion_key_x = KSYM_UNDEFINED;
918   static Key motion_key_y = KSYM_UNDEFINED;
919   static boolean started_on_player = FALSE;
920   static boolean player_is_dropping = FALSE;
921   static int player_drop_count = 0;
922   static int last_player_x = -1;
923   static int last_player_y = -1;
924
925   if (!strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER))
926     return;
927
928   if (button == MB_PRESSED && IN_GFX_FIELD_PLAY(mx, my))
929   {
930     touch_info[0].touched = TRUE;
931     touch_info[0].key = 0;
932
933     old_mx = mx;
934     old_my = my;
935
936     if (!motion_status)
937     {
938       started_on_player = FALSE;
939       player_is_dropping = FALSE;
940       player_drop_count = 0;
941       last_player_x = -1;
942       last_player_y = -1;
943
944       motion_key_x = KSYM_UNDEFINED;
945       motion_key_y = KSYM_UNDEFINED;
946
947       Error(ERR_DEBUG, "---------- TOUCH ACTION STARTED ----------");
948     }
949   }
950   else if (button == MB_RELEASED && touch_info[0].touched)
951   {
952     touch_info[0].touched = FALSE;
953     touch_info[0].key = 0;
954
955     old_mx = 0;
956     old_my = 0;
957
958     if (motion_key_x != KSYM_UNDEFINED)
959       HandleKey(motion_key_x, KEY_RELEASED);
960     if (motion_key_y != KSYM_UNDEFINED)
961       HandleKey(motion_key_y, KEY_RELEASED);
962
963     if (started_on_player)
964     {
965       if (player_is_dropping)
966       {
967         Error(ERR_DEBUG, "---------- DROP STOPPED ----------");
968
969         HandleKey(setup.input[0].key.drop, KEY_RELEASED);
970       }
971       else
972       {
973         Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
974
975         HandleKey(setup.input[0].key.snap, KEY_RELEASED);
976       }
977     }
978
979     motion_key_x = KSYM_UNDEFINED;
980     motion_key_y = KSYM_UNDEFINED;
981
982     Error(ERR_DEBUG, "---------- TOUCH ACTION STOPPED ----------");
983   }
984
985   if (touch_info[0].touched)
986   {
987     int src_x = local_player->jx;
988     int src_y = local_player->jy;
989     int dst_x = getLevelFromScreenX(old_mx);
990     int dst_y = getLevelFromScreenY(old_my);
991     int dx = dst_x - src_x;
992     int dy = dst_y - src_y;
993     Key new_motion_key_x = (dx < 0 ? setup.input[0].key.left :
994                             dx > 0 ? setup.input[0].key.right :
995                             KSYM_UNDEFINED);
996     Key new_motion_key_y = (dy < 0 ? setup.input[0].key.up :
997                             dy > 0 ? setup.input[0].key.down :
998                             KSYM_UNDEFINED);
999
1000     if (dx != 0 && dy != 0 && ABS(dx) != ABS(dy) &&
1001         (last_player_x != local_player->jx ||
1002          last_player_y != local_player->jy))
1003     {
1004       // in case of asymmetric diagonal movement, use "preferred" direction
1005
1006       int last_move_dir = (ABS(dx) > ABS(dy) ? MV_VERTICAL : MV_HORIZONTAL);
1007
1008       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1009         level.native_em_level->ply[0]->last_move_dir = last_move_dir;
1010       else
1011         local_player->last_move_dir = last_move_dir;
1012
1013       // (required to prevent accidentally forcing direction for next movement)
1014       last_player_x = local_player->jx;
1015       last_player_y = local_player->jy;
1016     }
1017
1018     if (button == MB_PRESSED && !motion_status && dx == 0 && dy == 0)
1019     {
1020       started_on_player = TRUE;
1021       player_drop_count = getPlayerInventorySize(0);
1022       player_is_dropping = (player_drop_count > 0);
1023
1024       if (player_is_dropping)
1025       {
1026         Error(ERR_DEBUG, "---------- DROP STARTED ----------");
1027
1028         HandleKey(setup.input[0].key.drop, KEY_PRESSED);
1029       }
1030       else
1031       {
1032         Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
1033
1034         HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1035       }
1036     }
1037     else if (dx != 0 || dy != 0)
1038     {
1039       if (player_is_dropping &&
1040           player_drop_count == getPlayerInventorySize(0))
1041       {
1042         Error(ERR_DEBUG, "---------- DROP -> SNAP ----------");
1043
1044         HandleKey(setup.input[0].key.drop, KEY_RELEASED);
1045         HandleKey(setup.input[0].key.snap, KEY_PRESSED);
1046
1047         player_is_dropping = FALSE;
1048       }
1049     }
1050
1051     if (new_motion_key_x != motion_key_x)
1052     {
1053       Error(ERR_DEBUG, "---------- %s %s ----------",
1054             started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1055             dx < 0 ? "LEFT" : dx > 0 ? "RIGHT" : "PAUSED");
1056
1057       if (motion_key_x != KSYM_UNDEFINED)
1058         HandleKey(motion_key_x, KEY_RELEASED);
1059       if (new_motion_key_x != KSYM_UNDEFINED)
1060         HandleKey(new_motion_key_x, KEY_PRESSED);
1061     }
1062
1063     if (new_motion_key_y != motion_key_y)
1064     {
1065       Error(ERR_DEBUG, "---------- %s %s ----------",
1066             started_on_player && !player_is_dropping ? "SNAPPING" : "MOVING",
1067             dy < 0 ? "UP" : dy > 0 ? "DOWN" : "PAUSED");
1068
1069       if (motion_key_y != KSYM_UNDEFINED)
1070         HandleKey(motion_key_y, KEY_RELEASED);
1071       if (new_motion_key_y != KSYM_UNDEFINED)
1072         HandleKey(new_motion_key_y, KEY_PRESSED);
1073     }
1074
1075     motion_key_x = new_motion_key_x;
1076     motion_key_y = new_motion_key_y;
1077   }
1078 }
1079
1080 static boolean checkTextInputKeyModState()
1081 {
1082   // when playing, only handle raw key events and ignore text input
1083   if (game_status == GAME_MODE_PLAYING)
1084     return FALSE;
1085
1086   return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
1087 }
1088
1089 void HandleTextEvent(TextEvent *event)
1090 {
1091   char *text = event->text;
1092   Key key = getKeyFromKeyName(text);
1093
1094 #if DEBUG_EVENTS_TEXT
1095   Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
1096         text,
1097         strlen(text),
1098         text[0], (int)(text[0]),
1099         key,
1100         getKeyNameFromKey(key),
1101         GetKeyModState());
1102 #endif
1103
1104 #if !defined(HAS_SCREEN_KEYBOARD)
1105   // non-mobile devices: only handle key input with modifier keys pressed here
1106   // (every other key input is handled directly as physical key input event)
1107   if (!checkTextInputKeyModState())
1108     return;
1109 #endif
1110
1111   // process text input as "classic" (with uppercase etc.) key input event
1112   HandleKey(key, KEY_PRESSED);
1113   HandleKey(key, KEY_RELEASED);
1114 }
1115
1116 void HandlePauseResumeEvent(PauseResumeEvent *event)
1117 {
1118   if (event->type == SDL_APP_WILLENTERBACKGROUND)
1119   {
1120     Mix_PauseMusic();
1121   }
1122   else if (event->type == SDL_APP_DIDENTERFOREGROUND)
1123   {
1124     Mix_ResumeMusic();
1125   }
1126 }
1127
1128 #endif
1129
1130 void HandleKeyEvent(KeyEvent *event)
1131 {
1132   int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
1133   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
1134   Key key = GetEventKey(event, with_modifiers);
1135   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
1136
1137 #if DEBUG_EVENTS_KEY
1138   Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
1139         event->type == EVENT_KEYPRESS ? "pressed" : "released",
1140         event->keysym.scancode,
1141         event->keysym.sym,
1142         keymod,
1143         GetKeyModState(),
1144         key,
1145         getKeyNameFromKey(key));
1146 #endif
1147
1148 #if defined(PLATFORM_ANDROID)
1149   // always map the "back" button to the "escape" key on Android devices
1150   if (key == KSYM_Back)
1151     key = KSYM_Escape;
1152 #endif
1153
1154   HandleKeyModState(keymod, key_status);
1155
1156 #if defined(TARGET_SDL2)
1157   // only handle raw key input without text modifier keys pressed
1158   if (!checkTextInputKeyModState())
1159     HandleKey(key, key_status);
1160 #else
1161   HandleKey(key, key_status);
1162 #endif
1163 }
1164
1165 void HandleFocusEvent(FocusChangeEvent *event)
1166 {
1167   static int old_joystick_status = -1;
1168
1169   if (event->type == EVENT_FOCUSOUT)
1170   {
1171     KeyboardAutoRepeatOn();
1172     old_joystick_status = joystick.status;
1173     joystick.status = JOYSTICK_NOT_AVAILABLE;
1174
1175     ClearPlayerAction();
1176   }
1177   else if (event->type == EVENT_FOCUSIN)
1178   {
1179     /* When there are two Rocks'n'Diamonds windows which overlap and
1180        the player moves the pointer from one game window to the other,
1181        a 'FocusOut' event is generated for the window the pointer is
1182        leaving and a 'FocusIn' event is generated for the window the
1183        pointer is entering. In some cases, it can happen that the
1184        'FocusIn' event is handled by the one game process before the
1185        'FocusOut' event by the other game process. In this case the
1186        X11 environment would end up with activated keyboard auto repeat,
1187        because unfortunately this is a global setting and not (which
1188        would be far better) set for each X11 window individually.
1189        The effect would be keyboard auto repeat while playing the game
1190        (game_status == GAME_MODE_PLAYING), which is not desired.
1191        To avoid this special case, we just wait 1/10 second before
1192        processing the 'FocusIn' event.
1193     */
1194
1195     if (game_status == GAME_MODE_PLAYING)
1196     {
1197       Delay(100);
1198       KeyboardAutoRepeatOffUnlessAutoplay();
1199     }
1200
1201     if (old_joystick_status != -1)
1202       joystick.status = old_joystick_status;
1203   }
1204 }
1205
1206 void HandleClientMessageEvent(ClientMessageEvent *event)
1207 {
1208   if (CheckCloseWindowEvent(event))
1209     CloseAllAndExit(0);
1210 }
1211
1212 void HandleWindowManagerEvent(Event *event)
1213 {
1214 #if defined(TARGET_SDL)
1215   SDLHandleWindowManagerEvent(event);
1216 #endif
1217 }
1218
1219 void HandleButton(int mx, int my, int button, int button_nr)
1220 {
1221   static int old_mx = 0, old_my = 0;
1222   boolean button_hold = FALSE;
1223
1224   if (button_nr < 0)
1225   {
1226     mx = old_mx;
1227     my = old_my;
1228     button_nr = -button_nr;
1229     button_hold = TRUE;
1230   }
1231   else
1232   {
1233     old_mx = mx;
1234     old_my = my;
1235   }
1236
1237 #if defined(PLATFORM_ANDROID)
1238   // when playing, only handle gadgets when using "follow finger" controls
1239   boolean handle_gadgets =
1240     (game_status != GAME_MODE_PLAYING ||
1241      strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER));
1242
1243   if (handle_gadgets &&
1244       HandleGadgets(mx, my, button))
1245   {
1246     /* do not handle this button event anymore */
1247     mx = my = -32;      /* force mouse event to be outside screen tiles */
1248   }
1249 #else
1250   if (HandleGadgets(mx, my, button))
1251   {
1252     /* do not handle this button event anymore */
1253     mx = my = -32;      /* force mouse event to be outside screen tiles */
1254   }
1255 #endif
1256
1257   if (HandleGlobalAnimClicks(mx, my, button))
1258   {
1259     /* do not handle this button event anymore */
1260     mx = my = -32;      /* force mouse event to be outside screen tiles */
1261   }
1262
1263   if (button_hold && game_status == GAME_MODE_PLAYING && tape.pausing)
1264     return;
1265
1266   /* do not use scroll wheel button events for anything other than gadgets */
1267   if (IS_WHEEL_BUTTON(button_nr))
1268     return;
1269
1270   switch (game_status)
1271   {
1272     case GAME_MODE_TITLE:
1273       HandleTitleScreen(mx, my, 0, 0, button);
1274       break;
1275
1276     case GAME_MODE_MAIN:
1277       HandleMainMenu(mx, my, 0, 0, button);
1278       break;
1279
1280     case GAME_MODE_PSEUDO_TYPENAME:
1281       HandleTypeName(0, KSYM_Return);
1282       break;
1283
1284     case GAME_MODE_LEVELS:
1285       HandleChooseLevelSet(mx, my, 0, 0, button);
1286       break;
1287
1288     case GAME_MODE_LEVELNR:
1289       HandleChooseLevelNr(mx, my, 0, 0, button);
1290       break;
1291
1292     case GAME_MODE_SCORES:
1293       HandleHallOfFame(0, 0, 0, 0, button);
1294       break;
1295
1296     case GAME_MODE_EDITOR:
1297       HandleLevelEditorIdle();
1298       break;
1299
1300     case GAME_MODE_INFO:
1301       HandleInfoScreen(mx, my, 0, 0, button);
1302       break;
1303
1304     case GAME_MODE_SETUP:
1305       HandleSetupScreen(mx, my, 0, 0, button);
1306       break;
1307
1308 #if defined(TARGET_SDL2)
1309     case GAME_MODE_PLAYING:
1310       HandleFollowFinger(mx, my, button);
1311 #endif
1312
1313 #ifdef DEBUG
1314       if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my) &&
1315           GetKeyModState() & KMOD_Control)
1316         DumpTileFromScreen(mx, my);
1317 #endif
1318
1319       break;
1320
1321     default:
1322       break;
1323   }
1324 }
1325
1326 static boolean is_string_suffix(char *string, char *suffix)
1327 {
1328   int string_len = strlen(string);
1329   int suffix_len = strlen(suffix);
1330
1331   if (suffix_len > string_len)
1332     return FALSE;
1333
1334   return (strEqual(&string[string_len - suffix_len], suffix));
1335 }
1336
1337 #define MAX_CHEAT_INPUT_LEN     32
1338
1339 static void HandleKeysSpecial(Key key)
1340 {
1341   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1342   char letter = getCharFromKey(key);
1343   int cheat_input_len = strlen(cheat_input);
1344   int i;
1345
1346   if (letter == 0)
1347     return;
1348
1349   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1350   {
1351     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1352       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1353
1354     cheat_input_len = MAX_CHEAT_INPUT_LEN;
1355   }
1356
1357   cheat_input[cheat_input_len++] = letter;
1358   cheat_input[cheat_input_len] = '\0';
1359
1360 #if DEBUG_EVENTS_KEY
1361   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1362 #endif
1363
1364   if (game_status == GAME_MODE_MAIN)
1365   {
1366     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1367         is_string_suffix(cheat_input, ":ist"))
1368     {
1369       InsertSolutionTape();
1370     }
1371     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1372              is_string_suffix(cheat_input, ":rg"))
1373     {
1374       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1375       DrawMainMenu();
1376     }
1377     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1378              is_string_suffix(cheat_input, ":rs"))
1379     {
1380       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1381       DrawMainMenu();
1382     }
1383     else if (is_string_suffix(cheat_input, ":reload-music") ||
1384              is_string_suffix(cheat_input, ":rm"))
1385     {
1386       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1387       DrawMainMenu();
1388     }
1389     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1390              is_string_suffix(cheat_input, ":ra"))
1391     {
1392       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1393                           1 << ARTWORK_TYPE_SOUNDS |
1394                           1 << ARTWORK_TYPE_MUSIC);
1395       DrawMainMenu();
1396     }
1397     else if (is_string_suffix(cheat_input, ":dump-level") ||
1398              is_string_suffix(cheat_input, ":dl"))
1399     {
1400       DumpLevel(&level);
1401     }
1402     else if (is_string_suffix(cheat_input, ":dump-tape") ||
1403              is_string_suffix(cheat_input, ":dt"))
1404     {
1405       DumpTape(&tape);
1406     }
1407     else if (is_string_suffix(cheat_input, ":fix-tape") ||
1408              is_string_suffix(cheat_input, ":ft"))
1409     {
1410       /* fix single-player tapes that contain player input for more than one
1411          player (due to a bug in 3.3.1.2 and earlier versions), which results
1412          in playing levels with more than one player in multi-player mode,
1413          even though the tape was originally recorded in single-player mode */
1414
1415       /* remove player input actions for all players but the first one */
1416       for (i = 1; i < MAX_PLAYERS; i++)
1417         tape.player_participates[i] = FALSE;
1418
1419       tape.changed = TRUE;
1420     }
1421     else if (is_string_suffix(cheat_input, ":save-native-level") ||
1422              is_string_suffix(cheat_input, ":snl"))
1423     {
1424       SaveNativeLevel(&level);
1425     }
1426   }
1427   else if (game_status == GAME_MODE_PLAYING)
1428   {
1429 #ifdef DEBUG
1430     if (is_string_suffix(cheat_input, ".q"))
1431       DEBUG_SetMaximumDynamite();
1432 #endif
1433   }
1434   else if (game_status == GAME_MODE_EDITOR)
1435   {
1436     if (is_string_suffix(cheat_input, ":dump-brush") ||
1437         is_string_suffix(cheat_input, ":DB"))
1438     {
1439       DumpBrush();
1440     }
1441     else if (is_string_suffix(cheat_input, ":DDB"))
1442     {
1443       DumpBrush_Small();
1444     }
1445   }
1446 }
1447
1448 void HandleKeysDebug(Key key)
1449 {
1450 #ifdef DEBUG
1451   int i;
1452
1453   if (game_status == GAME_MODE_PLAYING || !setup.debug.frame_delay_game_only)
1454   {
1455     boolean mod_key_pressed = (GetKeyModState() != KMOD_None);
1456
1457     for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1458     {
1459       if (key == setup.debug.frame_delay_key[i] &&
1460           (mod_key_pressed == setup.debug.frame_delay_use_mod_key))
1461       {
1462         GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1463                           setup.debug.frame_delay[i] : setup.game_frame_delay);
1464
1465         if (!setup.debug.frame_delay_game_only)
1466           MenuFrameDelay = GameFrameDelay;
1467
1468         SetVideoFrameDelay(GameFrameDelay);
1469
1470         if (GameFrameDelay > ONE_SECOND_DELAY)
1471           Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1472         else if (GameFrameDelay != 0)
1473           Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1474                 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1475                 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1476         else
1477           Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1478
1479         break;
1480       }
1481     }
1482   }
1483
1484   if (game_status == GAME_MODE_PLAYING)
1485   {
1486     if (key == KSYM_d)
1487     {
1488       options.debug = !options.debug;
1489
1490       Error(ERR_DEBUG, "debug mode %s",
1491             (options.debug ? "enabled" : "disabled"));
1492     }
1493     else if (key == KSYM_v)
1494     {
1495       Error(ERR_DEBUG, "currently using game engine version %d",
1496             game.engine_version);
1497     }
1498   }
1499 #endif
1500 }
1501
1502 void HandleKey(Key key, int key_status)
1503 {
1504   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1505   static boolean ignore_repeated_key = FALSE;
1506   static struct SetupKeyboardInfo ski;
1507   static struct SetupShortcutInfo ssi;
1508   static struct
1509   {
1510     Key *key_custom;
1511     Key *key_snap;
1512     Key key_default;
1513     byte action;
1514   } key_info[] =
1515   {
1516     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
1517     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
1518     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
1519     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
1520     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
1521     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
1522   };
1523   int joy = 0;
1524   int i;
1525
1526   if (game_status == GAME_MODE_PLAYING)
1527   {
1528     /* only needed for single-step tape recording mode */
1529     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1530     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1531     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1532     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1533     int pnr;
1534
1535     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1536     {
1537       byte key_action = 0;
1538
1539       if (setup.input[pnr].use_joystick)
1540         continue;
1541
1542       ski = setup.input[pnr].key;
1543
1544       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1545         if (key == *key_info[i].key_custom)
1546           key_action |= key_info[i].action;
1547
1548       /* use combined snap+direction keys for the first player only */
1549       if (pnr == 0)
1550       {
1551         ssi = setup.shortcut;
1552
1553         for (i = 0; i < NUM_DIRECTIONS; i++)
1554           if (key == *key_info[i].key_snap)
1555             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1556       }
1557
1558       /* clear delayed snap and drop actions in single step mode (see below) */
1559       if (tape.single_step)
1560       {
1561         if (clear_snap_button[pnr])
1562         {
1563           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1564           clear_snap_button[pnr] = FALSE;
1565         }
1566
1567         if (clear_drop_button[pnr])
1568         {
1569           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1570           clear_drop_button[pnr] = FALSE;
1571         }
1572       }
1573
1574       if (key_status == KEY_PRESSED)
1575         stored_player[pnr].action |= key_action;
1576       else
1577         stored_player[pnr].action &= ~key_action;
1578
1579       if (tape.single_step && tape.recording && tape.pausing)
1580       {
1581         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1582         {
1583           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1584
1585           /* if snap key already pressed, don't snap when releasing (below) */
1586           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1587             element_snapped[pnr] = TRUE;
1588
1589           /* if drop key already pressed, don't drop when releasing (below) */
1590           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1591             element_dropped[pnr] = TRUE;
1592         }
1593         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1594         {
1595           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1596               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1597           {
1598
1599             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1600                 getRedDiskReleaseFlag_SP() == 0)
1601               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1602
1603             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1604           }
1605         }
1606         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1607         {
1608           if (key_action & KEY_BUTTON_SNAP)
1609           {
1610             /* if snap key was released without moving (see above), snap now */
1611             if (!element_snapped[pnr])
1612             {
1613               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1614
1615               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1616
1617               /* clear delayed snap button on next event */
1618               clear_snap_button[pnr] = TRUE;
1619             }
1620
1621             element_snapped[pnr] = FALSE;
1622           }
1623
1624           if (key_action & KEY_BUTTON_DROP &&
1625               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1626           {
1627             /* if drop key was released without moving (see above), drop now */
1628             if (!element_dropped[pnr])
1629             {
1630               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1631
1632               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1633                   getRedDiskReleaseFlag_SP() != 0)
1634                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1635
1636               /* clear delayed drop button on next event */
1637               clear_drop_button[pnr] = TRUE;
1638             }
1639
1640             element_dropped[pnr] = FALSE;
1641           }
1642         }
1643       }
1644       else if (tape.recording && tape.pausing)
1645       {
1646         /* prevent key release events from un-pausing a paused game */
1647         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1648           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1649       }
1650     }
1651   }
1652   else
1653   {
1654     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1655       if (key == key_info[i].key_default)
1656         joy |= key_info[i].action;
1657   }
1658
1659   if (joy)
1660   {
1661     if (key_status == KEY_PRESSED)
1662       key_joystick_mapping |= joy;
1663     else
1664       key_joystick_mapping &= ~joy;
1665
1666     HandleJoystick();
1667   }
1668
1669   if (game_status != GAME_MODE_PLAYING)
1670     key_joystick_mapping = 0;
1671
1672   if (key_status == KEY_RELEASED)
1673   {
1674     // reset flag to ignore repeated "key pressed" events after key release
1675     ignore_repeated_key = FALSE;
1676
1677     return;
1678   }
1679
1680   if ((key == KSYM_F11 ||
1681        ((key == KSYM_Return ||
1682          key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1683       video.fullscreen_available &&
1684       !ignore_repeated_key)
1685   {
1686     setup.fullscreen = !setup.fullscreen;
1687
1688     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1689
1690     if (game_status == GAME_MODE_SETUP)
1691       RedrawSetupScreenAfterFullscreenToggle();
1692
1693     // set flag to ignore repeated "key pressed" events
1694     ignore_repeated_key = TRUE;
1695
1696     return;
1697   }
1698
1699   if ((key == KSYM_0     || key == KSYM_KP_0 ||
1700        key == KSYM_minus || key == KSYM_KP_Subtract ||
1701        key == KSYM_plus  || key == KSYM_KP_Add ||
1702        key == KSYM_equal) &&    // ("Shift-=" is "+" on US keyboards)
1703       (GetKeyModState() & (KMOD_Control | KMOD_Meta)) &&
1704       video.window_scaling_available &&
1705       !video.fullscreen_enabled)
1706   {
1707     if (key == KSYM_0 || key == KSYM_KP_0)
1708       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1709     else if (key == KSYM_minus || key == KSYM_KP_Subtract)
1710       setup.window_scaling_percent -= STEP_WINDOW_SCALING_PERCENT;
1711     else
1712       setup.window_scaling_percent += STEP_WINDOW_SCALING_PERCENT;
1713
1714     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1715       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1716     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1717       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1718
1719     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1720
1721     if (game_status == GAME_MODE_SETUP)
1722       RedrawSetupScreenAfterFullscreenToggle();
1723
1724     return;
1725   }
1726
1727   if (HandleGlobalAnimClicks(-1, -1, (key == KSYM_space ||
1728                                       key == KSYM_Return ||
1729                                       key == KSYM_Escape)))
1730   {
1731     /* do not handle this key event anymore */
1732     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1733       return;
1734   }
1735
1736   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1737       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1738   {
1739     GameEnd();
1740
1741     return;
1742   }
1743
1744   if (game_status == GAME_MODE_MAIN &&
1745       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1746   {
1747     StartGameActions(options.network, setup.autorecord, level.random_seed);
1748
1749     return;
1750   }
1751
1752   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1753   {
1754     if (key == setup.shortcut.save_game)
1755       TapeQuickSave();
1756     else if (key == setup.shortcut.load_game)
1757       TapeQuickLoad();
1758     else if (key == setup.shortcut.toggle_pause)
1759       TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1760
1761     HandleTapeButtonKeys(key);
1762     HandleSoundButtonKeys(key);
1763   }
1764
1765   if (game_status == GAME_MODE_PLAYING && !network_playing)
1766   {
1767     int centered_player_nr_next = -999;
1768
1769     if (key == setup.shortcut.focus_player_all)
1770       centered_player_nr_next = -1;
1771     else
1772       for (i = 0; i < MAX_PLAYERS; i++)
1773         if (key == setup.shortcut.focus_player[i])
1774           centered_player_nr_next = i;
1775
1776     if (centered_player_nr_next != -999)
1777     {
1778       game.centered_player_nr_next = centered_player_nr_next;
1779       game.set_centered_player = TRUE;
1780
1781       if (tape.recording)
1782       {
1783         tape.centered_player_nr_next = game.centered_player_nr_next;
1784         tape.set_centered_player = TRUE;
1785       }
1786     }
1787   }
1788
1789   HandleKeysSpecial(key);
1790
1791   if (HandleGadgetsKeyInput(key))
1792   {
1793     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1794       key = KSYM_UNDEFINED;
1795   }
1796
1797   switch (game_status)
1798   {
1799     case GAME_MODE_PSEUDO_TYPENAME:
1800       HandleTypeName(0, key);
1801       break;
1802
1803     case GAME_MODE_TITLE:
1804     case GAME_MODE_MAIN:
1805     case GAME_MODE_LEVELS:
1806     case GAME_MODE_LEVELNR:
1807     case GAME_MODE_SETUP:
1808     case GAME_MODE_INFO:
1809     case GAME_MODE_SCORES:
1810       switch (key)
1811       {
1812         case KSYM_space:
1813         case KSYM_Return:
1814           if (game_status == GAME_MODE_TITLE)
1815             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1816           else if (game_status == GAME_MODE_MAIN)
1817             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1818           else if (game_status == GAME_MODE_LEVELS)
1819             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1820           else if (game_status == GAME_MODE_LEVELNR)
1821             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1822           else if (game_status == GAME_MODE_SETUP)
1823             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1824           else if (game_status == GAME_MODE_INFO)
1825             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1826           else if (game_status == GAME_MODE_SCORES)
1827             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1828           break;
1829
1830         case KSYM_Escape:
1831           if (game_status != GAME_MODE_MAIN)
1832             FadeSkipNextFadeIn();
1833
1834           if (game_status == GAME_MODE_TITLE)
1835             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1836           else if (game_status == GAME_MODE_LEVELS)
1837             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1838           else if (game_status == GAME_MODE_LEVELNR)
1839             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1840           else if (game_status == GAME_MODE_SETUP)
1841             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1842           else if (game_status == GAME_MODE_INFO)
1843             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1844           else if (game_status == GAME_MODE_SCORES)
1845             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1846           break;
1847
1848         case KSYM_Page_Up:
1849           if (game_status == GAME_MODE_LEVELS)
1850             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1851           else if (game_status == GAME_MODE_LEVELNR)
1852             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1853           else if (game_status == GAME_MODE_SETUP)
1854             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1855           else if (game_status == GAME_MODE_INFO)
1856             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1857           else if (game_status == GAME_MODE_SCORES)
1858             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1859           break;
1860
1861         case KSYM_Page_Down:
1862           if (game_status == GAME_MODE_LEVELS)
1863             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1864           else if (game_status == GAME_MODE_LEVELNR)
1865             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1866           else if (game_status == GAME_MODE_SETUP)
1867             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1868           else if (game_status == GAME_MODE_INFO)
1869             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1870           else if (game_status == GAME_MODE_SCORES)
1871             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1872           break;
1873
1874         default:
1875           break;
1876       }
1877       break;
1878
1879     case GAME_MODE_EDITOR:
1880       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1881         HandleLevelEditorKeyInput(key);
1882       break;
1883
1884     case GAME_MODE_PLAYING:
1885     {
1886       switch (key)
1887       {
1888         case KSYM_Escape:
1889           RequestQuitGame(setup.ask_on_escape);
1890           break;
1891
1892         default:
1893           break;
1894       }
1895       break;
1896     }
1897
1898     default:
1899       if (key == KSYM_Escape)
1900       {
1901         SetGameStatus(GAME_MODE_MAIN);
1902
1903         DrawMainMenu();
1904
1905         return;
1906       }
1907   }
1908
1909   HandleKeysDebug(key);
1910 }
1911
1912 void HandleNoEvent()
1913 {
1914   // if (button_status && game_status != GAME_MODE_PLAYING)
1915   if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1916   {
1917     HandleButton(0, 0, button_status, -button_status);
1918   }
1919   else
1920   {
1921     HandleJoystick();
1922   }
1923
1924 #if defined(NETWORK_AVALIABLE)
1925   if (options.network)
1926     HandleNetworking();
1927 #endif
1928
1929   switch (game_status)
1930   {
1931     case GAME_MODE_MAIN:
1932       DrawPreviewLevelAnimation();
1933       break;
1934
1935     case GAME_MODE_EDITOR:
1936       HandleLevelEditorIdle();
1937       break;
1938
1939 #if defined(TARGET_SDL2)
1940     case GAME_MODE_PLAYING:
1941       HandleFollowFinger(-1, -1, -1);
1942       break;
1943 #endif
1944
1945     default:
1946       break;
1947   }
1948 }
1949
1950 static int HandleJoystickForAllPlayers()
1951 {
1952   int i;
1953   int result = 0;
1954   boolean no_joysticks_configured = TRUE;
1955   boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING);
1956   static byte joy_action_last[MAX_PLAYERS];
1957
1958   for (i = 0; i < MAX_PLAYERS; i++)
1959     if (setup.input[i].use_joystick)
1960       no_joysticks_configured = FALSE;
1961
1962   /* if no joysticks configured, map connected joysticks to players */
1963   if (no_joysticks_configured)
1964     use_as_joystick_nr = TRUE;
1965
1966   for (i = 0; i < MAX_PLAYERS; i++)
1967   {
1968     byte joy_action = 0;
1969
1970     joy_action = JoystickExt(i, use_as_joystick_nr);
1971     result |= joy_action;
1972
1973     if ((setup.input[i].use_joystick || no_joysticks_configured) &&
1974         joy_action != joy_action_last[i])
1975       stored_player[i].action = joy_action;
1976
1977     joy_action_last[i] = joy_action;
1978   }
1979
1980   return result;
1981 }
1982
1983 void HandleJoystick()
1984 {
1985   int joystick  = HandleJoystickForAllPlayers();
1986   int keyboard  = key_joystick_mapping;
1987   int joy       = (joystick | keyboard);
1988   int left      = joy & JOY_LEFT;
1989   int right     = joy & JOY_RIGHT;
1990   int up        = joy & JOY_UP;
1991   int down      = joy & JOY_DOWN;
1992   int button    = joy & JOY_BUTTON;
1993   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1994   int dx        = (left ? -1    : right ? 1     : 0);
1995   int dy        = (up   ? -1    : down  ? 1     : 0);
1996
1997   if (HandleGlobalAnimClicks(-1, -1, newbutton))
1998   {
1999     /* do not handle this button event anymore */
2000     return;
2001   }
2002
2003   switch (game_status)
2004   {
2005     case GAME_MODE_TITLE:
2006     case GAME_MODE_MAIN:
2007     case GAME_MODE_LEVELS:
2008     case GAME_MODE_LEVELNR:
2009     case GAME_MODE_SETUP:
2010     case GAME_MODE_INFO:
2011     {
2012       static unsigned int joystickmove_delay = 0;
2013
2014       if (joystick && !button &&
2015           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
2016         newbutton = dx = dy = 0;
2017
2018       if (game_status == GAME_MODE_TITLE)
2019         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2020       else if (game_status == GAME_MODE_MAIN)
2021         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2022       else if (game_status == GAME_MODE_LEVELS)
2023         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
2024       else if (game_status == GAME_MODE_LEVELNR)
2025         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
2026       else if (game_status == GAME_MODE_SETUP)
2027         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2028       else if (game_status == GAME_MODE_INFO)
2029         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
2030       break;
2031     }
2032
2033     case GAME_MODE_SCORES:
2034       HandleHallOfFame(0, 0, dx, dy, !newbutton);
2035       break;
2036
2037     case GAME_MODE_PLAYING:
2038       if (tape.playing || keyboard)
2039         newbutton = ((joy & JOY_BUTTON) != 0);
2040
2041       if (newbutton && AllPlayersGone)
2042       {
2043         GameEnd();
2044
2045         return;
2046       }
2047
2048       if (tape.recording && tape.pausing)
2049       {
2050         if (joystick & JOY_ACTION)
2051           TapeTogglePause(TAPE_TOGGLE_MANUAL);
2052       }
2053
2054       break;
2055
2056     default:
2057       break;
2058   }
2059 }
2060
2061 void HandleSpecialGameControllerButtons(Event *event)
2062 {
2063 #if defined(TARGET_SDL2)
2064   switch (event->type)
2065   {
2066     case SDL_CONTROLLERBUTTONDOWN:
2067       if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2068         HandleKey(KSYM_space, KEY_PRESSED);
2069       else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2070         HandleKey(KSYM_Escape, KEY_PRESSED);
2071
2072       break;
2073
2074     case SDL_CONTROLLERBUTTONUP:
2075       if (event->cbutton.button == SDL_CONTROLLER_BUTTON_START)
2076         HandleKey(KSYM_space, KEY_RELEASED);
2077       else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
2078         HandleKey(KSYM_Escape, KEY_RELEASED);
2079
2080       break;
2081   }
2082 #endif
2083 }