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