removed artifact from removed X11 support
[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 #if 0
1555         case KSYM_s:
1556           if (!global.fps_slowdown)
1557           {
1558             global.fps_slowdown = TRUE;
1559             global.fps_slowdown_factor = 2;
1560             printf("fps slowdown enabled -- display only every 2nd frame\n");
1561           }
1562           else if (global.fps_slowdown_factor == 2)
1563           {
1564             global.fps_slowdown_factor = 4;
1565             printf("fps slowdown enabled -- display only every 4th frame\n");
1566           }
1567           else
1568           {
1569             global.fps_slowdown = FALSE;
1570             global.fps_slowdown_factor = 1;
1571             printf("fps slowdown disabled\n");
1572           }
1573           break;
1574 #endif
1575
1576         case KSYM_v:
1577           printf("::: currently using game engine version %d\n",
1578                  game.engine_version);
1579           break;
1580 #endif
1581
1582         default:
1583           break;
1584       }
1585       break;
1586     }
1587
1588     default:
1589       if (key == KSYM_Escape)
1590       {
1591         game_status = GAME_MODE_MAIN;
1592         DrawMainMenu();
1593
1594         return;
1595       }
1596   }
1597 }
1598
1599 void HandleNoEvent()
1600 {
1601   // if (button_status && game_status != GAME_MODE_PLAYING)
1602   if (button_status && (game_status != GAME_MODE_PLAYING || tape.pausing))
1603   {
1604     HandleButton(0, 0, -button_status, button_status);
1605   }
1606   else
1607   {
1608     HandleJoystick();
1609   }
1610
1611 #if defined(NETWORK_AVALIABLE)
1612   if (options.network)
1613     HandleNetworking();
1614 #endif
1615
1616   switch (game_status)
1617   {
1618     case GAME_MODE_MAIN:
1619       DrawPreviewLevelAnimation();
1620       DoAnimation();
1621       break;
1622
1623     case GAME_MODE_LEVELS:
1624     case GAME_MODE_LEVELNR:
1625     case GAME_MODE_SETUP:
1626     case GAME_MODE_INFO:
1627     case GAME_MODE_SCORES:
1628       DoAnimation();
1629       break;
1630
1631     case GAME_MODE_EDITOR:
1632       HandleLevelEditorIdle();
1633       break;
1634
1635     default:
1636       break;
1637   }
1638 }
1639
1640 static int HandleJoystickForAllPlayers()
1641 {
1642   int i;
1643   int result = 0;
1644
1645   for (i = 0; i < MAX_PLAYERS; i++)
1646   {
1647     byte joy_action = 0;
1648
1649     /*
1650     if (!setup.input[i].use_joystick)
1651       continue;
1652       */
1653
1654     joy_action = Joystick(i);
1655     result |= joy_action;
1656
1657     if (!setup.input[i].use_joystick)
1658       continue;
1659
1660     stored_player[i].action = joy_action;
1661   }
1662
1663   return result;
1664 }
1665
1666 void HandleJoystick()
1667 {
1668   int joystick  = HandleJoystickForAllPlayers();
1669   int keyboard  = key_joystick_mapping;
1670   int joy       = (joystick | keyboard);
1671   int left      = joy & JOY_LEFT;
1672   int right     = joy & JOY_RIGHT;
1673   int up        = joy & JOY_UP;
1674   int down      = joy & JOY_DOWN;
1675   int button    = joy & JOY_BUTTON;
1676   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1677   int dx        = (left ? -1    : right ? 1     : 0);
1678   int dy        = (up   ? -1    : down  ? 1     : 0);
1679
1680   switch (game_status)
1681   {
1682     case GAME_MODE_TITLE:
1683     case GAME_MODE_MAIN:
1684     case GAME_MODE_LEVELS:
1685     case GAME_MODE_LEVELNR:
1686     case GAME_MODE_SETUP:
1687     case GAME_MODE_INFO:
1688     {
1689       static unsigned int joystickmove_delay = 0;
1690
1691       if (joystick && !button &&
1692           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1693         newbutton = dx = dy = 0;
1694
1695       if (game_status == GAME_MODE_TITLE)
1696         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1697       else if (game_status == GAME_MODE_MAIN)
1698         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1699       else if (game_status == GAME_MODE_LEVELS)
1700         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1701       else if (game_status == GAME_MODE_LEVELNR)
1702         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1703       else if (game_status == GAME_MODE_SETUP)
1704         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1705       else if (game_status == GAME_MODE_INFO)
1706         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1707       break;
1708     }
1709
1710     case GAME_MODE_SCORES:
1711       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1712       break;
1713
1714     case GAME_MODE_PLAYING:
1715       if (tape.playing || keyboard)
1716         newbutton = ((joy & JOY_BUTTON) != 0);
1717
1718       if (newbutton && AllPlayersGone)
1719       {
1720         GameEnd();
1721
1722         return;
1723       }
1724
1725       break;
1726
1727     default:
1728       break;
1729   }
1730 }