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