rnd-20140224-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   boolean touched;
524   SDL_FingerID finger_id;
525   int counter;
526   Key key;
527 } touch_info[NUM_TOUCH_FINGERS];
528
529 void HandleFingerEvent(FingerEvent *event)
530 {
531   static Key motion_key_x = KSYM_UNDEFINED;
532   static Key motion_key_y = KSYM_UNDEFINED;
533   static Key button_key = KSYM_UNDEFINED;
534   static float motion_x1, motion_y1;
535   static float button_x1, button_y1;
536   static SDL_FingerID motion_id = 0;
537   static SDL_FingerID button_id = 0;
538   int trigger_distance_percent = 1;     // percent of touchpad width/height
539   float trigger_distance = (float)trigger_distance_percent / 100;
540   float event_x = event->x;
541   float event_y = event->y;
542
543 #if 1
544 #if DEBUG_EVENTS
545   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
546         event->type == EVENT_FINGERPRESS ? "pressed" :
547         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
548         event->touchId,
549         event->fingerId,
550         event->x, event->y,
551         event->dx, event->dy,
552         event->pressure);
553 #endif
554 #endif
555
556   if (game_status != GAME_MODE_PLAYING)
557     return;
558
559   if (1)
560   {
561     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
562                       KEY_PRESSED);
563 #if 1
564     Key key = (event->x < 1.0 / 3.0 ?
565                (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
566                 setup.input[0].key.drop) :
567                event->x > 2.0 / 3.0 ?
568                (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
569                 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
570                 event->x < 5.0 / 6.0 ? setup.input[0].key.left :
571                 setup.input[0].key.right) :
572                KSYM_UNDEFINED);
573 #if 0
574     char *key_name = (key == setup.input[0].key.snap  ? "SNAP" :
575                       key == setup.input[0].key.drop  ? "DROP" :
576                       key == setup.input[0].key.up    ? "UP" :
577                       key == setup.input[0].key.down  ? "DOWN" :
578                       key == setup.input[0].key.left  ? "LEFT" :
579                       key == setup.input[0].key.right ? "RIGHT" : "(unknown)");
580 #endif
581     char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
582                              "KEY_PRESSED");
583 #else
584     Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
585                event->y > 2.0 / 3.0 ? setup.input[0].key.down :
586                event->x < 1.0 / 3.0 ? setup.input[0].key.left :
587                event->x > 2.0 / 3.0 ? setup.input[0].key.right :
588                setup.input[0].key.snap);
589 #endif
590     int i;
591
592     Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
593           getKeyNameFromKey(key), key_status_name, event->fingerId);
594
595     // check if we already know this touch event's finger id
596     for (i = 0; i < NUM_TOUCH_FINGERS; i++)
597     {
598       if (touch_info[i].touched &&
599           touch_info[i].finger_id == event->fingerId)
600       {
601         // Error(ERR_DEBUG, "MARK 1: %d", i);
602
603         break;
604       }
605     }
606
607     if (i >= NUM_TOUCH_FINGERS)
608     {
609       if (key_status == KEY_PRESSED)
610       {
611         int oldest_pos = 0, oldest_counter = touch_info[0].counter;
612
613         // unknown finger id -- get new, empty slot, if available
614         for (i = 0; i < NUM_TOUCH_FINGERS; i++)
615         {
616           if (touch_info[i].counter < oldest_counter)
617           {
618             oldest_pos = i;
619             oldest_counter = touch_info[i].counter;
620
621             // Error(ERR_DEBUG, "MARK 2: %d", i);
622           }
623
624           if (!touch_info[i].touched)
625           {
626             // Error(ERR_DEBUG, "MARK 3: %d", i);
627
628             break;
629           }
630         }
631
632         if (i >= NUM_TOUCH_FINGERS)
633         {
634           // all slots allocated -- use oldest slot
635           i = oldest_pos;
636
637           // Error(ERR_DEBUG, "MARK 4: %d", i);
638         }
639       }
640       else
641       {
642         // release of previously unknown key (should not happen)
643
644         if (key != KSYM_UNDEFINED)
645         {
646           HandleKey(key, KEY_RELEASED);
647
648           Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
649                 getKeyNameFromKey(key), "KEY_RELEASED", i);
650         }
651       }
652     }
653
654     if (i < NUM_TOUCH_FINGERS)
655     {
656       if (key_status == KEY_PRESSED)
657       {
658         if (touch_info[i].key != key)
659         {
660           if (touch_info[i].key != KSYM_UNDEFINED)
661           {
662             HandleKey(touch_info[i].key, KEY_RELEASED);
663
664             Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
665                   getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
666           }
667
668           if (key != KSYM_UNDEFINED)
669           {
670             HandleKey(key, KEY_PRESSED);
671
672             Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
673                   getKeyNameFromKey(key), "KEY_PRESSED", i);
674           }
675         }
676
677         touch_info[i].touched = TRUE;
678         touch_info[i].finger_id = event->fingerId;
679         touch_info[i].counter = Counter();
680         touch_info[i].key = key;
681       }
682       else
683       {
684         if (touch_info[i].key != KSYM_UNDEFINED)
685         {
686           HandleKey(touch_info[i].key, KEY_RELEASED);
687
688           Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
689                 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
690         }
691
692         touch_info[i].touched = FALSE;
693         touch_info[i].finger_id = 0;
694         touch_info[i].counter = 0;
695         touch_info[i].key = 0;
696       }
697     }
698
699 #if 0
700 #if 1
701     Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d]",
702           key_name, key_status_name, i);
703 #else
704     Error(ERR_DEBUG, "=> key == %d, key_status == %d [%d]", key, key_status, i);
705 #endif
706 #endif
707
708     return;
709   }
710
711   if (event->type == EVENT_FINGERPRESS)
712   {
713     if (event_x > 1.0 / 3.0)
714     {
715       // motion area
716
717       motion_id = event->fingerId;
718
719       motion_x1 = event_x;
720       motion_y1 = event_y;
721
722       motion_key_x = KSYM_UNDEFINED;
723       motion_key_y = KSYM_UNDEFINED;
724
725       Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
726     }
727     else
728     {
729       // button area
730
731       button_id = event->fingerId;
732
733       button_x1 = event_x;
734       button_y1 = event_y;
735
736       button_key = setup.input[0].key.snap;
737
738       HandleKey(button_key, KEY_PRESSED);
739
740       Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
741     }
742   }
743   else if (event->type == EVENT_FINGERRELEASE)
744   {
745     if (event->fingerId == motion_id)
746     {
747       motion_id = 0;
748
749       if (motion_key_x != KSYM_UNDEFINED)
750         HandleKey(motion_key_x, KEY_RELEASED);
751       if (motion_key_y != KSYM_UNDEFINED)
752         HandleKey(motion_key_y, KEY_RELEASED);
753
754       motion_key_x = KSYM_UNDEFINED;
755       motion_key_y = KSYM_UNDEFINED;
756
757       Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
758     }
759     else if (event->fingerId == button_id)
760     {
761       button_id = 0;
762
763       if (button_key != KSYM_UNDEFINED)
764         HandleKey(button_key, KEY_RELEASED);
765
766       button_key = KSYM_UNDEFINED;
767
768       Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
769     }
770   }
771   else if (event->type == EVENT_FINGERMOTION)
772   {
773     if (event->fingerId == motion_id)
774     {
775       float distance_x = ABS(event_x - motion_x1);
776       float distance_y = ABS(event_y - motion_y1);
777       Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
778                               event_x > motion_x1 ? setup.input[0].key.right :
779                               KSYM_UNDEFINED);
780       Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
781                               event_y > motion_y1 ? setup.input[0].key.down :
782                               KSYM_UNDEFINED);
783
784       if (distance_x < trigger_distance / 2 ||
785           distance_x < distance_y)
786         new_motion_key_x = KSYM_UNDEFINED;
787
788       if (distance_y < trigger_distance / 2 ||
789           distance_y < distance_x)
790         new_motion_key_y = KSYM_UNDEFINED;
791
792       if (distance_x > trigger_distance ||
793           distance_y > trigger_distance)
794       {
795         if (new_motion_key_x != motion_key_x)
796         {
797           if (motion_key_x != KSYM_UNDEFINED)
798             HandleKey(motion_key_x, KEY_RELEASED);
799           if (new_motion_key_x != KSYM_UNDEFINED)
800             HandleKey(new_motion_key_x, KEY_PRESSED);
801         }
802
803         if (new_motion_key_y != motion_key_y)
804         {
805           if (motion_key_y != KSYM_UNDEFINED)
806             HandleKey(motion_key_y, KEY_RELEASED);
807           if (new_motion_key_y != KSYM_UNDEFINED)
808             HandleKey(new_motion_key_y, KEY_PRESSED);
809         }
810
811         motion_x1 = event_x;
812         motion_y1 = event_y;
813
814         motion_key_x = new_motion_key_x;
815         motion_key_y = new_motion_key_y;
816
817         Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
818       }
819     }
820     else if (event->fingerId == button_id)
821     {
822       float distance_x = ABS(event_x - button_x1);
823       float distance_y = ABS(event_y - button_y1);
824
825       if (distance_x < trigger_distance / 2 &&
826           distance_y > trigger_distance)
827       {
828         if (button_key == setup.input[0].key.snap)
829           HandleKey(button_key, KEY_RELEASED);
830
831         button_x1 = event_x;
832         button_y1 = event_y;
833
834         button_key = setup.input[0].key.drop;
835
836         HandleKey(button_key, KEY_PRESSED);
837
838         Error(ERR_DEBUG, "---------- DROP STARTED ----------");
839       }
840     }
841   }
842 }
843
844 #else
845
846 void HandleFingerEvent(FingerEvent *event)
847 {
848 #if 0
849   static int num_events = 0;
850   int max_events = 10;
851 #endif
852
853 #if 0
854 #if DEBUG_EVENTS
855   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
856         event->type == EVENT_FINGERPRESS ? "pressed" :
857         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
858         event->touchId,
859         event->fingerId,
860         event->x, event->y,
861         event->dx, event->dy,
862         event->pressure);
863 #endif
864 #endif
865
866 #if 0
867   int x = (int)(event->x * video.width);
868   int y = (int)(event->y * video.height);
869   int button = MB_LEFTBUTTON;
870
871   Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
872 #endif
873
874 #if 0
875   if (++num_events >= max_events)
876     CloseAllAndExit(0);
877 #endif
878
879 #if 1
880 #if 0
881   if (event->type == EVENT_FINGERPRESS ||
882       event->type == EVENT_FINGERMOTION)
883     button_status = button;
884   else
885     button_status = MB_RELEASED;
886
887   int max_x = SX + SXSIZE;
888   int max_y = SY + SYSIZE;
889 #endif
890
891 #if 1
892   if (game_status == GAME_MODE_PLAYING)
893 #else
894   if (game_status == GAME_MODE_PLAYING &&
895       x < max_x)
896 #endif
897   {
898     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
899                       KEY_PRESSED);
900 #if 1
901     Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
902                event->y > 2.0 / 3.0 ? setup.input[0].key.down :
903                event->x < 1.0 / 3.0 ? setup.input[0].key.left :
904                event->x > 2.0 / 3.0 ? setup.input[0].key.right :
905                setup.input[0].key.drop);
906 #else
907     Key key = (y <     max_y / 3 ? setup.input[0].key.up :
908                y > 2 * max_y / 3 ? setup.input[0].key.down :
909                x <     max_x / 3 ? setup.input[0].key.left :
910                x > 2 * max_x / 3 ? setup.input[0].key.right :
911                setup.input[0].key.drop);
912 #endif
913
914     Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
915
916     HandleKey(key, key_status);
917   }
918   else
919   {
920 #if 0
921     Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
922           button_status, button);
923
924     HandleButton(x, y, button_status, button);
925 #endif
926   }
927 #endif
928 }
929
930 #endif
931
932 static boolean checkTextInputKeyModState()
933 {
934   // when playing, only handle raw key events and ignore text input
935   if (game_status == GAME_MODE_PLAYING)
936     return FALSE;
937
938   return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
939 }
940
941 void HandleTextEvent(TextEvent *event)
942 {
943   char *text = event->text;
944   Key key = getKeyFromKeyName(text);
945
946 #if DEBUG_EVENTS
947   Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
948         text,
949         strlen(text),
950         text[0], (int)(text[0]),
951         key,
952         getKeyNameFromKey(key),
953         GetKeyModState());
954 #endif
955
956   // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
957   /*
958   if (game_status != GAME_MODE_PLAYING &&
959       (GetKeyModState() & KMOD_TextInput) != KMOD_None)
960   */
961   if (checkTextInputKeyModState())
962   {
963     HandleKey(key, KEY_PRESSED);
964     HandleKey(key, KEY_RELEASED);
965   }
966 }
967 #endif
968
969 void HandleKeyEvent(KeyEvent *event)
970 {
971   int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
972   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
973   Key key = GetEventKey(event, with_modifiers);
974   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
975
976 #if DEBUG_EVENTS
977   Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
978         event->type == EVENT_KEYPRESS ? "pressed" : "released",
979         event->keysym.scancode,
980         event->keysym.sym,
981         keymod,
982         GetKeyModState(),
983         key,
984         getKeyNameFromKey(key));
985 #endif
986
987 #if 0
988   if (key == KSYM_Menu)
989     Error(ERR_DEBUG, "menu key pressed");
990   else if (key == KSYM_Back)
991     Error(ERR_DEBUG, "back key pressed");
992 #endif
993
994 #if defined(PLATFORM_ANDROID)
995   // always map the "back" button to the "escape" key on Android devices
996   if (key == KSYM_Back)
997     key = KSYM_Escape;
998 #endif
999
1000   HandleKeyModState(keymod, key_status);
1001
1002 #if defined(TARGET_SDL2)
1003
1004   // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
1005   /*
1006   if (game_status == GAME_MODE_PLAYING ||
1007       (GetKeyModState() & KMOD_TextInput) == KMOD_None)
1008   */
1009   if (!checkTextInputKeyModState())
1010     HandleKey(key, key_status);
1011 #else
1012   HandleKey(key, key_status);
1013 #endif
1014 }
1015
1016 void HandleFocusEvent(FocusChangeEvent *event)
1017 {
1018   static int old_joystick_status = -1;
1019
1020   if (event->type == EVENT_FOCUSOUT)
1021   {
1022     KeyboardAutoRepeatOn();
1023     old_joystick_status = joystick.status;
1024     joystick.status = JOYSTICK_NOT_AVAILABLE;
1025
1026     ClearPlayerAction();
1027   }
1028   else if (event->type == EVENT_FOCUSIN)
1029   {
1030     /* When there are two Rocks'n'Diamonds windows which overlap and
1031        the player moves the pointer from one game window to the other,
1032        a 'FocusOut' event is generated for the window the pointer is
1033        leaving and a 'FocusIn' event is generated for the window the
1034        pointer is entering. In some cases, it can happen that the
1035        'FocusIn' event is handled by the one game process before the
1036        'FocusOut' event by the other game process. In this case the
1037        X11 environment would end up with activated keyboard auto repeat,
1038        because unfortunately this is a global setting and not (which
1039        would be far better) set for each X11 window individually.
1040        The effect would be keyboard auto repeat while playing the game
1041        (game_status == GAME_MODE_PLAYING), which is not desired.
1042        To avoid this special case, we just wait 1/10 second before
1043        processing the 'FocusIn' event.
1044     */
1045
1046     if (game_status == GAME_MODE_PLAYING)
1047     {
1048       Delay(100);
1049       KeyboardAutoRepeatOffUnlessAutoplay();
1050     }
1051
1052     if (old_joystick_status != -1)
1053       joystick.status = old_joystick_status;
1054   }
1055 }
1056
1057 void HandleClientMessageEvent(ClientMessageEvent *event)
1058 {
1059   if (CheckCloseWindowEvent(event))
1060     CloseAllAndExit(0);
1061 }
1062
1063 void HandleWindowManagerEvent(Event *event)
1064 {
1065 #if defined(TARGET_SDL)
1066   SDLHandleWindowManagerEvent(event);
1067 #endif
1068 }
1069
1070 void HandleButton(int mx, int my, int button, int button_nr)
1071 {
1072   static int old_mx = 0, old_my = 0;
1073
1074   if (button < 0)
1075   {
1076     mx = old_mx;
1077     my = old_my;
1078     button = -button;
1079   }
1080   else
1081   {
1082     old_mx = mx;
1083     old_my = my;
1084   }
1085
1086 #if defined(PLATFORM_ANDROID)
1087   if (game_status != GAME_MODE_PLAYING &&
1088       HandleGadgets(mx, my, button))
1089   {
1090     /* do not handle this button event anymore */
1091     mx = my = -32;      /* force mouse event to be outside screen tiles */
1092   }
1093 #else
1094   if (HandleGadgets(mx, my, button))
1095   {
1096     /* do not handle this button event anymore */
1097     mx = my = -32;      /* force mouse event to be outside screen tiles */
1098   }
1099 #endif
1100
1101   /* do not use scroll wheel button events for anything other than gadgets */
1102   if (IS_WHEEL_BUTTON(button_nr))
1103     return;
1104
1105 #if 0
1106   Error(ERR_DEBUG, "::: game_status == %d", game_status);
1107 #endif
1108
1109   switch (game_status)
1110   {
1111     case GAME_MODE_TITLE:
1112       HandleTitleScreen(mx, my, 0, 0, button);
1113       break;
1114
1115     case GAME_MODE_MAIN:
1116       HandleMainMenu(mx, my, 0, 0, button);
1117       break;
1118
1119     case GAME_MODE_PSEUDO_TYPENAME:
1120       HandleTypeName(0, KSYM_Return);
1121       break;
1122
1123     case GAME_MODE_LEVELS:
1124       HandleChooseLevelSet(mx, my, 0, 0, button);
1125       break;
1126
1127     case GAME_MODE_LEVELNR:
1128       HandleChooseLevelNr(mx, my, 0, 0, button);
1129       break;
1130
1131     case GAME_MODE_SCORES:
1132       HandleHallOfFame(0, 0, 0, 0, button);
1133       break;
1134
1135     case GAME_MODE_EDITOR:
1136       HandleLevelEditorIdle();
1137       break;
1138
1139     case GAME_MODE_INFO:
1140       HandleInfoScreen(mx, my, 0, 0, button);
1141       break;
1142
1143     case GAME_MODE_SETUP:
1144       HandleSetupScreen(mx, my, 0, 0, button);
1145       break;
1146
1147     case GAME_MODE_PLAYING:
1148 #ifdef DEBUG
1149       if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1150         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1151 #endif
1152       break;
1153
1154     default:
1155       break;
1156   }
1157 }
1158
1159 static boolean is_string_suffix(char *string, char *suffix)
1160 {
1161   int string_len = strlen(string);
1162   int suffix_len = strlen(suffix);
1163
1164   if (suffix_len > string_len)
1165     return FALSE;
1166
1167   return (strEqual(&string[string_len - suffix_len], suffix));
1168 }
1169
1170 #define MAX_CHEAT_INPUT_LEN     32
1171
1172 static void HandleKeysSpecial(Key key)
1173 {
1174   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1175   char letter = getCharFromKey(key);
1176   int cheat_input_len = strlen(cheat_input);
1177   int i;
1178
1179   if (letter == 0)
1180     return;
1181
1182   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1183   {
1184     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1185       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1186
1187     cheat_input_len = MAX_CHEAT_INPUT_LEN;
1188   }
1189
1190   cheat_input[cheat_input_len++] = letter;
1191   cheat_input[cheat_input_len] = '\0';
1192
1193 #if DEBUG_EVENTS
1194   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1195 #endif
1196
1197   if (game_status == GAME_MODE_MAIN)
1198   {
1199     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1200         is_string_suffix(cheat_input, ":ist"))
1201     {
1202       InsertSolutionTape();
1203     }
1204     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1205              is_string_suffix(cheat_input, ":rg"))
1206     {
1207       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1208       DrawMainMenu();
1209     }
1210     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1211              is_string_suffix(cheat_input, ":rs"))
1212     {
1213       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1214       DrawMainMenu();
1215     }
1216     else if (is_string_suffix(cheat_input, ":reload-music") ||
1217              is_string_suffix(cheat_input, ":rm"))
1218     {
1219       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1220       DrawMainMenu();
1221     }
1222     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1223              is_string_suffix(cheat_input, ":ra"))
1224     {
1225       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1226                           1 << ARTWORK_TYPE_SOUNDS |
1227                           1 << ARTWORK_TYPE_MUSIC);
1228       DrawMainMenu();
1229     }
1230     else if (is_string_suffix(cheat_input, ":dump-level") ||
1231              is_string_suffix(cheat_input, ":dl"))
1232     {
1233       DumpLevel(&level);
1234     }
1235     else if (is_string_suffix(cheat_input, ":dump-tape") ||
1236              is_string_suffix(cheat_input, ":dt"))
1237     {
1238       DumpTape(&tape);
1239     }
1240     else if (is_string_suffix(cheat_input, ":fix-tape") ||
1241              is_string_suffix(cheat_input, ":ft"))
1242     {
1243       /* fix single-player tapes that contain player input for more than one
1244          player (due to a bug in 3.3.1.2 and earlier versions), which results
1245          in playing levels with more than one player in multi-player mode,
1246          even though the tape was originally recorded in single-player mode */
1247
1248       /* remove player input actions for all players but the first one */
1249       for (i = 1; i < MAX_PLAYERS; i++)
1250         tape.player_participates[i] = FALSE;
1251
1252       tape.changed = TRUE;
1253     }
1254     else if (is_string_suffix(cheat_input, ":save-native-level") ||
1255              is_string_suffix(cheat_input, ":snl"))
1256     {
1257       SaveNativeLevel(&level);
1258     }
1259   }
1260   else if (game_status == GAME_MODE_PLAYING)
1261   {
1262 #ifdef DEBUG
1263     if (is_string_suffix(cheat_input, ".q"))
1264       DEBUG_SetMaximumDynamite();
1265 #endif
1266   }
1267   else if (game_status == GAME_MODE_EDITOR)
1268   {
1269     if (is_string_suffix(cheat_input, ":dump-brush") ||
1270         is_string_suffix(cheat_input, ":DB"))
1271     {
1272       DumpBrush();
1273     }
1274     else if (is_string_suffix(cheat_input, ":DDB"))
1275     {
1276       DumpBrush_Small();
1277     }
1278   }
1279 }
1280
1281 void HandleKey(Key key, int key_status)
1282 {
1283   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1284   static struct SetupKeyboardInfo ski;
1285   static struct SetupShortcutInfo ssi;
1286   static struct
1287   {
1288     Key *key_custom;
1289     Key *key_snap;
1290     Key key_default;
1291     byte action;
1292   } key_info[] =
1293   {
1294     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
1295     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
1296     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
1297     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
1298     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
1299     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
1300   };
1301   int joy = 0;
1302   int i;
1303
1304   if (game_status == GAME_MODE_PLAYING)
1305   {
1306     /* only needed for single-step tape recording mode */
1307     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1308     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1309     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1310     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1311     int pnr;
1312
1313     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1314     {
1315       byte key_action = 0;
1316
1317       if (setup.input[pnr].use_joystick)
1318         continue;
1319
1320       ski = setup.input[pnr].key;
1321
1322       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1323         if (key == *key_info[i].key_custom)
1324           key_action |= key_info[i].action;
1325
1326       /* use combined snap+direction keys for the first player only */
1327       if (pnr == 0)
1328       {
1329         ssi = setup.shortcut;
1330
1331         for (i = 0; i < NUM_DIRECTIONS; i++)
1332           if (key == *key_info[i].key_snap)
1333             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1334       }
1335
1336       /* clear delayed snap and drop actions in single step mode (see below) */
1337       if (tape.single_step)
1338       {
1339         if (clear_snap_button[pnr])
1340         {
1341           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1342           clear_snap_button[pnr] = FALSE;
1343         }
1344
1345         if (clear_drop_button[pnr])
1346         {
1347           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1348           clear_drop_button[pnr] = FALSE;
1349         }
1350       }
1351
1352       if (key_status == KEY_PRESSED)
1353         stored_player[pnr].action |= key_action;
1354       else
1355         stored_player[pnr].action &= ~key_action;
1356
1357       if (tape.single_step && tape.recording && tape.pausing)
1358       {
1359         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1360         {
1361           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1362
1363           /* if snap key already pressed, don't snap when releasing (below) */
1364           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1365             element_snapped[pnr] = TRUE;
1366
1367           /* if drop key already pressed, don't drop when releasing (below) */
1368           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1369             element_dropped[pnr] = TRUE;
1370         }
1371 #if 1
1372         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1373         {
1374           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1375               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1376           {
1377 #if 0
1378             printf("::: drop key pressed\n");
1379 #endif
1380
1381             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1382                 getRedDiskReleaseFlag_SP() == 0)
1383               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1384
1385             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1386           }
1387         }
1388 #endif
1389         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1390         {
1391           if (key_action & KEY_BUTTON_SNAP)
1392           {
1393             /* if snap key was released without moving (see above), snap now */
1394             if (!element_snapped[pnr])
1395             {
1396               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1397
1398               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1399
1400               /* clear delayed snap button on next event */
1401               clear_snap_button[pnr] = TRUE;
1402             }
1403
1404             element_snapped[pnr] = FALSE;
1405           }
1406
1407 #if 1
1408           if (key_action & KEY_BUTTON_DROP &&
1409               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1410           {
1411             /* if drop key was released without moving (see above), drop now */
1412             if (!element_dropped[pnr])
1413             {
1414               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1415
1416               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1417                   getRedDiskReleaseFlag_SP() != 0)
1418                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1419
1420               /* clear delayed drop button on next event */
1421               clear_drop_button[pnr] = TRUE;
1422             }
1423
1424             element_dropped[pnr] = FALSE;
1425           }
1426 #endif
1427         }
1428       }
1429       else if (tape.recording && tape.pausing)
1430       {
1431         /* prevent key release events from un-pausing a paused game */
1432         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1433           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1434       }
1435     }
1436   }
1437   else
1438   {
1439     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1440       if (key == key_info[i].key_default)
1441         joy |= key_info[i].action;
1442   }
1443
1444   if (joy)
1445   {
1446     if (key_status == KEY_PRESSED)
1447       key_joystick_mapping |= joy;
1448     else
1449       key_joystick_mapping &= ~joy;
1450
1451     HandleJoystick();
1452   }
1453
1454   if (game_status != GAME_MODE_PLAYING)
1455     key_joystick_mapping = 0;
1456
1457   if (key_status == KEY_RELEASED)
1458     return;
1459
1460   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1461       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1462   {
1463     setup.fullscreen = !setup.fullscreen;
1464
1465 #if 0
1466     printf("::: %d\n", setup.window_scaling_percent);
1467 #endif
1468
1469     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1470
1471     if (game_status == GAME_MODE_SETUP)
1472       RedrawSetupScreenAfterFullscreenToggle();
1473
1474     return;
1475   }
1476
1477   if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1478       (GetKeyModState() & KMOD_Alt) && video.window_scaling_available &&
1479       !video.fullscreen_enabled)
1480   {
1481     if (key == KSYM_0)
1482       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1483     else
1484       setup.window_scaling_percent +=
1485         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1486
1487     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1488       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1489     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1490       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1491
1492     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1493
1494     if (game_status == GAME_MODE_SETUP)
1495       RedrawSetupScreenAfterFullscreenToggle();
1496
1497     return;
1498   }
1499
1500 #if 0
1501   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1502       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1503 #else
1504   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1505       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1506 #endif
1507   {
1508     GameEnd();
1509
1510     return;
1511   }
1512
1513   if (game_status == GAME_MODE_MAIN &&
1514       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1515   {
1516     StartGameActions(options.network, setup.autorecord, level.random_seed);
1517
1518     return;
1519   }
1520
1521   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1522   {
1523     if (key == setup.shortcut.save_game)
1524       TapeQuickSave();
1525     else if (key == setup.shortcut.load_game)
1526       TapeQuickLoad();
1527     else if (key == setup.shortcut.toggle_pause)
1528       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1529
1530     HandleTapeButtonKeys(key);
1531     HandleSoundButtonKeys(key);
1532   }
1533
1534   if (game_status == GAME_MODE_PLAYING && !network_playing)
1535   {
1536     int centered_player_nr_next = -999;
1537
1538     if (key == setup.shortcut.focus_player_all)
1539       centered_player_nr_next = -1;
1540     else
1541       for (i = 0; i < MAX_PLAYERS; i++)
1542         if (key == setup.shortcut.focus_player[i])
1543           centered_player_nr_next = i;
1544
1545     if (centered_player_nr_next != -999)
1546     {
1547       game.centered_player_nr_next = centered_player_nr_next;
1548       game.set_centered_player = TRUE;
1549
1550       if (tape.recording)
1551       {
1552         tape.centered_player_nr_next = game.centered_player_nr_next;
1553         tape.set_centered_player = TRUE;
1554       }
1555     }
1556   }
1557
1558   HandleKeysSpecial(key);
1559
1560   if (HandleGadgetsKeyInput(key))
1561   {
1562     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1563       key = KSYM_UNDEFINED;
1564   }
1565
1566   switch (game_status)
1567   {
1568     case GAME_MODE_PSEUDO_TYPENAME:
1569       HandleTypeName(0, key);
1570       break;
1571
1572     case GAME_MODE_TITLE:
1573     case GAME_MODE_MAIN:
1574     case GAME_MODE_LEVELS:
1575     case GAME_MODE_LEVELNR:
1576     case GAME_MODE_SETUP:
1577     case GAME_MODE_INFO:
1578     case GAME_MODE_SCORES:
1579       switch (key)
1580       {
1581         case KSYM_space:
1582         case KSYM_Return:
1583           if (game_status == GAME_MODE_TITLE)
1584             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1585           else if (game_status == GAME_MODE_MAIN)
1586             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1587           else if (game_status == GAME_MODE_LEVELS)
1588             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1589           else if (game_status == GAME_MODE_LEVELNR)
1590             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1591           else if (game_status == GAME_MODE_SETUP)
1592             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1593           else if (game_status == GAME_MODE_INFO)
1594             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1595           else if (game_status == GAME_MODE_SCORES)
1596             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1597           break;
1598
1599         case KSYM_Escape:
1600           if (game_status != GAME_MODE_MAIN)
1601             FadeSkipNextFadeIn();
1602
1603           if (game_status == GAME_MODE_TITLE)
1604             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1605           else if (game_status == GAME_MODE_LEVELS)
1606             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1607           else if (game_status == GAME_MODE_LEVELNR)
1608             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1609           else if (game_status == GAME_MODE_SETUP)
1610             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1611           else if (game_status == GAME_MODE_INFO)
1612             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1613           else if (game_status == GAME_MODE_SCORES)
1614             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1615           break;
1616
1617         case KSYM_Page_Up:
1618           if (game_status == GAME_MODE_LEVELS)
1619             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1620           else if (game_status == GAME_MODE_LEVELNR)
1621             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1622           else if (game_status == GAME_MODE_SETUP)
1623             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1624           else if (game_status == GAME_MODE_INFO)
1625             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1626           else if (game_status == GAME_MODE_SCORES)
1627             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1628           break;
1629
1630         case KSYM_Page_Down:
1631           if (game_status == GAME_MODE_LEVELS)
1632             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1633           else if (game_status == GAME_MODE_LEVELNR)
1634             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1635           else if (game_status == GAME_MODE_SETUP)
1636             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1637           else if (game_status == GAME_MODE_INFO)
1638             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1639           else if (game_status == GAME_MODE_SCORES)
1640             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1641           break;
1642
1643 #ifdef DEBUG
1644         case KSYM_0:
1645           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1646           break;
1647
1648         case KSYM_b:
1649           setup.sp_show_border_elements = !setup.sp_show_border_elements;
1650           printf("Supaplex border elements %s\n",
1651                  setup.sp_show_border_elements ? "enabled" : "disabled");
1652           break;
1653 #endif
1654
1655         default:
1656           break;
1657       }
1658       break;
1659
1660     case GAME_MODE_EDITOR:
1661       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1662         HandleLevelEditorKeyInput(key);
1663       break;
1664
1665     case GAME_MODE_PLAYING:
1666     {
1667       switch (key)
1668       {
1669         case KSYM_Escape:
1670           RequestQuitGame(setup.ask_on_escape);
1671           break;
1672
1673 #ifdef DEBUG
1674         case KSYM_0:
1675 #if 0
1676         case KSYM_1:
1677         case KSYM_2:
1678         case KSYM_3:
1679         case KSYM_4:
1680         case KSYM_5:
1681         case KSYM_6:
1682         case KSYM_7:
1683         case KSYM_8:
1684         case KSYM_9:
1685 #endif
1686           if (key == KSYM_0)
1687           {
1688             if (GameFrameDelay == 500)
1689               GameFrameDelay = GAME_FRAME_DELAY;
1690             else
1691               GameFrameDelay = 500;
1692           }
1693           else
1694             GameFrameDelay = (key - KSYM_0) * 10;
1695           printf("Game speed == %d%% (%d ms delay between two frames)\n",
1696                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1697           break;
1698
1699         case KSYM_d:
1700           if (options.debug)
1701           {
1702             options.debug = FALSE;
1703             printf("debug mode disabled\n");
1704           }
1705           else
1706           {
1707             options.debug = TRUE;
1708             printf("debug mode enabled\n");
1709           }
1710           break;
1711
1712 #if 0
1713         case KSYM_s:
1714           if (!global.fps_slowdown)
1715           {
1716             global.fps_slowdown = TRUE;
1717             global.fps_slowdown_factor = 2;
1718             printf("fps slowdown enabled -- display only every 2nd frame\n");
1719           }
1720           else if (global.fps_slowdown_factor == 2)
1721           {
1722             global.fps_slowdown_factor = 4;
1723             printf("fps slowdown enabled -- display only every 4th frame\n");
1724           }
1725           else
1726           {
1727             global.fps_slowdown = FALSE;
1728             global.fps_slowdown_factor = 1;
1729             printf("fps slowdown disabled\n");
1730           }
1731           break;
1732 #endif
1733
1734 #if 0
1735         case KSYM_f:
1736           ScrollStepSize = TILEX / 8;
1737           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1738           break;
1739
1740         case KSYM_g:
1741           ScrollStepSize = TILEX / 4;
1742           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1743           break;
1744
1745         case KSYM_h:
1746           ScrollStepSize = TILEX / 2;
1747           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1748           break;
1749
1750         case KSYM_l:
1751           ScrollStepSize = TILEX;
1752           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1753           break;
1754 #endif
1755
1756         case KSYM_v:
1757           printf("::: currently using game engine version %d\n",
1758                  game.engine_version);
1759           break;
1760 #endif
1761
1762         default:
1763           break;
1764       }
1765       break;
1766     }
1767
1768     default:
1769       if (key == KSYM_Escape)
1770       {
1771         game_status = GAME_MODE_MAIN;
1772         DrawMainMenu();
1773
1774         return;
1775       }
1776   }
1777 }
1778
1779 void HandleNoEvent()
1780 {
1781   if (button_status && game_status != GAME_MODE_PLAYING)
1782   {
1783     HandleButton(0, 0, -button_status, button_status);
1784
1785 #if 0
1786     return;
1787 #endif
1788   }
1789   else
1790   {
1791     HandleJoystick();
1792   }
1793
1794 #if defined(NETWORK_AVALIABLE)
1795   if (options.network)
1796     HandleNetworking();
1797 #endif
1798
1799   switch (game_status)
1800   {
1801     case GAME_MODE_MAIN:
1802       DrawPreviewLevelAnimation();
1803       DoAnimation();
1804       break;
1805
1806     case GAME_MODE_LEVELS:
1807     case GAME_MODE_LEVELNR:
1808     case GAME_MODE_SETUP:
1809     case GAME_MODE_INFO:
1810     case GAME_MODE_SCORES:
1811       DoAnimation();
1812       break;
1813
1814     case GAME_MODE_EDITOR:
1815       HandleLevelEditorIdle();
1816       break;
1817
1818     default:
1819       break;
1820   }
1821 }
1822
1823 static int HandleJoystickForAllPlayers()
1824 {
1825   int i;
1826   int result = 0;
1827
1828   for (i = 0; i < MAX_PLAYERS; i++)
1829   {
1830     byte joy_action = 0;
1831
1832     /*
1833     if (!setup.input[i].use_joystick)
1834       continue;
1835       */
1836
1837     joy_action = Joystick(i);
1838     result |= joy_action;
1839
1840     if (!setup.input[i].use_joystick)
1841       continue;
1842
1843     stored_player[i].action = joy_action;
1844   }
1845
1846   return result;
1847 }
1848
1849 void HandleJoystick()
1850 {
1851   int joystick  = HandleJoystickForAllPlayers();
1852   int keyboard  = key_joystick_mapping;
1853   int joy       = (joystick | keyboard);
1854   int left      = joy & JOY_LEFT;
1855   int right     = joy & JOY_RIGHT;
1856   int up        = joy & JOY_UP;
1857   int down      = joy & JOY_DOWN;
1858   int button    = joy & JOY_BUTTON;
1859   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1860   int dx        = (left ? -1    : right ? 1     : 0);
1861   int dy        = (up   ? -1    : down  ? 1     : 0);
1862
1863   switch (game_status)
1864   {
1865     case GAME_MODE_TITLE:
1866     case GAME_MODE_MAIN:
1867     case GAME_MODE_LEVELS:
1868     case GAME_MODE_LEVELNR:
1869     case GAME_MODE_SETUP:
1870     case GAME_MODE_INFO:
1871     {
1872       static unsigned int joystickmove_delay = 0;
1873
1874       if (joystick && !button &&
1875           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1876         newbutton = dx = dy = 0;
1877
1878       if (game_status == GAME_MODE_TITLE)
1879         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1880       else if (game_status == GAME_MODE_MAIN)
1881         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1882       else if (game_status == GAME_MODE_LEVELS)
1883         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1884       else if (game_status == GAME_MODE_LEVELNR)
1885         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1886       else if (game_status == GAME_MODE_SETUP)
1887         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1888       else if (game_status == GAME_MODE_INFO)
1889         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1890       break;
1891     }
1892
1893     case GAME_MODE_SCORES:
1894       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1895       break;
1896
1897 #if 0
1898     case GAME_MODE_EDITOR:
1899       HandleLevelEditorIdle();
1900       break;
1901 #endif
1902
1903     case GAME_MODE_PLAYING:
1904       if (tape.playing || keyboard)
1905         newbutton = ((joy & JOY_BUTTON) != 0);
1906
1907 #if 0
1908       if (newbutton && local_player->LevelSolved_GameEnd)
1909 #else
1910       if (newbutton && AllPlayersGone)
1911 #endif
1912       {
1913         GameEnd();
1914
1915         return;
1916       }
1917
1918       break;
1919
1920     default:
1921       break;
1922   }
1923 }