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