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