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