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