removed some remaining unused X11 stuff
[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 "network.h"
23
24
25 #define DEBUG_EVENTS            0
26
27 #define DEBUG_EVENTS_BUTTON     (DEBUG_EVENTS   * 0)
28 #define DEBUG_EVENTS_MOTION     (DEBUG_EVENTS   * 0)
29 #define DEBUG_EVENTS_WINDOW     (DEBUG_EVENTS   * 0)
30 #define DEBUG_EVENTS_FINGER     (DEBUG_EVENTS   * 0)
31 #define DEBUG_EVENTS_TEXT       (DEBUG_EVENTS   * 1)
32 #define DEBUG_EVENTS_KEY        (DEBUG_EVENTS   * 1)
33
34
35 static boolean cursor_inside_playfield = FALSE;
36 static boolean playfield_cursor_set = FALSE;
37 static unsigned int playfield_cursor_delay = 0;
38
39
40 /* event filter especially needed for SDL event filtering due to
41    delay problems with lots of mouse motion events when mouse button
42    not pressed (X11 can handle this with 'PointerMotionHintMask') */
43
44 /* event filter addition for SDL2: as SDL2 does not have a function to enable
45    or disable keyboard auto-repeat, filter repeated keyboard events instead */
46
47 static int FilterEventsExt(const Event *event)
48 {
49   MotionEvent *motion;
50
51 #if defined(TARGET_SDL2)
52   /* skip repeated key press events if keyboard auto-repeat is disabled */
53   if (event->type == EVENT_KEYPRESS &&
54       event->key.repeat &&
55       !keyrepeat_status)
56     return 0;
57 #endif
58
59   /* non-motion events are directly passed to event handler functions */
60   if (event->type != EVENT_MOTIONNOTIFY)
61     return 1;
62
63   motion = (MotionEvent *)event;
64   cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
65                              motion->y >= SY && motion->y < SY + SYSIZE);
66
67   if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
68   {
69     SetMouseCursor(CURSOR_DEFAULT);
70     playfield_cursor_set = FALSE;
71     DelayReached(&playfield_cursor_delay, 0);
72   }
73
74   /* skip mouse motion events without pressed button outside level editor */
75   if (button_status == MB_RELEASED &&
76       game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
77     return 0;
78
79   return 1;
80 }
81
82 #if defined(TARGET_SDL2)
83 int FilterEvents(void *userdata, Event *event)
84 {
85   return FilterEventsExt(event);
86 }
87 #else
88 int FilterEvents(const Event *event)
89 {
90   return FilterEventsExt(event);
91 }
92 #endif
93
94 /* to prevent delay problems, skip mouse motion events if the very next
95    event is also a mouse motion event (and therefore effectively only
96    handling the last of a row of mouse motion events in the event queue) */
97
98 boolean SkipPressedMouseMotionEvent(const Event *event)
99 {
100   /* nothing to do if the current event is not a mouse motion event */
101   if (event->type != EVENT_MOTIONNOTIFY)
102     return FALSE;
103
104   /* only skip motion events with pressed button outside level editor */
105   if (button_status == MB_RELEASED ||
106       game_status == GAME_MODE_EDITOR || game_status == GAME_MODE_PLAYING)
107     return FALSE;
108
109   if (PendingEvent())
110   {
111     Event next_event;
112
113     PeekEvent(&next_event);
114
115     /* if next event is also a mouse motion event, skip the current one */
116     if (next_event.type == EVENT_MOTIONNOTIFY)
117       return TRUE;
118   }
119
120   return FALSE;
121 }
122
123 /* this is only really needed for non-SDL targets to filter unwanted events;
124    when using SDL with properly installed event filter, this function can be
125    replaced with a simple "NextEvent()" call, but it doesn't hurt either */
126
127 static boolean NextValidEvent(Event *event)
128 {
129   while (PendingEvent())
130   {
131     boolean handle_this_event = FALSE;
132
133     NextEvent(event);
134
135     if (FilterEventsExt(event))
136       handle_this_event = TRUE;
137
138     if (SkipPressedMouseMotionEvent(event))
139       handle_this_event = FALSE;
140
141     if (handle_this_event)
142       return TRUE;
143   }
144
145   return FALSE;
146 }
147
148 void EventLoop(void)
149 {
150   while (1)
151   {
152     if (PendingEvent())         /* got event */
153     {
154       Event event;
155
156       while (NextValidEvent(&event))
157       {
158         switch (event.type)
159         {
160           case EVENT_BUTTONPRESS:
161           case EVENT_BUTTONRELEASE:
162             HandleButtonEvent((ButtonEvent *) &event);
163             break;
164   
165           case EVENT_MOTIONNOTIFY:
166             HandleMotionEvent((MotionEvent *) &event);
167             break;
168
169 #if defined(TARGET_SDL2)
170           case SDL_WINDOWEVENT:
171             HandleWindowEvent((WindowEvent *) &event);
172             break;
173
174           case EVENT_FINGERPRESS:
175           case EVENT_FINGERRELEASE:
176           case EVENT_FINGERMOTION:
177             HandleFingerEvent((FingerEvent *) &event);
178             break;
179
180           case EVENT_TEXTINPUT:
181             HandleTextEvent((TextEvent *) &event);
182             break;
183
184           case SDL_APP_WILLENTERBACKGROUND:
185           case SDL_APP_DIDENTERBACKGROUND:
186           case SDL_APP_WILLENTERFOREGROUND:
187           case SDL_APP_DIDENTERFOREGROUND:
188             HandlePauseResumeEvent((PauseResumeEvent *) &event);
189             break;
190 #endif
191
192           case EVENT_KEYPRESS:
193           case EVENT_KEYRELEASE:
194             HandleKeyEvent((KeyEvent *) &event);
195             break;
196
197           default:
198             HandleOtherEvents(&event);
199             break;
200         }
201       }
202     }
203     else
204     {
205       /* when playing, display a special mouse pointer inside the playfield */
206       if (game_status == GAME_MODE_PLAYING && !tape.pausing)
207       {
208         if (!playfield_cursor_set && cursor_inside_playfield &&
209             DelayReached(&playfield_cursor_delay, 1000))
210         {
211           SetMouseCursor(CURSOR_PLAYFIELD);
212           playfield_cursor_set = TRUE;
213         }
214       }
215       else if (playfield_cursor_set)
216       {
217         SetMouseCursor(CURSOR_DEFAULT);
218         playfield_cursor_set = FALSE;
219       }
220     }
221
222     /* also execute after pending events have been processed before */
223     HandleNoEvent();
224
225     /* don't use all CPU time when idle; the main loop while playing
226        has its own synchronization and is CPU friendly, too */
227
228     if (game_status == GAME_MODE_PLAYING)
229     {
230       HandleGameActions();
231     }
232     else
233     {
234       if (!PendingEvent())      /* delay only if no pending events */
235         Delay(10);
236     }
237
238     /* refresh window contents from drawing buffer, if needed */
239     BackToFront();
240
241     if (game_status == GAME_MODE_QUIT)
242       return;
243   }
244 }
245
246 void HandleOtherEvents(Event *event)
247 {
248   switch (event->type)
249   {
250     case EVENT_EXPOSE:
251       HandleExposeEvent((ExposeEvent *) event);
252       break;
253
254     case EVENT_UNMAPNOTIFY:
255 #if 0
256       /* This causes the game to stop not only when iconified, but also
257          when on another virtual desktop, which might be not desired. */
258       SleepWhileUnmapped();
259 #endif
260       break;
261
262     case EVENT_FOCUSIN:
263     case EVENT_FOCUSOUT:
264       HandleFocusEvent((FocusChangeEvent *) event);
265       break;
266
267     case EVENT_CLIENTMESSAGE:
268       HandleClientMessageEvent((ClientMessageEvent *) event);
269       break;
270
271 #if defined(TARGET_SDL)
272     case SDL_JOYAXISMOTION:
273     case SDL_JOYBUTTONDOWN:
274     case SDL_JOYBUTTONUP:
275       HandleJoystickEvent(event);
276       break;
277
278     case SDL_SYSWMEVENT:
279       HandleWindowManagerEvent(event);
280       break;
281 #endif
282
283     default:
284       break;
285   }
286 }
287
288 void ClearEventQueue()
289 {
290   while (PendingEvent())
291   {
292     Event event;
293
294     NextEvent(&event);
295
296     switch (event.type)
297     {
298       case EVENT_BUTTONRELEASE:
299         button_status = MB_RELEASED;
300         break;
301
302       case EVENT_KEYRELEASE:
303         ClearPlayerAction();
304         break;
305
306       default:
307         HandleOtherEvents(&event);
308         break;
309     }
310   }
311 }
312
313 void ClearPlayerAction()
314 {
315   int i;
316
317   /* simulate key release events for still pressed keys */
318   key_joystick_mapping = 0;
319   for (i = 0; i < MAX_PLAYERS; i++)
320     stored_player[i].action = 0;
321 }
322
323 void SleepWhileUnmapped()
324 {
325   boolean window_unmapped = TRUE;
326
327   KeyboardAutoRepeatOn();
328
329   while (window_unmapped)
330   {
331     Event event;
332
333     NextEvent(&event);
334
335     switch (event.type)
336     {
337       case EVENT_BUTTONRELEASE:
338         button_status = MB_RELEASED;
339         break;
340
341       case EVENT_KEYRELEASE:
342         key_joystick_mapping = 0;
343         break;
344
345       case EVENT_MAPNOTIFY:
346         window_unmapped = FALSE;
347         break;
348
349       case EVENT_UNMAPNOTIFY:
350         /* this is only to surely prevent the 'should not happen' case
351          * of recursively looping between 'SleepWhileUnmapped()' and
352          * 'HandleOtherEvents()' which usually calls this funtion.
353          */
354         break;
355
356       default:
357         HandleOtherEvents(&event);
358         break;
359     }
360   }
361
362   if (game_status == GAME_MODE_PLAYING)
363     KeyboardAutoRepeatOffUnlessAutoplay();
364 }
365
366 void HandleExposeEvent(ExposeEvent *event)
367 {
368 }
369
370 void HandleButtonEvent(ButtonEvent *event)
371 {
372 #if DEBUG_EVENTS_BUTTON
373   Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
374         event->button,
375         event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
376         event->x, event->y);
377 #endif
378
379   motion_status = FALSE;
380
381   if (event->type == EVENT_BUTTONPRESS)
382     button_status = event->button;
383   else
384     button_status = MB_RELEASED;
385
386   HandleButton(event->x, event->y, button_status, event->button);
387 }
388
389 void HandleMotionEvent(MotionEvent *event)
390 {
391   if (!PointerInWindow(window))
392     return;     /* window and pointer are on different screens */
393
394   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
395     return;
396
397   motion_status = TRUE;
398
399 #if DEBUG_EVENTS_MOTION
400   Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
401         button_status, event->x, event->y);
402 #endif
403
404   HandleButton(event->x, event->y, button_status, button_status);
405 }
406
407 #if defined(TARGET_SDL2)
408
409 void HandleWindowEvent(WindowEvent *event)
410 {
411 #if DEBUG_EVENTS_WINDOW
412   int subtype = event->event;
413
414   char *event_name =
415     (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
416      subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
417      subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
418      subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
419      subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
420      subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
421      subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
422      subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
423      subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
424      subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
425      subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
426      subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
427      subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
428      subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
429      "(UNKNOWN)");
430
431   Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
432         event_name, event->data1, event->data2);
433 #endif
434
435   if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
436       event->event == SDL_WINDOWEVENT_RESIZED ||
437       event->event == SDL_WINDOWEVENT_EXPOSED)
438     SDLRedrawWindow();
439
440   if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
441   {
442     int new_window_width  = event->data1;
443     int new_window_height = event->data2;
444
445     // if window size has changed after resizing, calculate new scaling factor
446     if (new_window_width  != video.window_width ||
447         new_window_height != video.window_height)
448     {
449       int new_xpercent = (100 * new_window_width  / video.width);
450       int new_ypercent = (100 * new_window_height / video.height);
451
452       setup.window_scaling_percent = video.window_scaling_percent =
453         MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
454             MAX_WINDOW_SCALING_PERCENT);
455
456       video.window_width  = new_window_width;
457       video.window_height = new_window_height;
458
459       if (game_status == GAME_MODE_SETUP)
460         RedrawSetupScreenAfterFullscreenToggle();
461
462       SetWindowTitle();
463     }
464   }
465 }
466
467 #define NUM_TOUCH_FINGERS               3
468
469 static struct
470 {
471   boolean touched;
472   SDL_FingerID finger_id;
473   int counter;
474   Key key;
475 } touch_info[NUM_TOUCH_FINGERS];
476
477 void HandleFingerEvent(FingerEvent *event)
478 {
479   static Key motion_key_x = KSYM_UNDEFINED;
480   static Key motion_key_y = KSYM_UNDEFINED;
481   static Key button_key = KSYM_UNDEFINED;
482   static float motion_x1, motion_y1;
483   static float button_x1, button_y1;
484   static SDL_FingerID motion_id = -1;
485   static SDL_FingerID button_id = -1;
486   int move_trigger_distance_percent = 2;   // percent of touchpad width/height
487   int drop_trigger_distance_percent = 5;   // percent of touchpad width/height
488   float move_trigger_distance = (float)move_trigger_distance_percent / 100;
489   float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
490   float event_x = event->x;
491   float event_y = event->y;
492
493 #if DEBUG_EVENTS_FINGER
494   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
495         event->type == EVENT_FINGERPRESS ? "pressed" :
496         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
497         event->touchId,
498         event->fingerId,
499         event->x, event->y,
500         event->dx, event->dy,
501         event->pressure);
502 #endif
503
504   if (game_status != GAME_MODE_PLAYING)
505     return;
506
507   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
508   {
509     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
510                       KEY_PRESSED);
511     Key key = (event->x < 1.0 / 3.0 ?
512                (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
513                 setup.input[0].key.drop) :
514                event->x > 2.0 / 3.0 ?
515                (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
516                 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
517                 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
518                 setup.input[0].key.right) :
519                KSYM_UNDEFINED);
520     char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
521                              "KEY_PRESSED");
522     int i;
523
524     Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
525           getKeyNameFromKey(key), key_status_name, event->fingerId);
526
527     // check if we already know this touch event's finger id
528     for (i = 0; i < NUM_TOUCH_FINGERS; i++)
529     {
530       if (touch_info[i].touched &&
531           touch_info[i].finger_id == event->fingerId)
532       {
533         // Error(ERR_DEBUG, "MARK 1: %d", i);
534
535         break;
536       }
537     }
538
539     if (i >= NUM_TOUCH_FINGERS)
540     {
541       if (key_status == KEY_PRESSED)
542       {
543         int oldest_pos = 0, oldest_counter = touch_info[0].counter;
544
545         // unknown finger id -- get new, empty slot, if available
546         for (i = 0; i < NUM_TOUCH_FINGERS; i++)
547         {
548           if (touch_info[i].counter < oldest_counter)
549           {
550             oldest_pos = i;
551             oldest_counter = touch_info[i].counter;
552
553             // Error(ERR_DEBUG, "MARK 2: %d", i);
554           }
555
556           if (!touch_info[i].touched)
557           {
558             // Error(ERR_DEBUG, "MARK 3: %d", i);
559
560             break;
561           }
562         }
563
564         if (i >= NUM_TOUCH_FINGERS)
565         {
566           // all slots allocated -- use oldest slot
567           i = oldest_pos;
568
569           // Error(ERR_DEBUG, "MARK 4: %d", i);
570         }
571       }
572       else
573       {
574         // release of previously unknown key (should not happen)
575
576         if (key != KSYM_UNDEFINED)
577         {
578           HandleKey(key, KEY_RELEASED);
579
580           Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
581                 getKeyNameFromKey(key), "KEY_RELEASED", i);
582         }
583       }
584     }
585
586     if (i < NUM_TOUCH_FINGERS)
587     {
588       if (key_status == KEY_PRESSED)
589       {
590         if (touch_info[i].key != key)
591         {
592           if (touch_info[i].key != KSYM_UNDEFINED)
593           {
594             HandleKey(touch_info[i].key, KEY_RELEASED);
595
596             Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
597                   getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
598           }
599
600           if (key != KSYM_UNDEFINED)
601           {
602             HandleKey(key, KEY_PRESSED);
603
604             Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
605                   getKeyNameFromKey(key), "KEY_PRESSED", i);
606           }
607         }
608
609         touch_info[i].touched = TRUE;
610         touch_info[i].finger_id = event->fingerId;
611         touch_info[i].counter = Counter();
612         touch_info[i].key = key;
613       }
614       else
615       {
616         if (touch_info[i].key != KSYM_UNDEFINED)
617         {
618           HandleKey(touch_info[i].key, KEY_RELEASED);
619
620           Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
621                 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
622         }
623
624         touch_info[i].touched = FALSE;
625         touch_info[i].finger_id = 0;
626         touch_info[i].counter = 0;
627         touch_info[i].key = 0;
628       }
629     }
630
631     return;
632   }
633
634   // use touch direction control
635
636   if (event->type == EVENT_FINGERPRESS)
637   {
638     if (event_x > 1.0 / 3.0)
639     {
640       // motion area
641
642       motion_id = event->fingerId;
643
644       motion_x1 = event_x;
645       motion_y1 = event_y;
646
647       motion_key_x = KSYM_UNDEFINED;
648       motion_key_y = KSYM_UNDEFINED;
649
650       Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
651     }
652     else
653     {
654       // button area
655
656       button_id = event->fingerId;
657
658       button_x1 = event_x;
659       button_y1 = event_y;
660
661       button_key = setup.input[0].key.snap;
662
663       HandleKey(button_key, KEY_PRESSED);
664
665       Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
666     }
667   }
668   else if (event->type == EVENT_FINGERRELEASE)
669   {
670     if (event->fingerId == motion_id)
671     {
672       motion_id = -1;
673
674       if (motion_key_x != KSYM_UNDEFINED)
675         HandleKey(motion_key_x, KEY_RELEASED);
676       if (motion_key_y != KSYM_UNDEFINED)
677         HandleKey(motion_key_y, KEY_RELEASED);
678
679       motion_key_x = KSYM_UNDEFINED;
680       motion_key_y = KSYM_UNDEFINED;
681
682       Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
683     }
684     else if (event->fingerId == button_id)
685     {
686       button_id = -1;
687
688       if (button_key != KSYM_UNDEFINED)
689         HandleKey(button_key, KEY_RELEASED);
690
691       button_key = KSYM_UNDEFINED;
692
693       Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
694     }
695   }
696   else if (event->type == EVENT_FINGERMOTION)
697   {
698     if (event->fingerId == motion_id)
699     {
700       float distance_x = ABS(event_x - motion_x1);
701       float distance_y = ABS(event_y - motion_y1);
702       Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
703                               event_x > motion_x1 ? setup.input[0].key.right :
704                               KSYM_UNDEFINED);
705       Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
706                               event_y > motion_y1 ? setup.input[0].key.down :
707                               KSYM_UNDEFINED);
708
709       if (distance_x < move_trigger_distance / 2 ||
710           distance_x < distance_y)
711         new_motion_key_x = KSYM_UNDEFINED;
712
713       if (distance_y < move_trigger_distance / 2 ||
714           distance_y < distance_x)
715         new_motion_key_y = KSYM_UNDEFINED;
716
717       if (distance_x > move_trigger_distance ||
718           distance_y > move_trigger_distance)
719       {
720         if (new_motion_key_x != motion_key_x)
721         {
722           if (motion_key_x != KSYM_UNDEFINED)
723             HandleKey(motion_key_x, KEY_RELEASED);
724           if (new_motion_key_x != KSYM_UNDEFINED)
725             HandleKey(new_motion_key_x, KEY_PRESSED);
726         }
727
728         if (new_motion_key_y != motion_key_y)
729         {
730           if (motion_key_y != KSYM_UNDEFINED)
731             HandleKey(motion_key_y, KEY_RELEASED);
732           if (new_motion_key_y != KSYM_UNDEFINED)
733             HandleKey(new_motion_key_y, KEY_PRESSED);
734         }
735
736         motion_x1 = event_x;
737         motion_y1 = event_y;
738
739         motion_key_x = new_motion_key_x;
740         motion_key_y = new_motion_key_y;
741
742         Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
743       }
744     }
745     else if (event->fingerId == button_id)
746     {
747       float distance_x = ABS(event_x - button_x1);
748       float distance_y = ABS(event_y - button_y1);
749
750       if (distance_x < drop_trigger_distance / 2 &&
751           distance_y > drop_trigger_distance)
752       {
753         if (button_key == setup.input[0].key.snap)
754           HandleKey(button_key, KEY_RELEASED);
755
756         button_x1 = event_x;
757         button_y1 = event_y;
758
759         button_key = setup.input[0].key.drop;
760
761         HandleKey(button_key, KEY_PRESSED);
762
763         Error(ERR_DEBUG, "---------- DROP STARTED ----------");
764       }
765     }
766   }
767 }
768
769 static boolean checkTextInputKeyModState()
770 {
771   // when playing, only handle raw key events and ignore text input
772   if (game_status == GAME_MODE_PLAYING)
773     return FALSE;
774
775   return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
776 }
777
778 void HandleTextEvent(TextEvent *event)
779 {
780   char *text = event->text;
781   Key key = getKeyFromKeyName(text);
782
783 #if DEBUG_EVENTS_TEXT
784   Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
785         text,
786         strlen(text),
787         text[0], (int)(text[0]),
788         key,
789         getKeyNameFromKey(key),
790         GetKeyModState());
791 #endif
792
793 #if defined(PLATFORM_ANDROID)
794   if (game_status == GAME_MODE_PSEUDO_TYPENAME)
795   {
796     HandleTypeName(0, key);
797
798     return;
799   }
800 #endif
801
802   // only handle key input with text modifier keys pressed
803   if (checkTextInputKeyModState())
804   {
805     HandleKey(key, KEY_PRESSED);
806     HandleKey(key, KEY_RELEASED);
807   }
808 }
809
810 void HandlePauseResumeEvent(PauseResumeEvent *event)
811 {
812   if (event->type == SDL_APP_WILLENTERBACKGROUND)
813   {
814     Mix_PauseMusic();
815   }
816   else if (event->type == SDL_APP_DIDENTERFOREGROUND)
817   {
818     Mix_ResumeMusic();
819   }
820 }
821
822 #endif
823
824 void HandleKeyEvent(KeyEvent *event)
825 {
826   int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
827   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
828   Key key = GetEventKey(event, with_modifiers);
829   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
830
831 #if DEBUG_EVENTS_KEY
832   Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
833         event->type == EVENT_KEYPRESS ? "pressed" : "released",
834         event->keysym.scancode,
835         event->keysym.sym,
836         keymod,
837         GetKeyModState(),
838         key,
839         getKeyNameFromKey(key));
840 #endif
841
842 #if defined(PLATFORM_ANDROID)
843   // always map the "back" button to the "escape" key on Android devices
844   if (key == KSYM_Back)
845     key = KSYM_Escape;
846 #endif
847
848   HandleKeyModState(keymod, key_status);
849
850 #if defined(TARGET_SDL2)
851   // only handle raw key input without text modifier keys pressed
852   if (!checkTextInputKeyModState())
853     HandleKey(key, key_status);
854 #else
855   HandleKey(key, key_status);
856 #endif
857 }
858
859 void HandleFocusEvent(FocusChangeEvent *event)
860 {
861   static int old_joystick_status = -1;
862
863   if (event->type == EVENT_FOCUSOUT)
864   {
865     KeyboardAutoRepeatOn();
866     old_joystick_status = joystick.status;
867     joystick.status = JOYSTICK_NOT_AVAILABLE;
868
869     ClearPlayerAction();
870   }
871   else if (event->type == EVENT_FOCUSIN)
872   {
873     /* When there are two Rocks'n'Diamonds windows which overlap and
874        the player moves the pointer from one game window to the other,
875        a 'FocusOut' event is generated for the window the pointer is
876        leaving and a 'FocusIn' event is generated for the window the
877        pointer is entering. In some cases, it can happen that the
878        'FocusIn' event is handled by the one game process before the
879        'FocusOut' event by the other game process. In this case the
880        X11 environment would end up with activated keyboard auto repeat,
881        because unfortunately this is a global setting and not (which
882        would be far better) set for each X11 window individually.
883        The effect would be keyboard auto repeat while playing the game
884        (game_status == GAME_MODE_PLAYING), which is not desired.
885        To avoid this special case, we just wait 1/10 second before
886        processing the 'FocusIn' event.
887     */
888
889     if (game_status == GAME_MODE_PLAYING)
890     {
891       Delay(100);
892       KeyboardAutoRepeatOffUnlessAutoplay();
893     }
894
895     if (old_joystick_status != -1)
896       joystick.status = old_joystick_status;
897   }
898 }
899
900 void HandleClientMessageEvent(ClientMessageEvent *event)
901 {
902   if (CheckCloseWindowEvent(event))
903     CloseAllAndExit(0);
904 }
905
906 void HandleWindowManagerEvent(Event *event)
907 {
908 #if defined(TARGET_SDL)
909   SDLHandleWindowManagerEvent(event);
910 #endif
911 }
912
913 void HandleButton(int mx, int my, int button, int button_nr)
914 {
915   static int old_mx = 0, old_my = 0;
916
917   if (button < 0)
918   {
919     mx = old_mx;
920     my = old_my;
921     button = -button;
922   }
923   else
924   {
925     old_mx = mx;
926     old_my = my;
927   }
928
929 #if defined(PLATFORM_ANDROID)
930   if (game_status != GAME_MODE_PLAYING &&
931       HandleGadgets(mx, my, button))
932   {
933     /* do not handle this button event anymore */
934     mx = my = -32;      /* force mouse event to be outside screen tiles */
935   }
936 #else
937   if (HandleGadgets(mx, my, button))
938   {
939     /* do not handle this button event anymore */
940     mx = my = -32;      /* force mouse event to be outside screen tiles */
941   }
942 #endif
943
944   /* do not use scroll wheel button events for anything other than gadgets */
945   if (IS_WHEEL_BUTTON(button_nr))
946     return;
947
948   switch (game_status)
949   {
950     case GAME_MODE_TITLE:
951       HandleTitleScreen(mx, my, 0, 0, button);
952       break;
953
954     case GAME_MODE_MAIN:
955       HandleMainMenu(mx, my, 0, 0, button);
956       break;
957
958     case GAME_MODE_PSEUDO_TYPENAME:
959       HandleTypeName(0, KSYM_Return);
960       break;
961
962     case GAME_MODE_LEVELS:
963       HandleChooseLevelSet(mx, my, 0, 0, button);
964       break;
965
966     case GAME_MODE_LEVELNR:
967       HandleChooseLevelNr(mx, my, 0, 0, button);
968       break;
969
970     case GAME_MODE_SCORES:
971       HandleHallOfFame(0, 0, 0, 0, button);
972       break;
973
974     case GAME_MODE_EDITOR:
975       HandleLevelEditorIdle();
976       break;
977
978     case GAME_MODE_INFO:
979       HandleInfoScreen(mx, my, 0, 0, button);
980       break;
981
982     case GAME_MODE_SETUP:
983       HandleSetupScreen(mx, my, 0, 0, button);
984       break;
985
986     case GAME_MODE_PLAYING:
987 #ifdef DEBUG
988       if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
989         DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
990                  LEVELY((my - SY) / TILESIZE_VAR));
991         // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
992 #endif
993       break;
994
995     default:
996       break;
997   }
998 }
999
1000 static boolean is_string_suffix(char *string, char *suffix)
1001 {
1002   int string_len = strlen(string);
1003   int suffix_len = strlen(suffix);
1004
1005   if (suffix_len > string_len)
1006     return FALSE;
1007
1008   return (strEqual(&string[string_len - suffix_len], suffix));
1009 }
1010
1011 #define MAX_CHEAT_INPUT_LEN     32
1012
1013 static void HandleKeysSpecial(Key key)
1014 {
1015   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1016   char letter = getCharFromKey(key);
1017   int cheat_input_len = strlen(cheat_input);
1018   int i;
1019
1020   if (letter == 0)
1021     return;
1022
1023   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1024   {
1025     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1026       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1027
1028     cheat_input_len = MAX_CHEAT_INPUT_LEN;
1029   }
1030
1031   cheat_input[cheat_input_len++] = letter;
1032   cheat_input[cheat_input_len] = '\0';
1033
1034 #if DEBUG_EVENTS_KEY
1035   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1036 #endif
1037
1038   if (game_status == GAME_MODE_MAIN)
1039   {
1040     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1041         is_string_suffix(cheat_input, ":ist"))
1042     {
1043       InsertSolutionTape();
1044     }
1045     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1046              is_string_suffix(cheat_input, ":rg"))
1047     {
1048       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1049       DrawMainMenu();
1050     }
1051     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1052              is_string_suffix(cheat_input, ":rs"))
1053     {
1054       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1055       DrawMainMenu();
1056     }
1057     else if (is_string_suffix(cheat_input, ":reload-music") ||
1058              is_string_suffix(cheat_input, ":rm"))
1059     {
1060       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1061       DrawMainMenu();
1062     }
1063     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1064              is_string_suffix(cheat_input, ":ra"))
1065     {
1066       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1067                           1 << ARTWORK_TYPE_SOUNDS |
1068                           1 << ARTWORK_TYPE_MUSIC);
1069       DrawMainMenu();
1070     }
1071     else if (is_string_suffix(cheat_input, ":dump-level") ||
1072              is_string_suffix(cheat_input, ":dl"))
1073     {
1074       DumpLevel(&level);
1075     }
1076     else if (is_string_suffix(cheat_input, ":dump-tape") ||
1077              is_string_suffix(cheat_input, ":dt"))
1078     {
1079       DumpTape(&tape);
1080     }
1081     else if (is_string_suffix(cheat_input, ":fix-tape") ||
1082              is_string_suffix(cheat_input, ":ft"))
1083     {
1084       /* fix single-player tapes that contain player input for more than one
1085          player (due to a bug in 3.3.1.2 and earlier versions), which results
1086          in playing levels with more than one player in multi-player mode,
1087          even though the tape was originally recorded in single-player mode */
1088
1089       /* remove player input actions for all players but the first one */
1090       for (i = 1; i < MAX_PLAYERS; i++)
1091         tape.player_participates[i] = FALSE;
1092
1093       tape.changed = TRUE;
1094     }
1095     else if (is_string_suffix(cheat_input, ":save-native-level") ||
1096              is_string_suffix(cheat_input, ":snl"))
1097     {
1098       SaveNativeLevel(&level);
1099     }
1100   }
1101   else if (game_status == GAME_MODE_PLAYING)
1102   {
1103 #ifdef DEBUG
1104     if (is_string_suffix(cheat_input, ".q"))
1105       DEBUG_SetMaximumDynamite();
1106 #endif
1107   }
1108   else if (game_status == GAME_MODE_EDITOR)
1109   {
1110     if (is_string_suffix(cheat_input, ":dump-brush") ||
1111         is_string_suffix(cheat_input, ":DB"))
1112     {
1113       DumpBrush();
1114     }
1115     else if (is_string_suffix(cheat_input, ":DDB"))
1116     {
1117       DumpBrush_Small();
1118     }
1119   }
1120 }
1121
1122 void HandleKey(Key key, int key_status)
1123 {
1124   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1125   static struct SetupKeyboardInfo ski;
1126   static struct SetupShortcutInfo ssi;
1127   static struct
1128   {
1129     Key *key_custom;
1130     Key *key_snap;
1131     Key key_default;
1132     byte action;
1133   } key_info[] =
1134   {
1135     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
1136     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
1137     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
1138     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
1139     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
1140     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
1141   };
1142   int joy = 0;
1143   int i;
1144
1145   if (game_status == GAME_MODE_PLAYING)
1146   {
1147     /* only needed for single-step tape recording mode */
1148     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1149     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1150     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1151     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1152     int pnr;
1153
1154     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1155     {
1156       byte key_action = 0;
1157
1158       if (setup.input[pnr].use_joystick)
1159         continue;
1160
1161       ski = setup.input[pnr].key;
1162
1163       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1164         if (key == *key_info[i].key_custom)
1165           key_action |= key_info[i].action;
1166
1167       /* use combined snap+direction keys for the first player only */
1168       if (pnr == 0)
1169       {
1170         ssi = setup.shortcut;
1171
1172         for (i = 0; i < NUM_DIRECTIONS; i++)
1173           if (key == *key_info[i].key_snap)
1174             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1175       }
1176
1177       /* clear delayed snap and drop actions in single step mode (see below) */
1178       if (tape.single_step)
1179       {
1180         if (clear_snap_button[pnr])
1181         {
1182           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1183           clear_snap_button[pnr] = FALSE;
1184         }
1185
1186         if (clear_drop_button[pnr])
1187         {
1188           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1189           clear_drop_button[pnr] = FALSE;
1190         }
1191       }
1192
1193       if (key_status == KEY_PRESSED)
1194         stored_player[pnr].action |= key_action;
1195       else
1196         stored_player[pnr].action &= ~key_action;
1197
1198       if (tape.single_step && tape.recording && tape.pausing)
1199       {
1200         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1201         {
1202           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1203
1204           /* if snap key already pressed, don't snap when releasing (below) */
1205           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1206             element_snapped[pnr] = TRUE;
1207
1208           /* if drop key already pressed, don't drop when releasing (below) */
1209           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1210             element_dropped[pnr] = TRUE;
1211         }
1212         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1213         {
1214           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1215               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1216           {
1217
1218             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1219                 getRedDiskReleaseFlag_SP() == 0)
1220               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1221
1222             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1223           }
1224         }
1225         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1226         {
1227           if (key_action & KEY_BUTTON_SNAP)
1228           {
1229             /* if snap key was released without moving (see above), snap now */
1230             if (!element_snapped[pnr])
1231             {
1232               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1233
1234               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1235
1236               /* clear delayed snap button on next event */
1237               clear_snap_button[pnr] = TRUE;
1238             }
1239
1240             element_snapped[pnr] = FALSE;
1241           }
1242
1243           if (key_action & KEY_BUTTON_DROP &&
1244               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1245           {
1246             /* if drop key was released without moving (see above), drop now */
1247             if (!element_dropped[pnr])
1248             {
1249               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1250
1251               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1252                   getRedDiskReleaseFlag_SP() != 0)
1253                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1254
1255               /* clear delayed drop button on next event */
1256               clear_drop_button[pnr] = TRUE;
1257             }
1258
1259             element_dropped[pnr] = FALSE;
1260           }
1261         }
1262       }
1263       else if (tape.recording && tape.pausing)
1264       {
1265         /* prevent key release events from un-pausing a paused game */
1266         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1267           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1268       }
1269     }
1270   }
1271   else
1272   {
1273     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1274       if (key == key_info[i].key_default)
1275         joy |= key_info[i].action;
1276   }
1277
1278   if (joy)
1279   {
1280     if (key_status == KEY_PRESSED)
1281       key_joystick_mapping |= joy;
1282     else
1283       key_joystick_mapping &= ~joy;
1284
1285     HandleJoystick();
1286   }
1287
1288   if (game_status != GAME_MODE_PLAYING)
1289     key_joystick_mapping = 0;
1290
1291   if (key_status == KEY_RELEASED)
1292     return;
1293
1294   if ((key == KSYM_F11 ||
1295        ((key == KSYM_Return ||
1296          key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1297       video.fullscreen_available)
1298   {
1299     setup.fullscreen = !setup.fullscreen;
1300
1301     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1302
1303     if (game_status == GAME_MODE_SETUP)
1304       RedrawSetupScreenAfterFullscreenToggle();
1305
1306     return;
1307   }
1308
1309   if ((key == KSYM_minus ||
1310        key == KSYM_plus ||
1311        key == KSYM_0) &&
1312       ((GetKeyModState() & KMOD_Control) ||
1313        (GetKeyModState() & KMOD_Alt)) &&
1314       video.window_scaling_available &&
1315       !video.fullscreen_enabled)
1316   {
1317     if (key == KSYM_0)
1318       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1319     else
1320       setup.window_scaling_percent +=
1321         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1322
1323     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1324       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1325     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1326       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1327
1328     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1329
1330     if (game_status == GAME_MODE_SETUP)
1331       RedrawSetupScreenAfterFullscreenToggle();
1332
1333     return;
1334   }
1335
1336   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1337       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1338   {
1339     GameEnd();
1340
1341     return;
1342   }
1343
1344   if (game_status == GAME_MODE_MAIN &&
1345       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1346   {
1347     StartGameActions(options.network, setup.autorecord, level.random_seed);
1348
1349     return;
1350   }
1351
1352   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1353   {
1354     if (key == setup.shortcut.save_game)
1355       TapeQuickSave();
1356     else if (key == setup.shortcut.load_game)
1357       TapeQuickLoad();
1358     else if (key == setup.shortcut.toggle_pause)
1359       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1360
1361     HandleTapeButtonKeys(key);
1362     HandleSoundButtonKeys(key);
1363   }
1364
1365   if (game_status == GAME_MODE_PLAYING && !network_playing)
1366   {
1367     int centered_player_nr_next = -999;
1368
1369     if (key == setup.shortcut.focus_player_all)
1370       centered_player_nr_next = -1;
1371     else
1372       for (i = 0; i < MAX_PLAYERS; i++)
1373         if (key == setup.shortcut.focus_player[i])
1374           centered_player_nr_next = i;
1375
1376     if (centered_player_nr_next != -999)
1377     {
1378       game.centered_player_nr_next = centered_player_nr_next;
1379       game.set_centered_player = TRUE;
1380
1381       if (tape.recording)
1382       {
1383         tape.centered_player_nr_next = game.centered_player_nr_next;
1384         tape.set_centered_player = TRUE;
1385       }
1386     }
1387   }
1388
1389   HandleKeysSpecial(key);
1390
1391   if (HandleGadgetsKeyInput(key))
1392   {
1393     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1394       key = KSYM_UNDEFINED;
1395   }
1396
1397   switch (game_status)
1398   {
1399     case GAME_MODE_PSEUDO_TYPENAME:
1400       HandleTypeName(0, key);
1401       break;
1402
1403     case GAME_MODE_TITLE:
1404     case GAME_MODE_MAIN:
1405     case GAME_MODE_LEVELS:
1406     case GAME_MODE_LEVELNR:
1407     case GAME_MODE_SETUP:
1408     case GAME_MODE_INFO:
1409     case GAME_MODE_SCORES:
1410       switch (key)
1411       {
1412         case KSYM_space:
1413         case KSYM_Return:
1414           if (game_status == GAME_MODE_TITLE)
1415             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1416           else if (game_status == GAME_MODE_MAIN)
1417             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1418           else if (game_status == GAME_MODE_LEVELS)
1419             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1420           else if (game_status == GAME_MODE_LEVELNR)
1421             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1422           else if (game_status == GAME_MODE_SETUP)
1423             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1424           else if (game_status == GAME_MODE_INFO)
1425             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1426           else if (game_status == GAME_MODE_SCORES)
1427             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1428           break;
1429
1430         case KSYM_Escape:
1431           if (game_status != GAME_MODE_MAIN)
1432             FadeSkipNextFadeIn();
1433
1434           if (game_status == GAME_MODE_TITLE)
1435             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1436           else if (game_status == GAME_MODE_LEVELS)
1437             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1438           else if (game_status == GAME_MODE_LEVELNR)
1439             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1440           else if (game_status == GAME_MODE_SETUP)
1441             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1442           else if (game_status == GAME_MODE_INFO)
1443             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1444           else if (game_status == GAME_MODE_SCORES)
1445             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1446           break;
1447
1448         case KSYM_Page_Up:
1449           if (game_status == GAME_MODE_LEVELS)
1450             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1451           else if (game_status == GAME_MODE_LEVELNR)
1452             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1453           else if (game_status == GAME_MODE_SETUP)
1454             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1455           else if (game_status == GAME_MODE_INFO)
1456             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1457           else if (game_status == GAME_MODE_SCORES)
1458             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1459           break;
1460
1461         case KSYM_Page_Down:
1462           if (game_status == GAME_MODE_LEVELS)
1463             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1464           else if (game_status == GAME_MODE_LEVELNR)
1465             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1466           else if (game_status == GAME_MODE_SETUP)
1467             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1468           else if (game_status == GAME_MODE_INFO)
1469             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1470           else if (game_status == GAME_MODE_SCORES)
1471             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1472           break;
1473
1474 #ifdef DEBUG
1475         case KSYM_0:
1476           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1477           break;
1478
1479         case KSYM_b:
1480           setup.sp_show_border_elements = !setup.sp_show_border_elements;
1481           printf("Supaplex border elements %s\n",
1482                  setup.sp_show_border_elements ? "enabled" : "disabled");
1483           break;
1484 #endif
1485
1486         default:
1487           break;
1488       }
1489       break;
1490
1491     case GAME_MODE_EDITOR:
1492       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1493         HandleLevelEditorKeyInput(key);
1494       break;
1495
1496     case GAME_MODE_PLAYING:
1497     {
1498       switch (key)
1499       {
1500         case KSYM_Escape:
1501           RequestQuitGame(setup.ask_on_escape);
1502           break;
1503
1504 #ifdef DEBUG
1505         case KSYM_0:
1506           if (key == KSYM_0)
1507           {
1508             if (GameFrameDelay == 500)
1509               GameFrameDelay = GAME_FRAME_DELAY;
1510             else
1511               GameFrameDelay = 500;
1512           }
1513           else
1514             GameFrameDelay = (key - KSYM_0) * 10;
1515           printf("Game speed == %d%% (%d ms delay between two frames)\n",
1516                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1517           break;
1518
1519         case KSYM_d:
1520           if (options.debug)
1521           {
1522             options.debug = FALSE;
1523             printf("debug mode disabled\n");
1524           }
1525           else
1526           {
1527             options.debug = TRUE;
1528             printf("debug mode enabled\n");
1529           }
1530           break;
1531
1532 #if 0
1533         case KSYM_s:
1534           if (!global.fps_slowdown)
1535           {
1536             global.fps_slowdown = TRUE;
1537             global.fps_slowdown_factor = 2;
1538             printf("fps slowdown enabled -- display only every 2nd frame\n");
1539           }
1540           else if (global.fps_slowdown_factor == 2)
1541           {
1542             global.fps_slowdown_factor = 4;
1543             printf("fps slowdown enabled -- display only every 4th frame\n");
1544           }
1545           else
1546           {
1547             global.fps_slowdown = FALSE;
1548             global.fps_slowdown_factor = 1;
1549             printf("fps slowdown disabled\n");
1550           }
1551           break;
1552 #endif
1553
1554         case KSYM_v:
1555           printf("::: currently using game engine version %d\n",
1556                  game.engine_version);
1557           break;
1558 #endif
1559
1560         default:
1561           break;
1562       }
1563       break;
1564     }
1565
1566     default:
1567       if (key == KSYM_Escape)
1568       {
1569         game_status = GAME_MODE_MAIN;
1570         DrawMainMenu();
1571
1572         return;
1573       }
1574   }
1575 }
1576
1577 void HandleNoEvent()
1578 {
1579   if (button_status && game_status != GAME_MODE_PLAYING)
1580   {
1581     HandleButton(0, 0, -button_status, button_status);
1582   }
1583   else
1584   {
1585     HandleJoystick();
1586   }
1587
1588 #if defined(NETWORK_AVALIABLE)
1589   if (options.network)
1590     HandleNetworking();
1591 #endif
1592
1593   switch (game_status)
1594   {
1595     case GAME_MODE_MAIN:
1596       DrawPreviewLevelAnimation();
1597       DoAnimation();
1598       break;
1599
1600     case GAME_MODE_LEVELS:
1601     case GAME_MODE_LEVELNR:
1602     case GAME_MODE_SETUP:
1603     case GAME_MODE_INFO:
1604     case GAME_MODE_SCORES:
1605       DoAnimation();
1606       break;
1607
1608     case GAME_MODE_EDITOR:
1609       HandleLevelEditorIdle();
1610       break;
1611
1612     default:
1613       break;
1614   }
1615 }
1616
1617 static int HandleJoystickForAllPlayers()
1618 {
1619   int i;
1620   int result = 0;
1621
1622   for (i = 0; i < MAX_PLAYERS; i++)
1623   {
1624     byte joy_action = 0;
1625
1626     /*
1627     if (!setup.input[i].use_joystick)
1628       continue;
1629       */
1630
1631     joy_action = Joystick(i);
1632     result |= joy_action;
1633
1634     if (!setup.input[i].use_joystick)
1635       continue;
1636
1637     stored_player[i].action = joy_action;
1638   }
1639
1640   return result;
1641 }
1642
1643 void HandleJoystick()
1644 {
1645   int joystick  = HandleJoystickForAllPlayers();
1646   int keyboard  = key_joystick_mapping;
1647   int joy       = (joystick | keyboard);
1648   int left      = joy & JOY_LEFT;
1649   int right     = joy & JOY_RIGHT;
1650   int up        = joy & JOY_UP;
1651   int down      = joy & JOY_DOWN;
1652   int button    = joy & JOY_BUTTON;
1653   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1654   int dx        = (left ? -1    : right ? 1     : 0);
1655   int dy        = (up   ? -1    : down  ? 1     : 0);
1656
1657   switch (game_status)
1658   {
1659     case GAME_MODE_TITLE:
1660     case GAME_MODE_MAIN:
1661     case GAME_MODE_LEVELS:
1662     case GAME_MODE_LEVELNR:
1663     case GAME_MODE_SETUP:
1664     case GAME_MODE_INFO:
1665     {
1666       static unsigned int joystickmove_delay = 0;
1667
1668       if (joystick && !button &&
1669           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1670         newbutton = dx = dy = 0;
1671
1672       if (game_status == GAME_MODE_TITLE)
1673         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1674       else if (game_status == GAME_MODE_MAIN)
1675         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1676       else if (game_status == GAME_MODE_LEVELS)
1677         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1678       else if (game_status == GAME_MODE_LEVELNR)
1679         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1680       else if (game_status == GAME_MODE_SETUP)
1681         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1682       else if (game_status == GAME_MODE_INFO)
1683         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1684       break;
1685     }
1686
1687     case GAME_MODE_SCORES:
1688       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1689       break;
1690
1691     case GAME_MODE_PLAYING:
1692       if (tape.playing || keyboard)
1693         newbutton = ((joy & JOY_BUTTON) != 0);
1694
1695       if (newbutton && AllPlayersGone)
1696       {
1697         GameEnd();
1698
1699         return;
1700       }
1701
1702       break;
1703
1704     default:
1705       break;
1706   }
1707 }