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