rnd-20140514-1-src
[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 #if 0
469       printf("::: RESIZED from %d, %d to %d, %d\n",
470              video.window_width, video.window_height,
471              new_window_width, new_window_height);
472 #endif
473
474       setup.window_scaling_percent = video.window_scaling_percent =
475         MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
476             MAX_WINDOW_SCALING_PERCENT);
477
478       video.window_width  = new_window_width;
479       video.window_height = new_window_height;
480
481 #if 0
482       printf("::: setup.window_scaling_percent set to %d\n",
483              setup.window_scaling_percent);
484 #endif
485
486       if (game_status == GAME_MODE_SETUP)
487         RedrawSetupScreenAfterFullscreenToggle();
488
489       SetWindowTitle();
490     }
491 #else
492     // prevent slightly wrong scaling factor due to rounding differences
493     float scaling_factor = (float)setup.window_scaling_percent / 100;
494     int old_xsize = (int)(scaling_factor * video.width);
495     int old_ysize = (int)(scaling_factor * video.height);
496     int new_xsize = event->data1;
497     int new_ysize = event->data2;
498
499     // window size is unchanged when going from fullscreen to window mode,
500     // but reverse calculation of scaling factor might result in a scaling
501     // factor that is slightly different due to rounding differences;
502     // therefore compare old/new window size and not old/new scaling factor
503     if (old_xsize != new_xsize ||
504         old_ysize != new_ysize)
505     {
506       int new_xpercent = (100 * new_xsize / video.width);
507       int new_ypercent = (100 * new_ysize / video.height);
508
509       setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
510
511       if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
512         setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
513       else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
514         setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
515
516       printf("::: setup.window_scaling_percent set to %d\n",
517              setup.window_scaling_percent);
518     }
519 #endif
520   }
521 }
522
523 #if 1
524
525 #define NUM_TOUCH_FINGERS               3
526
527 static struct
528 {
529   boolean touched;
530   SDL_FingerID finger_id;
531   int counter;
532   Key key;
533 } touch_info[NUM_TOUCH_FINGERS];
534
535 void HandleFingerEvent(FingerEvent *event)
536 {
537   static Key motion_key_x = KSYM_UNDEFINED;
538   static Key motion_key_y = KSYM_UNDEFINED;
539   static Key button_key = KSYM_UNDEFINED;
540   static float motion_x1, motion_y1;
541   static float button_x1, button_y1;
542   static SDL_FingerID motion_id = -1;
543   static SDL_FingerID button_id = -1;
544   int move_trigger_distance_percent = 2;   // percent of touchpad width/height
545   int drop_trigger_distance_percent = 5;   // percent of touchpad width/height
546   float move_trigger_distance = (float)move_trigger_distance_percent / 100;
547   float drop_trigger_distance = (float)drop_trigger_distance_percent / 100;
548   float event_x = event->x;
549   float event_y = event->y;
550
551 #if 1
552 #if DEBUG_EVENTS
553   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
554         event->type == EVENT_FINGERPRESS ? "pressed" :
555         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
556         event->touchId,
557         event->fingerId,
558         event->x, event->y,
559         event->dx, event->dy,
560         event->pressure);
561 #endif
562 #endif
563
564   if (game_status != GAME_MODE_PLAYING)
565     return;
566
567 #if 1
568   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
569 #else
570   boolean use_virtual_button_control = FALSE;
571
572   if (use_virtual_button_control)
573 #endif
574   {
575     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
576                       KEY_PRESSED);
577 #if 1
578     Key key = (event->x < 1.0 / 3.0 ?
579                (event->y < 1.0 / 2.0 ? setup.input[0].key.snap :
580                 setup.input[0].key.drop) :
581                event->x > 2.0 / 3.0 ?
582                (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 < 5.0 / 6.0 ? setup.input[0].key.left :
585                 setup.input[0].key.right) :
586                KSYM_UNDEFINED);
587 #if 0
588     char *key_name = (key == setup.input[0].key.snap  ? "SNAP" :
589                       key == setup.input[0].key.drop  ? "DROP" :
590                       key == setup.input[0].key.up    ? "UP" :
591                       key == setup.input[0].key.down  ? "DOWN" :
592                       key == setup.input[0].key.left  ? "LEFT" :
593                       key == setup.input[0].key.right ? "RIGHT" : "(unknown)");
594 #endif
595     char *key_status_name = (key_status == KEY_RELEASED ? "KEY_RELEASED" :
596                              "KEY_PRESSED");
597 #else
598     Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
599                event->y > 2.0 / 3.0 ? setup.input[0].key.down :
600                event->x < 1.0 / 3.0 ? setup.input[0].key.left :
601                event->x > 2.0 / 3.0 ? setup.input[0].key.right :
602                setup.input[0].key.snap);
603 #endif
604     int i;
605
606     Error(ERR_DEBUG, "::: key '%s' was '%s' [fingerId: %lld]",
607           getKeyNameFromKey(key), key_status_name, event->fingerId);
608
609     // check if we already know this touch event's finger id
610     for (i = 0; i < NUM_TOUCH_FINGERS; i++)
611     {
612       if (touch_info[i].touched &&
613           touch_info[i].finger_id == event->fingerId)
614       {
615         // Error(ERR_DEBUG, "MARK 1: %d", i);
616
617         break;
618       }
619     }
620
621     if (i >= NUM_TOUCH_FINGERS)
622     {
623       if (key_status == KEY_PRESSED)
624       {
625         int oldest_pos = 0, oldest_counter = touch_info[0].counter;
626
627         // unknown finger id -- get new, empty slot, if available
628         for (i = 0; i < NUM_TOUCH_FINGERS; i++)
629         {
630           if (touch_info[i].counter < oldest_counter)
631           {
632             oldest_pos = i;
633             oldest_counter = touch_info[i].counter;
634
635             // Error(ERR_DEBUG, "MARK 2: %d", i);
636           }
637
638           if (!touch_info[i].touched)
639           {
640             // Error(ERR_DEBUG, "MARK 3: %d", i);
641
642             break;
643           }
644         }
645
646         if (i >= NUM_TOUCH_FINGERS)
647         {
648           // all slots allocated -- use oldest slot
649           i = oldest_pos;
650
651           // Error(ERR_DEBUG, "MARK 4: %d", i);
652         }
653       }
654       else
655       {
656         // release of previously unknown key (should not happen)
657
658         if (key != KSYM_UNDEFINED)
659         {
660           HandleKey(key, KEY_RELEASED);
661
662           Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [1]",
663                 getKeyNameFromKey(key), "KEY_RELEASED", i);
664         }
665       }
666     }
667
668     if (i < NUM_TOUCH_FINGERS)
669     {
670       if (key_status == KEY_PRESSED)
671       {
672         if (touch_info[i].key != key)
673         {
674           if (touch_info[i].key != KSYM_UNDEFINED)
675           {
676             HandleKey(touch_info[i].key, KEY_RELEASED);
677
678             Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [2]",
679                   getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
680           }
681
682           if (key != KSYM_UNDEFINED)
683           {
684             HandleKey(key, KEY_PRESSED);
685
686             Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [3]",
687                   getKeyNameFromKey(key), "KEY_PRESSED", i);
688           }
689         }
690
691         touch_info[i].touched = TRUE;
692         touch_info[i].finger_id = event->fingerId;
693         touch_info[i].counter = Counter();
694         touch_info[i].key = key;
695       }
696       else
697       {
698         if (touch_info[i].key != KSYM_UNDEFINED)
699         {
700           HandleKey(touch_info[i].key, KEY_RELEASED);
701
702           Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d] [4]",
703                 getKeyNameFromKey(touch_info[i].key), "KEY_RELEASED", i);
704         }
705
706         touch_info[i].touched = FALSE;
707         touch_info[i].finger_id = 0;
708         touch_info[i].counter = 0;
709         touch_info[i].key = 0;
710       }
711     }
712
713 #if 0
714 #if 1
715     Error(ERR_DEBUG, "=> key == '%s', key_status == '%s' [slot %d]",
716           key_name, key_status_name, i);
717 #else
718     Error(ERR_DEBUG, "=> key == %d, key_status == %d [%d]", key, key_status, i);
719 #endif
720 #endif
721
722     return;
723   }
724
725   // use touch direction control
726
727   if (event->type == EVENT_FINGERPRESS)
728   {
729     if (event_x > 1.0 / 3.0)
730     {
731       // motion area
732
733       motion_id = event->fingerId;
734
735       motion_x1 = event_x;
736       motion_y1 = event_y;
737
738       motion_key_x = KSYM_UNDEFINED;
739       motion_key_y = KSYM_UNDEFINED;
740
741       Error(ERR_DEBUG, "---------- MOVE STARTED (WAIT) ----------");
742     }
743     else
744     {
745       // button area
746
747       button_id = event->fingerId;
748
749       button_x1 = event_x;
750       button_y1 = event_y;
751
752       button_key = setup.input[0].key.snap;
753
754       HandleKey(button_key, KEY_PRESSED);
755
756       Error(ERR_DEBUG, "---------- SNAP STARTED ----------");
757     }
758   }
759   else if (event->type == EVENT_FINGERRELEASE)
760   {
761     if (event->fingerId == motion_id)
762     {
763       motion_id = -1;
764
765       if (motion_key_x != KSYM_UNDEFINED)
766         HandleKey(motion_key_x, KEY_RELEASED);
767       if (motion_key_y != KSYM_UNDEFINED)
768         HandleKey(motion_key_y, KEY_RELEASED);
769
770       motion_key_x = KSYM_UNDEFINED;
771       motion_key_y = KSYM_UNDEFINED;
772
773       Error(ERR_DEBUG, "---------- MOVE STOPPED ----------");
774     }
775     else if (event->fingerId == button_id)
776     {
777       button_id = -1;
778
779       if (button_key != KSYM_UNDEFINED)
780         HandleKey(button_key, KEY_RELEASED);
781
782       button_key = KSYM_UNDEFINED;
783
784       Error(ERR_DEBUG, "---------- SNAP STOPPED ----------");
785     }
786   }
787   else if (event->type == EVENT_FINGERMOTION)
788   {
789     if (event->fingerId == motion_id)
790     {
791       float distance_x = ABS(event_x - motion_x1);
792       float distance_y = ABS(event_y - motion_y1);
793       Key new_motion_key_x = (event_x < motion_x1 ? setup.input[0].key.left :
794                               event_x > motion_x1 ? setup.input[0].key.right :
795                               KSYM_UNDEFINED);
796       Key new_motion_key_y = (event_y < motion_y1 ? setup.input[0].key.up :
797                               event_y > motion_y1 ? setup.input[0].key.down :
798                               KSYM_UNDEFINED);
799
800       if (distance_x < move_trigger_distance / 2 ||
801           distance_x < distance_y)
802         new_motion_key_x = KSYM_UNDEFINED;
803
804       if (distance_y < move_trigger_distance / 2 ||
805           distance_y < distance_x)
806         new_motion_key_y = KSYM_UNDEFINED;
807
808       if (distance_x > move_trigger_distance ||
809           distance_y > move_trigger_distance)
810       {
811         if (new_motion_key_x != motion_key_x)
812         {
813           if (motion_key_x != KSYM_UNDEFINED)
814             HandleKey(motion_key_x, KEY_RELEASED);
815           if (new_motion_key_x != KSYM_UNDEFINED)
816             HandleKey(new_motion_key_x, KEY_PRESSED);
817         }
818
819         if (new_motion_key_y != motion_key_y)
820         {
821           if (motion_key_y != KSYM_UNDEFINED)
822             HandleKey(motion_key_y, KEY_RELEASED);
823           if (new_motion_key_y != KSYM_UNDEFINED)
824             HandleKey(new_motion_key_y, KEY_PRESSED);
825         }
826
827         motion_x1 = event_x;
828         motion_y1 = event_y;
829
830         motion_key_x = new_motion_key_x;
831         motion_key_y = new_motion_key_y;
832
833         Error(ERR_DEBUG, "---------- MOVE STARTED (MOVE) ----------");
834       }
835     }
836     else if (event->fingerId == button_id)
837     {
838       float distance_x = ABS(event_x - button_x1);
839       float distance_y = ABS(event_y - button_y1);
840
841       if (distance_x < drop_trigger_distance / 2 &&
842           distance_y > drop_trigger_distance)
843       {
844         if (button_key == setup.input[0].key.snap)
845           HandleKey(button_key, KEY_RELEASED);
846
847         button_x1 = event_x;
848         button_y1 = event_y;
849
850         button_key = setup.input[0].key.drop;
851
852         HandleKey(button_key, KEY_PRESSED);
853
854         Error(ERR_DEBUG, "---------- DROP STARTED ----------");
855       }
856     }
857   }
858 }
859
860 #else
861
862 void HandleFingerEvent(FingerEvent *event)
863 {
864 #if 0
865   static int num_events = 0;
866   int max_events = 10;
867 #endif
868
869 #if 0
870 #if DEBUG_EVENTS
871   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
872         event->type == EVENT_FINGERPRESS ? "pressed" :
873         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
874         event->touchId,
875         event->fingerId,
876         event->x, event->y,
877         event->dx, event->dy,
878         event->pressure);
879 #endif
880 #endif
881
882 #if 0
883   int x = (int)(event->x * video.width);
884   int y = (int)(event->y * video.height);
885   int button = MB_LEFTBUTTON;
886
887   Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
888 #endif
889
890 #if 0
891   if (++num_events >= max_events)
892     CloseAllAndExit(0);
893 #endif
894
895 #if 1
896 #if 0
897   if (event->type == EVENT_FINGERPRESS ||
898       event->type == EVENT_FINGERMOTION)
899     button_status = button;
900   else
901     button_status = MB_RELEASED;
902
903   int max_x = SX + SXSIZE;
904   int max_y = SY + SYSIZE;
905 #endif
906
907 #if 1
908   if (game_status == GAME_MODE_PLAYING)
909 #else
910   if (game_status == GAME_MODE_PLAYING &&
911       x < max_x)
912 #endif
913   {
914     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
915                       KEY_PRESSED);
916 #if 1
917     Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
918                event->y > 2.0 / 3.0 ? setup.input[0].key.down :
919                event->x < 1.0 / 3.0 ? setup.input[0].key.left :
920                event->x > 2.0 / 3.0 ? setup.input[0].key.right :
921                setup.input[0].key.drop);
922 #else
923     Key key = (y <     max_y / 3 ? setup.input[0].key.up :
924                y > 2 * max_y / 3 ? setup.input[0].key.down :
925                x <     max_x / 3 ? setup.input[0].key.left :
926                x > 2 * max_x / 3 ? setup.input[0].key.right :
927                setup.input[0].key.drop);
928 #endif
929
930     Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
931
932     HandleKey(key, key_status);
933   }
934   else
935   {
936 #if 0
937     Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
938           button_status, button);
939
940     HandleButton(x, y, button_status, button);
941 #endif
942   }
943 #endif
944 }
945
946 #endif
947
948 static boolean checkTextInputKeyModState()
949 {
950   // when playing, only handle raw key events and ignore text input
951   if (game_status == GAME_MODE_PLAYING)
952     return FALSE;
953
954   return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
955 }
956
957 void HandleTextEvent(TextEvent *event)
958 {
959   char *text = event->text;
960   Key key = getKeyFromKeyName(text);
961
962 #if DEBUG_EVENTS
963   Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
964         text,
965         strlen(text),
966         text[0], (int)(text[0]),
967         key,
968         getKeyNameFromKey(key),
969         GetKeyModState());
970 #endif
971
972   // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
973   /*
974   if (game_status != GAME_MODE_PLAYING &&
975       (GetKeyModState() & KMOD_TextInput) != KMOD_None)
976   */
977   if (checkTextInputKeyModState())
978   {
979     HandleKey(key, KEY_PRESSED);
980     HandleKey(key, KEY_RELEASED);
981   }
982 }
983 #endif
984
985 void HandleKeyEvent(KeyEvent *event)
986 {
987   int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
988   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
989   Key key = GetEventKey(event, with_modifiers);
990   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
991
992 #if DEBUG_EVENTS
993   Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
994         event->type == EVENT_KEYPRESS ? "pressed" : "released",
995         event->keysym.scancode,
996         event->keysym.sym,
997         keymod,
998         GetKeyModState(),
999         key,
1000         getKeyNameFromKey(key));
1001 #endif
1002
1003 #if 0
1004   if (key == KSYM_Menu)
1005     Error(ERR_DEBUG, "menu key pressed");
1006   else if (key == KSYM_Back)
1007     Error(ERR_DEBUG, "back key pressed");
1008 #endif
1009
1010 #if defined(PLATFORM_ANDROID)
1011   // always map the "back" button to the "escape" key on Android devices
1012   if (key == KSYM_Back)
1013     key = KSYM_Escape;
1014 #endif
1015
1016   HandleKeyModState(keymod, key_status);
1017
1018 #if defined(TARGET_SDL2)
1019
1020   // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
1021   /*
1022   if (game_status == GAME_MODE_PLAYING ||
1023       (GetKeyModState() & KMOD_TextInput) == KMOD_None)
1024   */
1025   if (!checkTextInputKeyModState())
1026     HandleKey(key, key_status);
1027 #else
1028   HandleKey(key, key_status);
1029 #endif
1030 }
1031
1032 void HandleFocusEvent(FocusChangeEvent *event)
1033 {
1034   static int old_joystick_status = -1;
1035
1036   if (event->type == EVENT_FOCUSOUT)
1037   {
1038     KeyboardAutoRepeatOn();
1039     old_joystick_status = joystick.status;
1040     joystick.status = JOYSTICK_NOT_AVAILABLE;
1041
1042     ClearPlayerAction();
1043   }
1044   else if (event->type == EVENT_FOCUSIN)
1045   {
1046     /* When there are two Rocks'n'Diamonds windows which overlap and
1047        the player moves the pointer from one game window to the other,
1048        a 'FocusOut' event is generated for the window the pointer is
1049        leaving and a 'FocusIn' event is generated for the window the
1050        pointer is entering. In some cases, it can happen that the
1051        'FocusIn' event is handled by the one game process before the
1052        'FocusOut' event by the other game process. In this case the
1053        X11 environment would end up with activated keyboard auto repeat,
1054        because unfortunately this is a global setting and not (which
1055        would be far better) set for each X11 window individually.
1056        The effect would be keyboard auto repeat while playing the game
1057        (game_status == GAME_MODE_PLAYING), which is not desired.
1058        To avoid this special case, we just wait 1/10 second before
1059        processing the 'FocusIn' event.
1060     */
1061
1062     if (game_status == GAME_MODE_PLAYING)
1063     {
1064       Delay(100);
1065       KeyboardAutoRepeatOffUnlessAutoplay();
1066     }
1067
1068     if (old_joystick_status != -1)
1069       joystick.status = old_joystick_status;
1070   }
1071 }
1072
1073 void HandleClientMessageEvent(ClientMessageEvent *event)
1074 {
1075   if (CheckCloseWindowEvent(event))
1076     CloseAllAndExit(0);
1077 }
1078
1079 void HandleWindowManagerEvent(Event *event)
1080 {
1081 #if defined(TARGET_SDL)
1082   SDLHandleWindowManagerEvent(event);
1083 #endif
1084 }
1085
1086 void HandleButton(int mx, int my, int button, int button_nr)
1087 {
1088   static int old_mx = 0, old_my = 0;
1089
1090   if (button < 0)
1091   {
1092     mx = old_mx;
1093     my = old_my;
1094     button = -button;
1095   }
1096   else
1097   {
1098     old_mx = mx;
1099     old_my = my;
1100   }
1101
1102 #if defined(PLATFORM_ANDROID)
1103   if (game_status != GAME_MODE_PLAYING &&
1104       HandleGadgets(mx, my, button))
1105   {
1106     /* do not handle this button event anymore */
1107     mx = my = -32;      /* force mouse event to be outside screen tiles */
1108   }
1109 #else
1110   if (HandleGadgets(mx, my, button))
1111   {
1112     /* do not handle this button event anymore */
1113     mx = my = -32;      /* force mouse event to be outside screen tiles */
1114   }
1115 #endif
1116
1117   /* do not use scroll wheel button events for anything other than gadgets */
1118   if (IS_WHEEL_BUTTON(button_nr))
1119     return;
1120
1121 #if 0
1122   Error(ERR_DEBUG, "::: game_status == %d", game_status);
1123 #endif
1124
1125   switch (game_status)
1126   {
1127     case GAME_MODE_TITLE:
1128       HandleTitleScreen(mx, my, 0, 0, button);
1129       break;
1130
1131     case GAME_MODE_MAIN:
1132       HandleMainMenu(mx, my, 0, 0, button);
1133       break;
1134
1135     case GAME_MODE_PSEUDO_TYPENAME:
1136       HandleTypeName(0, KSYM_Return);
1137       break;
1138
1139     case GAME_MODE_LEVELS:
1140       HandleChooseLevelSet(mx, my, 0, 0, button);
1141       break;
1142
1143     case GAME_MODE_LEVELNR:
1144       HandleChooseLevelNr(mx, my, 0, 0, button);
1145       break;
1146
1147     case GAME_MODE_SCORES:
1148       HandleHallOfFame(0, 0, 0, 0, button);
1149       break;
1150
1151     case GAME_MODE_EDITOR:
1152       HandleLevelEditorIdle();
1153       break;
1154
1155     case GAME_MODE_INFO:
1156       HandleInfoScreen(mx, my, 0, 0, button);
1157       break;
1158
1159     case GAME_MODE_SETUP:
1160       HandleSetupScreen(mx, my, 0, 0, button);
1161       break;
1162
1163     case GAME_MODE_PLAYING:
1164 #ifdef DEBUG
1165       if (button == MB_PRESSED && !motion_status && IN_GFX_FIELD_PLAY(mx, my))
1166         DumpTile(LEVELX((mx - SX) / TILESIZE_VAR),
1167                  LEVELY((my - SY) / TILESIZE_VAR));
1168         // DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
1169 #endif
1170       break;
1171
1172     default:
1173       break;
1174   }
1175 }
1176
1177 static boolean is_string_suffix(char *string, char *suffix)
1178 {
1179   int string_len = strlen(string);
1180   int suffix_len = strlen(suffix);
1181
1182   if (suffix_len > string_len)
1183     return FALSE;
1184
1185   return (strEqual(&string[string_len - suffix_len], suffix));
1186 }
1187
1188 #define MAX_CHEAT_INPUT_LEN     32
1189
1190 static void HandleKeysSpecial(Key key)
1191 {
1192   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1193   char letter = getCharFromKey(key);
1194   int cheat_input_len = strlen(cheat_input);
1195   int i;
1196
1197   if (letter == 0)
1198     return;
1199
1200   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1201   {
1202     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1203       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1204
1205     cheat_input_len = MAX_CHEAT_INPUT_LEN;
1206   }
1207
1208   cheat_input[cheat_input_len++] = letter;
1209   cheat_input[cheat_input_len] = '\0';
1210
1211 #if DEBUG_EVENTS
1212   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1213 #endif
1214
1215   if (game_status == GAME_MODE_MAIN)
1216   {
1217     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1218         is_string_suffix(cheat_input, ":ist"))
1219     {
1220       InsertSolutionTape();
1221     }
1222     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1223              is_string_suffix(cheat_input, ":rg"))
1224     {
1225       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1226       DrawMainMenu();
1227     }
1228     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1229              is_string_suffix(cheat_input, ":rs"))
1230     {
1231       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1232       DrawMainMenu();
1233     }
1234     else if (is_string_suffix(cheat_input, ":reload-music") ||
1235              is_string_suffix(cheat_input, ":rm"))
1236     {
1237       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1238       DrawMainMenu();
1239     }
1240     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1241              is_string_suffix(cheat_input, ":ra"))
1242     {
1243       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1244                           1 << ARTWORK_TYPE_SOUNDS |
1245                           1 << ARTWORK_TYPE_MUSIC);
1246       DrawMainMenu();
1247     }
1248     else if (is_string_suffix(cheat_input, ":dump-level") ||
1249              is_string_suffix(cheat_input, ":dl"))
1250     {
1251       DumpLevel(&level);
1252     }
1253     else if (is_string_suffix(cheat_input, ":dump-tape") ||
1254              is_string_suffix(cheat_input, ":dt"))
1255     {
1256       DumpTape(&tape);
1257     }
1258     else if (is_string_suffix(cheat_input, ":fix-tape") ||
1259              is_string_suffix(cheat_input, ":ft"))
1260     {
1261       /* fix single-player tapes that contain player input for more than one
1262          player (due to a bug in 3.3.1.2 and earlier versions), which results
1263          in playing levels with more than one player in multi-player mode,
1264          even though the tape was originally recorded in single-player mode */
1265
1266       /* remove player input actions for all players but the first one */
1267       for (i = 1; i < MAX_PLAYERS; i++)
1268         tape.player_participates[i] = FALSE;
1269
1270       tape.changed = TRUE;
1271     }
1272     else if (is_string_suffix(cheat_input, ":save-native-level") ||
1273              is_string_suffix(cheat_input, ":snl"))
1274     {
1275       SaveNativeLevel(&level);
1276     }
1277   }
1278   else if (game_status == GAME_MODE_PLAYING)
1279   {
1280 #ifdef DEBUG
1281     if (is_string_suffix(cheat_input, ".q"))
1282       DEBUG_SetMaximumDynamite();
1283 #endif
1284   }
1285   else if (game_status == GAME_MODE_EDITOR)
1286   {
1287     if (is_string_suffix(cheat_input, ":dump-brush") ||
1288         is_string_suffix(cheat_input, ":DB"))
1289     {
1290       DumpBrush();
1291     }
1292     else if (is_string_suffix(cheat_input, ":DDB"))
1293     {
1294       DumpBrush_Small();
1295     }
1296   }
1297 }
1298
1299 void HandleKey(Key key, int key_status)
1300 {
1301   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1302   static struct SetupKeyboardInfo ski;
1303   static struct SetupShortcutInfo ssi;
1304   static struct
1305   {
1306     Key *key_custom;
1307     Key *key_snap;
1308     Key key_default;
1309     byte action;
1310   } key_info[] =
1311   {
1312     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
1313     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
1314     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
1315     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
1316     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
1317     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
1318   };
1319   int joy = 0;
1320   int i;
1321
1322   if (game_status == GAME_MODE_PLAYING)
1323   {
1324     /* only needed for single-step tape recording mode */
1325     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1326     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1327     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1328     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1329     int pnr;
1330
1331     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1332     {
1333       byte key_action = 0;
1334
1335       if (setup.input[pnr].use_joystick)
1336         continue;
1337
1338       ski = setup.input[pnr].key;
1339
1340       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1341         if (key == *key_info[i].key_custom)
1342           key_action |= key_info[i].action;
1343
1344       /* use combined snap+direction keys for the first player only */
1345       if (pnr == 0)
1346       {
1347         ssi = setup.shortcut;
1348
1349         for (i = 0; i < NUM_DIRECTIONS; i++)
1350           if (key == *key_info[i].key_snap)
1351             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1352       }
1353
1354       /* clear delayed snap and drop actions in single step mode (see below) */
1355       if (tape.single_step)
1356       {
1357         if (clear_snap_button[pnr])
1358         {
1359           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1360           clear_snap_button[pnr] = FALSE;
1361         }
1362
1363         if (clear_drop_button[pnr])
1364         {
1365           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1366           clear_drop_button[pnr] = FALSE;
1367         }
1368       }
1369
1370       if (key_status == KEY_PRESSED)
1371         stored_player[pnr].action |= key_action;
1372       else
1373         stored_player[pnr].action &= ~key_action;
1374
1375       if (tape.single_step && tape.recording && tape.pausing)
1376       {
1377         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1378         {
1379           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1380
1381           /* if snap key already pressed, don't snap when releasing (below) */
1382           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1383             element_snapped[pnr] = TRUE;
1384
1385           /* if drop key already pressed, don't drop when releasing (below) */
1386           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1387             element_dropped[pnr] = TRUE;
1388         }
1389 #if 1
1390         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1391         {
1392           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1393               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1394           {
1395 #if 0
1396             printf("::: drop key pressed\n");
1397 #endif
1398
1399             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1400                 getRedDiskReleaseFlag_SP() == 0)
1401               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1402
1403             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1404           }
1405         }
1406 #endif
1407         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1408         {
1409           if (key_action & KEY_BUTTON_SNAP)
1410           {
1411             /* if snap key was released without moving (see above), snap now */
1412             if (!element_snapped[pnr])
1413             {
1414               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1415
1416               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1417
1418               /* clear delayed snap button on next event */
1419               clear_snap_button[pnr] = TRUE;
1420             }
1421
1422             element_snapped[pnr] = FALSE;
1423           }
1424
1425 #if 1
1426           if (key_action & KEY_BUTTON_DROP &&
1427               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1428           {
1429             /* if drop key was released without moving (see above), drop now */
1430             if (!element_dropped[pnr])
1431             {
1432               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1433
1434               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1435                   getRedDiskReleaseFlag_SP() != 0)
1436                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1437
1438               /* clear delayed drop button on next event */
1439               clear_drop_button[pnr] = TRUE;
1440             }
1441
1442             element_dropped[pnr] = FALSE;
1443           }
1444 #endif
1445         }
1446       }
1447       else if (tape.recording && tape.pausing)
1448       {
1449         /* prevent key release events from un-pausing a paused game */
1450         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1451           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1452       }
1453     }
1454   }
1455   else
1456   {
1457     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1458       if (key == key_info[i].key_default)
1459         joy |= key_info[i].action;
1460   }
1461
1462   if (joy)
1463   {
1464     if (key_status == KEY_PRESSED)
1465       key_joystick_mapping |= joy;
1466     else
1467       key_joystick_mapping &= ~joy;
1468
1469     HandleJoystick();
1470   }
1471
1472   if (game_status != GAME_MODE_PLAYING)
1473     key_joystick_mapping = 0;
1474
1475   if (key_status == KEY_RELEASED)
1476     return;
1477
1478   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1479       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1480   {
1481     setup.fullscreen = !setup.fullscreen;
1482
1483 #if 0
1484     printf("::: %d\n", setup.window_scaling_percent);
1485 #endif
1486
1487     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1488
1489     if (game_status == GAME_MODE_SETUP)
1490       RedrawSetupScreenAfterFullscreenToggle();
1491
1492     return;
1493   }
1494
1495   if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1496       (GetKeyModState() & KMOD_Alt) && video.window_scaling_available &&
1497       !video.fullscreen_enabled)
1498   {
1499     if (key == KSYM_0)
1500       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1501     else
1502       setup.window_scaling_percent +=
1503         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1504
1505     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1506       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1507     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1508       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1509
1510     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1511
1512     if (game_status == GAME_MODE_SETUP)
1513       RedrawSetupScreenAfterFullscreenToggle();
1514
1515     return;
1516   }
1517
1518 #if 0
1519   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1520       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1521 #else
1522   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1523       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1524 #endif
1525   {
1526     GameEnd();
1527
1528     return;
1529   }
1530
1531   if (game_status == GAME_MODE_MAIN &&
1532       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1533   {
1534     StartGameActions(options.network, setup.autorecord, level.random_seed);
1535
1536     return;
1537   }
1538
1539   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1540   {
1541     if (key == setup.shortcut.save_game)
1542       TapeQuickSave();
1543     else if (key == setup.shortcut.load_game)
1544       TapeQuickLoad();
1545     else if (key == setup.shortcut.toggle_pause)
1546       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1547
1548     HandleTapeButtonKeys(key);
1549     HandleSoundButtonKeys(key);
1550   }
1551
1552   if (game_status == GAME_MODE_PLAYING && !network_playing)
1553   {
1554     int centered_player_nr_next = -999;
1555
1556     if (key == setup.shortcut.focus_player_all)
1557       centered_player_nr_next = -1;
1558     else
1559       for (i = 0; i < MAX_PLAYERS; i++)
1560         if (key == setup.shortcut.focus_player[i])
1561           centered_player_nr_next = i;
1562
1563     if (centered_player_nr_next != -999)
1564     {
1565       game.centered_player_nr_next = centered_player_nr_next;
1566       game.set_centered_player = TRUE;
1567
1568       if (tape.recording)
1569       {
1570         tape.centered_player_nr_next = game.centered_player_nr_next;
1571         tape.set_centered_player = TRUE;
1572       }
1573     }
1574   }
1575
1576   HandleKeysSpecial(key);
1577
1578   if (HandleGadgetsKeyInput(key))
1579   {
1580     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1581       key = KSYM_UNDEFINED;
1582   }
1583
1584   switch (game_status)
1585   {
1586     case GAME_MODE_PSEUDO_TYPENAME:
1587       HandleTypeName(0, key);
1588       break;
1589
1590     case GAME_MODE_TITLE:
1591     case GAME_MODE_MAIN:
1592     case GAME_MODE_LEVELS:
1593     case GAME_MODE_LEVELNR:
1594     case GAME_MODE_SETUP:
1595     case GAME_MODE_INFO:
1596     case GAME_MODE_SCORES:
1597       switch (key)
1598       {
1599         case KSYM_space:
1600         case KSYM_Return:
1601           if (game_status == GAME_MODE_TITLE)
1602             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1603           else if (game_status == GAME_MODE_MAIN)
1604             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1605           else if (game_status == GAME_MODE_LEVELS)
1606             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1607           else if (game_status == GAME_MODE_LEVELNR)
1608             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1609           else if (game_status == GAME_MODE_SETUP)
1610             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1611           else if (game_status == GAME_MODE_INFO)
1612             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1613           else if (game_status == GAME_MODE_SCORES)
1614             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1615           break;
1616
1617         case KSYM_Escape:
1618           if (game_status != GAME_MODE_MAIN)
1619             FadeSkipNextFadeIn();
1620
1621           if (game_status == GAME_MODE_TITLE)
1622             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1623           else if (game_status == GAME_MODE_LEVELS)
1624             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1625           else if (game_status == GAME_MODE_LEVELNR)
1626             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1627           else if (game_status == GAME_MODE_SETUP)
1628             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1629           else if (game_status == GAME_MODE_INFO)
1630             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1631           else if (game_status == GAME_MODE_SCORES)
1632             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1633           break;
1634
1635         case KSYM_Page_Up:
1636           if (game_status == GAME_MODE_LEVELS)
1637             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1638           else if (game_status == GAME_MODE_LEVELNR)
1639             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1640           else if (game_status == GAME_MODE_SETUP)
1641             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1642           else if (game_status == GAME_MODE_INFO)
1643             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1644           else if (game_status == GAME_MODE_SCORES)
1645             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1646           break;
1647
1648         case KSYM_Page_Down:
1649           if (game_status == GAME_MODE_LEVELS)
1650             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1651           else if (game_status == GAME_MODE_LEVELNR)
1652             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1653           else if (game_status == GAME_MODE_SETUP)
1654             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1655           else if (game_status == GAME_MODE_INFO)
1656             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1657           else if (game_status == GAME_MODE_SCORES)
1658             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1659           break;
1660
1661 #ifdef DEBUG
1662         case KSYM_0:
1663           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1664           break;
1665
1666         case KSYM_b:
1667           setup.sp_show_border_elements = !setup.sp_show_border_elements;
1668           printf("Supaplex border elements %s\n",
1669                  setup.sp_show_border_elements ? "enabled" : "disabled");
1670           break;
1671 #endif
1672
1673         default:
1674           break;
1675       }
1676       break;
1677
1678     case GAME_MODE_EDITOR:
1679       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1680         HandleLevelEditorKeyInput(key);
1681       break;
1682
1683     case GAME_MODE_PLAYING:
1684     {
1685       switch (key)
1686       {
1687         case KSYM_Escape:
1688           RequestQuitGame(setup.ask_on_escape);
1689           break;
1690
1691 #ifdef DEBUG
1692         case KSYM_0:
1693 #if 0
1694         case KSYM_1:
1695         case KSYM_2:
1696         case KSYM_3:
1697         case KSYM_4:
1698         case KSYM_5:
1699         case KSYM_6:
1700         case KSYM_7:
1701         case KSYM_8:
1702         case KSYM_9:
1703 #endif
1704           if (key == KSYM_0)
1705           {
1706             if (GameFrameDelay == 500)
1707               GameFrameDelay = GAME_FRAME_DELAY;
1708             else
1709               GameFrameDelay = 500;
1710           }
1711           else
1712             GameFrameDelay = (key - KSYM_0) * 10;
1713           printf("Game speed == %d%% (%d ms delay between two frames)\n",
1714                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1715           break;
1716
1717         case KSYM_d:
1718           if (options.debug)
1719           {
1720             options.debug = FALSE;
1721             printf("debug mode disabled\n");
1722           }
1723           else
1724           {
1725             options.debug = TRUE;
1726             printf("debug mode enabled\n");
1727           }
1728           break;
1729
1730 #if 0
1731         case KSYM_s:
1732           if (!global.fps_slowdown)
1733           {
1734             global.fps_slowdown = TRUE;
1735             global.fps_slowdown_factor = 2;
1736             printf("fps slowdown enabled -- display only every 2nd frame\n");
1737           }
1738           else if (global.fps_slowdown_factor == 2)
1739           {
1740             global.fps_slowdown_factor = 4;
1741             printf("fps slowdown enabled -- display only every 4th frame\n");
1742           }
1743           else
1744           {
1745             global.fps_slowdown = FALSE;
1746             global.fps_slowdown_factor = 1;
1747             printf("fps slowdown disabled\n");
1748           }
1749           break;
1750 #endif
1751
1752 #if 0
1753         case KSYM_f:
1754           ScrollStepSize = TILEX / 8;
1755           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1756           break;
1757
1758         case KSYM_g:
1759           ScrollStepSize = TILEX / 4;
1760           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1761           break;
1762
1763         case KSYM_h:
1764           ScrollStepSize = TILEX / 2;
1765           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1766           break;
1767
1768         case KSYM_l:
1769           ScrollStepSize = TILEX;
1770           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1771           break;
1772 #endif
1773
1774         case KSYM_v:
1775           printf("::: currently using game engine version %d\n",
1776                  game.engine_version);
1777           break;
1778 #endif
1779
1780         default:
1781           break;
1782       }
1783       break;
1784     }
1785
1786     default:
1787       if (key == KSYM_Escape)
1788       {
1789         game_status = GAME_MODE_MAIN;
1790         DrawMainMenu();
1791
1792         return;
1793       }
1794   }
1795 }
1796
1797 void HandleNoEvent()
1798 {
1799   if (button_status && game_status != GAME_MODE_PLAYING)
1800   {
1801     HandleButton(0, 0, -button_status, button_status);
1802
1803 #if 0
1804     return;
1805 #endif
1806   }
1807   else
1808   {
1809     HandleJoystick();
1810   }
1811
1812 #if defined(NETWORK_AVALIABLE)
1813   if (options.network)
1814     HandleNetworking();
1815 #endif
1816
1817   switch (game_status)
1818   {
1819     case GAME_MODE_MAIN:
1820       DrawPreviewLevelAnimation();
1821       DoAnimation();
1822       break;
1823
1824     case GAME_MODE_LEVELS:
1825     case GAME_MODE_LEVELNR:
1826     case GAME_MODE_SETUP:
1827     case GAME_MODE_INFO:
1828     case GAME_MODE_SCORES:
1829       DoAnimation();
1830       break;
1831
1832     case GAME_MODE_EDITOR:
1833       HandleLevelEditorIdle();
1834       break;
1835
1836     default:
1837       break;
1838   }
1839 }
1840
1841 static int HandleJoystickForAllPlayers()
1842 {
1843   int i;
1844   int result = 0;
1845
1846   for (i = 0; i < MAX_PLAYERS; i++)
1847   {
1848     byte joy_action = 0;
1849
1850     /*
1851     if (!setup.input[i].use_joystick)
1852       continue;
1853       */
1854
1855     joy_action = Joystick(i);
1856     result |= joy_action;
1857
1858     if (!setup.input[i].use_joystick)
1859       continue;
1860
1861     stored_player[i].action = joy_action;
1862   }
1863
1864   return result;
1865 }
1866
1867 void HandleJoystick()
1868 {
1869   int joystick  = HandleJoystickForAllPlayers();
1870   int keyboard  = key_joystick_mapping;
1871   int joy       = (joystick | keyboard);
1872   int left      = joy & JOY_LEFT;
1873   int right     = joy & JOY_RIGHT;
1874   int up        = joy & JOY_UP;
1875   int down      = joy & JOY_DOWN;
1876   int button    = joy & JOY_BUTTON;
1877   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1878   int dx        = (left ? -1    : right ? 1     : 0);
1879   int dy        = (up   ? -1    : down  ? 1     : 0);
1880
1881   switch (game_status)
1882   {
1883     case GAME_MODE_TITLE:
1884     case GAME_MODE_MAIN:
1885     case GAME_MODE_LEVELS:
1886     case GAME_MODE_LEVELNR:
1887     case GAME_MODE_SETUP:
1888     case GAME_MODE_INFO:
1889     {
1890       static unsigned int joystickmove_delay = 0;
1891
1892       if (joystick && !button &&
1893           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1894         newbutton = dx = dy = 0;
1895
1896       if (game_status == GAME_MODE_TITLE)
1897         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1898       else if (game_status == GAME_MODE_MAIN)
1899         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1900       else if (game_status == GAME_MODE_LEVELS)
1901         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1902       else if (game_status == GAME_MODE_LEVELNR)
1903         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1904       else if (game_status == GAME_MODE_SETUP)
1905         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1906       else if (game_status == GAME_MODE_INFO)
1907         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1908       break;
1909     }
1910
1911     case GAME_MODE_SCORES:
1912       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1913       break;
1914
1915 #if 0
1916     case GAME_MODE_EDITOR:
1917       HandleLevelEditorIdle();
1918       break;
1919 #endif
1920
1921     case GAME_MODE_PLAYING:
1922       if (tape.playing || keyboard)
1923         newbutton = ((joy & JOY_BUTTON) != 0);
1924
1925 #if 0
1926       if (newbutton && local_player->LevelSolved_GameEnd)
1927 #else
1928       if (newbutton && AllPlayersGone)
1929 #endif
1930       {
1931         GameEnd();
1932
1933         return;
1934       }
1935
1936       break;
1937
1938     default:
1939       break;
1940   }
1941 }