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