moved key event handling for debug keys to separate function
[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(PLATFORM_ANDROID)
871   if (game_status == GAME_MODE_PSEUDO_TYPENAME)
872   {
873     HandleTypeName(0, key);
874
875     return;
876   }
877 #endif
878
879   // only handle key input with text modifier keys pressed
880   if (checkTextInputKeyModState())
881   {
882     HandleKey(key, KEY_PRESSED);
883     HandleKey(key, KEY_RELEASED);
884   }
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_Control) ||
1213                                (GetKeyModState() & KMOD_Alt) ||
1214                                (GetKeyModState() & KMOD_Meta));
1215
1216     for (i = 0; i < NUM_DEBUG_FRAME_DELAY_KEYS; i++)
1217     {
1218       if (key == setup.debug.frame_delay_key[i] &&
1219           (mod_key_pressed || !setup.debug.frame_delay_use_mod_key))
1220       {
1221         GameFrameDelay = (GameFrameDelay != setup.debug.frame_delay[i] ?
1222                           setup.debug.frame_delay[i] : GAME_FRAME_DELAY);
1223
1224         if (!setup.debug.frame_delay_game_only)
1225           MenuFrameDelay = GameFrameDelay;
1226
1227         SetVideoFrameDelay(GameFrameDelay);
1228
1229         if (GameFrameDelay > ONE_SECOND_DELAY)
1230           Error(ERR_DEBUG, "frame delay == %d ms", GameFrameDelay);
1231         else if (GameFrameDelay != 0)
1232           Error(ERR_DEBUG, "frame delay == %d ms (max. %d fps / %d %%)",
1233                 GameFrameDelay, ONE_SECOND_DELAY / GameFrameDelay,
1234                 GAME_FRAME_DELAY * 100 / GameFrameDelay);
1235         else
1236           Error(ERR_DEBUG, "frame delay == 0 ms (maximum speed)");
1237
1238         break;
1239       }
1240     }
1241   }
1242
1243   if (game_status == GAME_MODE_PLAYING)
1244   {
1245     if (key == KSYM_d)
1246     {
1247       options.debug = !options.debug;
1248
1249       Error(ERR_DEBUG, "debug mode %s",
1250             (options.debug ? "enabled" : "disabled"));
1251     }
1252     else if (key == KSYM_v)
1253     {
1254       Error(ERR_DEBUG, "currently using game engine version %d",
1255             game.engine_version);
1256     }
1257   }
1258 #endif
1259 }
1260
1261 void HandleKey(Key key, int key_status)
1262 {
1263   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1264   static boolean ignore_repeated_key = FALSE;
1265   static struct SetupKeyboardInfo ski;
1266   static struct SetupShortcutInfo ssi;
1267   static struct
1268   {
1269     Key *key_custom;
1270     Key *key_snap;
1271     Key key_default;
1272     byte action;
1273   } key_info[] =
1274   {
1275     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
1276     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
1277     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
1278     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
1279     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
1280     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
1281   };
1282   int joy = 0;
1283   int i;
1284
1285   if (game_status == GAME_MODE_PLAYING)
1286   {
1287     /* only needed for single-step tape recording mode */
1288     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1289     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1290     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1291     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1292     int pnr;
1293
1294     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1295     {
1296       byte key_action = 0;
1297
1298       if (setup.input[pnr].use_joystick)
1299         continue;
1300
1301       ski = setup.input[pnr].key;
1302
1303       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1304         if (key == *key_info[i].key_custom)
1305           key_action |= key_info[i].action;
1306
1307       /* use combined snap+direction keys for the first player only */
1308       if (pnr == 0)
1309       {
1310         ssi = setup.shortcut;
1311
1312         for (i = 0; i < NUM_DIRECTIONS; i++)
1313           if (key == *key_info[i].key_snap)
1314             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1315       }
1316
1317       /* clear delayed snap and drop actions in single step mode (see below) */
1318       if (tape.single_step)
1319       {
1320         if (clear_snap_button[pnr])
1321         {
1322           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1323           clear_snap_button[pnr] = FALSE;
1324         }
1325
1326         if (clear_drop_button[pnr])
1327         {
1328           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1329           clear_drop_button[pnr] = FALSE;
1330         }
1331       }
1332
1333       if (key_status == KEY_PRESSED)
1334         stored_player[pnr].action |= key_action;
1335       else
1336         stored_player[pnr].action &= ~key_action;
1337
1338       if (tape.single_step && tape.recording && tape.pausing)
1339       {
1340         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1341         {
1342           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1343
1344           /* if snap key already pressed, don't snap when releasing (below) */
1345           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1346             element_snapped[pnr] = TRUE;
1347
1348           /* if drop key already pressed, don't drop when releasing (below) */
1349           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1350             element_dropped[pnr] = TRUE;
1351         }
1352         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1353         {
1354           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1355               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1356           {
1357
1358             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1359                 getRedDiskReleaseFlag_SP() == 0)
1360               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1361
1362             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1363           }
1364         }
1365         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1366         {
1367           if (key_action & KEY_BUTTON_SNAP)
1368           {
1369             /* if snap key was released without moving (see above), snap now */
1370             if (!element_snapped[pnr])
1371             {
1372               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1373
1374               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1375
1376               /* clear delayed snap button on next event */
1377               clear_snap_button[pnr] = TRUE;
1378             }
1379
1380             element_snapped[pnr] = FALSE;
1381           }
1382
1383           if (key_action & KEY_BUTTON_DROP &&
1384               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1385           {
1386             /* if drop key was released without moving (see above), drop now */
1387             if (!element_dropped[pnr])
1388             {
1389               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1390
1391               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1392                   getRedDiskReleaseFlag_SP() != 0)
1393                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1394
1395               /* clear delayed drop button on next event */
1396               clear_drop_button[pnr] = TRUE;
1397             }
1398
1399             element_dropped[pnr] = FALSE;
1400           }
1401         }
1402       }
1403       else if (tape.recording && tape.pausing)
1404       {
1405         /* prevent key release events from un-pausing a paused game */
1406         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1407           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1408       }
1409     }
1410   }
1411   else
1412   {
1413     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1414       if (key == key_info[i].key_default)
1415         joy |= key_info[i].action;
1416   }
1417
1418   if (joy)
1419   {
1420     if (key_status == KEY_PRESSED)
1421       key_joystick_mapping |= joy;
1422     else
1423       key_joystick_mapping &= ~joy;
1424
1425     HandleJoystick();
1426   }
1427
1428   if (game_status != GAME_MODE_PLAYING)
1429     key_joystick_mapping = 0;
1430
1431   if (key_status == KEY_RELEASED)
1432   {
1433     // reset flag to ignore repeated "key pressed" events after key release
1434     ignore_repeated_key = FALSE;
1435
1436     return;
1437   }
1438
1439   if ((key == KSYM_F11 ||
1440        ((key == KSYM_Return ||
1441          key == KSYM_KP_Enter) && (GetKeyModState() & KMOD_Alt))) &&
1442       video.fullscreen_available &&
1443       !ignore_repeated_key)
1444   {
1445     setup.fullscreen = !setup.fullscreen;
1446
1447     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1448
1449     if (game_status == GAME_MODE_SETUP)
1450       RedrawSetupScreenAfterFullscreenToggle();
1451
1452     // set flag to ignore repeated "key pressed" events
1453     ignore_repeated_key = TRUE;
1454
1455     return;
1456   }
1457
1458   if ((key == KSYM_minus ||
1459        key == KSYM_plus ||
1460        key == KSYM_equal ||     // ("Shift-=" is "+" on US keyboards)
1461        key == KSYM_0) &&
1462       ((GetKeyModState() & KMOD_Control) ||
1463        (GetKeyModState() & KMOD_Alt) ||
1464        (GetKeyModState() & KMOD_Meta)) &&
1465       video.window_scaling_available &&
1466       !video.fullscreen_enabled)
1467   {
1468     if (key == KSYM_0)
1469       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1470     else
1471       setup.window_scaling_percent +=
1472         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1473
1474     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1475       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1476     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1477       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1478
1479     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1480
1481     if (game_status == GAME_MODE_SETUP)
1482       RedrawSetupScreenAfterFullscreenToggle();
1483
1484     return;
1485   }
1486
1487   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1488       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1489   {
1490     GameEnd();
1491
1492     return;
1493   }
1494
1495   if (game_status == GAME_MODE_MAIN &&
1496       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1497   {
1498     StartGameActions(options.network, setup.autorecord, level.random_seed);
1499
1500     return;
1501   }
1502
1503   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1504   {
1505     if (key == setup.shortcut.save_game)
1506       TapeQuickSave();
1507     else if (key == setup.shortcut.load_game)
1508       TapeQuickLoad();
1509     else if (key == setup.shortcut.toggle_pause)
1510       TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
1511
1512     HandleTapeButtonKeys(key);
1513     HandleSoundButtonKeys(key);
1514   }
1515
1516   if (game_status == GAME_MODE_PLAYING && !network_playing)
1517   {
1518     int centered_player_nr_next = -999;
1519
1520     if (key == setup.shortcut.focus_player_all)
1521       centered_player_nr_next = -1;
1522     else
1523       for (i = 0; i < MAX_PLAYERS; i++)
1524         if (key == setup.shortcut.focus_player[i])
1525           centered_player_nr_next = i;
1526
1527     if (centered_player_nr_next != -999)
1528     {
1529       game.centered_player_nr_next = centered_player_nr_next;
1530       game.set_centered_player = TRUE;
1531
1532       if (tape.recording)
1533       {
1534         tape.centered_player_nr_next = game.centered_player_nr_next;
1535         tape.set_centered_player = TRUE;
1536       }
1537     }
1538   }
1539
1540   HandleKeysSpecial(key);
1541
1542   if (HandleGadgetsKeyInput(key))
1543   {
1544     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1545       key = KSYM_UNDEFINED;
1546   }
1547
1548   switch (game_status)
1549   {
1550     case GAME_MODE_PSEUDO_TYPENAME:
1551       HandleTypeName(0, key);
1552       break;
1553
1554     case GAME_MODE_TITLE:
1555     case GAME_MODE_MAIN:
1556     case GAME_MODE_LEVELS:
1557     case GAME_MODE_LEVELNR:
1558     case GAME_MODE_SETUP:
1559     case GAME_MODE_INFO:
1560     case GAME_MODE_SCORES:
1561       switch (key)
1562       {
1563         case KSYM_space:
1564         case KSYM_Return:
1565           if (game_status == GAME_MODE_TITLE)
1566             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1567           else if (game_status == GAME_MODE_MAIN)
1568             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1569           else if (game_status == GAME_MODE_LEVELS)
1570             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1571           else if (game_status == GAME_MODE_LEVELNR)
1572             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1573           else if (game_status == GAME_MODE_SETUP)
1574             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1575           else if (game_status == GAME_MODE_INFO)
1576             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1577           else if (game_status == GAME_MODE_SCORES)
1578             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1579           break;
1580
1581         case KSYM_Escape:
1582           if (game_status != GAME_MODE_MAIN)
1583             FadeSkipNextFadeIn();
1584
1585           if (game_status == GAME_MODE_TITLE)
1586             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1587           else if (game_status == GAME_MODE_LEVELS)
1588             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1589           else if (game_status == GAME_MODE_LEVELNR)
1590             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1591           else if (game_status == GAME_MODE_SETUP)
1592             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1593           else if (game_status == GAME_MODE_INFO)
1594             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1595           else if (game_status == GAME_MODE_SCORES)
1596             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1597           break;
1598
1599         case KSYM_Page_Up:
1600           if (game_status == GAME_MODE_LEVELS)
1601             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1602           else if (game_status == GAME_MODE_LEVELNR)
1603             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1604           else if (game_status == GAME_MODE_SETUP)
1605             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1606           else if (game_status == GAME_MODE_INFO)
1607             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1608           else if (game_status == GAME_MODE_SCORES)
1609             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1610           break;
1611
1612         case KSYM_Page_Down:
1613           if (game_status == GAME_MODE_LEVELS)
1614             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1615           else if (game_status == GAME_MODE_LEVELNR)
1616             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1617           else if (game_status == GAME_MODE_SETUP)
1618             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1619           else if (game_status == GAME_MODE_INFO)
1620             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1621           else if (game_status == GAME_MODE_SCORES)
1622             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1623           break;
1624
1625         default:
1626           break;
1627       }
1628       break;
1629
1630     case GAME_MODE_EDITOR:
1631       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1632         HandleLevelEditorKeyInput(key);
1633       break;
1634
1635     case GAME_MODE_PLAYING:
1636     {
1637       switch (key)
1638       {
1639         case KSYM_Escape:
1640           RequestQuitGame(setup.ask_on_escape);
1641           break;
1642
1643         default:
1644           break;
1645       }
1646       break;
1647     }
1648
1649     default:
1650       if (key == KSYM_Escape)
1651       {
1652         SetGameStatus(GAME_MODE_MAIN);
1653
1654         DrawMainMenu();
1655
1656         return;
1657       }
1658   }
1659
1660   HandleKeysDebug(key);
1661 }
1662
1663 void HandleNoEvent()
1664 {
1665   // if (button_status && game_status != GAME_MODE_PLAYING)
1666   if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1667   {
1668     HandleButton(0, 0, button_status, -button_status);
1669   }
1670   else
1671   {
1672     HandleJoystick();
1673   }
1674
1675 #if defined(NETWORK_AVALIABLE)
1676   if (options.network)
1677     HandleNetworking();
1678 #endif
1679
1680   switch (game_status)
1681   {
1682     case GAME_MODE_MAIN:
1683       DrawPreviewLevelAnimation();
1684       break;
1685
1686     case GAME_MODE_EDITOR:
1687       HandleLevelEditorIdle();
1688       break;
1689
1690     default:
1691       break;
1692   }
1693 }
1694
1695 static int HandleJoystickForAllPlayers()
1696 {
1697   int i;
1698   int result = 0;
1699
1700   for (i = 0; i < MAX_PLAYERS; i++)
1701   {
1702     byte joy_action = 0;
1703
1704     /*
1705     if (!setup.input[i].use_joystick)
1706       continue;
1707       */
1708
1709     joy_action = Joystick(i);
1710     result |= joy_action;
1711
1712     if (!setup.input[i].use_joystick)
1713       continue;
1714
1715     stored_player[i].action = joy_action;
1716   }
1717
1718   return result;
1719 }
1720
1721 void HandleJoystick()
1722 {
1723   int joystick  = HandleJoystickForAllPlayers();
1724   int keyboard  = key_joystick_mapping;
1725   int joy       = (joystick | keyboard);
1726   int left      = joy & JOY_LEFT;
1727   int right     = joy & JOY_RIGHT;
1728   int up        = joy & JOY_UP;
1729   int down      = joy & JOY_DOWN;
1730   int button    = joy & JOY_BUTTON;
1731   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1732   int dx        = (left ? -1    : right ? 1     : 0);
1733   int dy        = (up   ? -1    : down  ? 1     : 0);
1734
1735   switch (game_status)
1736   {
1737     case GAME_MODE_TITLE:
1738     case GAME_MODE_MAIN:
1739     case GAME_MODE_LEVELS:
1740     case GAME_MODE_LEVELNR:
1741     case GAME_MODE_SETUP:
1742     case GAME_MODE_INFO:
1743     {
1744       static unsigned int joystickmove_delay = 0;
1745
1746       if (joystick && !button &&
1747           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1748         newbutton = dx = dy = 0;
1749
1750       if (game_status == GAME_MODE_TITLE)
1751         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1752       else if (game_status == GAME_MODE_MAIN)
1753         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1754       else if (game_status == GAME_MODE_LEVELS)
1755         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1756       else if (game_status == GAME_MODE_LEVELNR)
1757         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1758       else if (game_status == GAME_MODE_SETUP)
1759         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1760       else if (game_status == GAME_MODE_INFO)
1761         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1762       break;
1763     }
1764
1765     case GAME_MODE_SCORES:
1766       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1767       break;
1768
1769     case GAME_MODE_PLAYING:
1770       if (tape.playing || keyboard)
1771         newbutton = ((joy & JOY_BUTTON) != 0);
1772
1773       if (newbutton && AllPlayersGone)
1774       {
1775         GameEnd();
1776
1777         return;
1778       }
1779
1780       break;
1781
1782     default:
1783       break;
1784   }
1785 }