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