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