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