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