rnd-20140418-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) / TILEX), LEVELY((my - SY) / TILEY));
1167 #endif
1168       break;
1169
1170     default:
1171       break;
1172   }
1173 }
1174
1175 static boolean is_string_suffix(char *string, char *suffix)
1176 {
1177   int string_len = strlen(string);
1178   int suffix_len = strlen(suffix);
1179
1180   if (suffix_len > string_len)
1181     return FALSE;
1182
1183   return (strEqual(&string[string_len - suffix_len], suffix));
1184 }
1185
1186 #define MAX_CHEAT_INPUT_LEN     32
1187
1188 static void HandleKeysSpecial(Key key)
1189 {
1190   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
1191   char letter = getCharFromKey(key);
1192   int cheat_input_len = strlen(cheat_input);
1193   int i;
1194
1195   if (letter == 0)
1196     return;
1197
1198   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
1199   {
1200     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
1201       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
1202
1203     cheat_input_len = MAX_CHEAT_INPUT_LEN;
1204   }
1205
1206   cheat_input[cheat_input_len++] = letter;
1207   cheat_input[cheat_input_len] = '\0';
1208
1209 #if DEBUG_EVENTS
1210   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
1211 #endif
1212
1213   if (game_status == GAME_MODE_MAIN)
1214   {
1215     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
1216         is_string_suffix(cheat_input, ":ist"))
1217     {
1218       InsertSolutionTape();
1219     }
1220     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
1221              is_string_suffix(cheat_input, ":rg"))
1222     {
1223       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
1224       DrawMainMenu();
1225     }
1226     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
1227              is_string_suffix(cheat_input, ":rs"))
1228     {
1229       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
1230       DrawMainMenu();
1231     }
1232     else if (is_string_suffix(cheat_input, ":reload-music") ||
1233              is_string_suffix(cheat_input, ":rm"))
1234     {
1235       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
1236       DrawMainMenu();
1237     }
1238     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
1239              is_string_suffix(cheat_input, ":ra"))
1240     {
1241       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
1242                           1 << ARTWORK_TYPE_SOUNDS |
1243                           1 << ARTWORK_TYPE_MUSIC);
1244       DrawMainMenu();
1245     }
1246     else if (is_string_suffix(cheat_input, ":dump-level") ||
1247              is_string_suffix(cheat_input, ":dl"))
1248     {
1249       DumpLevel(&level);
1250     }
1251     else if (is_string_suffix(cheat_input, ":dump-tape") ||
1252              is_string_suffix(cheat_input, ":dt"))
1253     {
1254       DumpTape(&tape);
1255     }
1256     else if (is_string_suffix(cheat_input, ":fix-tape") ||
1257              is_string_suffix(cheat_input, ":ft"))
1258     {
1259       /* fix single-player tapes that contain player input for more than one
1260          player (due to a bug in 3.3.1.2 and earlier versions), which results
1261          in playing levels with more than one player in multi-player mode,
1262          even though the tape was originally recorded in single-player mode */
1263
1264       /* remove player input actions for all players but the first one */
1265       for (i = 1; i < MAX_PLAYERS; i++)
1266         tape.player_participates[i] = FALSE;
1267
1268       tape.changed = TRUE;
1269     }
1270     else if (is_string_suffix(cheat_input, ":save-native-level") ||
1271              is_string_suffix(cheat_input, ":snl"))
1272     {
1273       SaveNativeLevel(&level);
1274     }
1275   }
1276   else if (game_status == GAME_MODE_PLAYING)
1277   {
1278 #ifdef DEBUG
1279     if (is_string_suffix(cheat_input, ".q"))
1280       DEBUG_SetMaximumDynamite();
1281 #endif
1282   }
1283   else if (game_status == GAME_MODE_EDITOR)
1284   {
1285     if (is_string_suffix(cheat_input, ":dump-brush") ||
1286         is_string_suffix(cheat_input, ":DB"))
1287     {
1288       DumpBrush();
1289     }
1290     else if (is_string_suffix(cheat_input, ":DDB"))
1291     {
1292       DumpBrush_Small();
1293     }
1294   }
1295 }
1296
1297 void HandleKey(Key key, int key_status)
1298 {
1299   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
1300   static struct SetupKeyboardInfo ski;
1301   static struct SetupShortcutInfo ssi;
1302   static struct
1303   {
1304     Key *key_custom;
1305     Key *key_snap;
1306     Key key_default;
1307     byte action;
1308   } key_info[] =
1309   {
1310     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
1311     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
1312     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
1313     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
1314     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
1315     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
1316   };
1317   int joy = 0;
1318   int i;
1319
1320   if (game_status == GAME_MODE_PLAYING)
1321   {
1322     /* only needed for single-step tape recording mode */
1323     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1324     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1325     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1326     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
1327     int pnr;
1328
1329     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
1330     {
1331       byte key_action = 0;
1332
1333       if (setup.input[pnr].use_joystick)
1334         continue;
1335
1336       ski = setup.input[pnr].key;
1337
1338       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1339         if (key == *key_info[i].key_custom)
1340           key_action |= key_info[i].action;
1341
1342       /* use combined snap+direction keys for the first player only */
1343       if (pnr == 0)
1344       {
1345         ssi = setup.shortcut;
1346
1347         for (i = 0; i < NUM_DIRECTIONS; i++)
1348           if (key == *key_info[i].key_snap)
1349             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
1350       }
1351
1352       /* clear delayed snap and drop actions in single step mode (see below) */
1353       if (tape.single_step)
1354       {
1355         if (clear_snap_button[pnr])
1356         {
1357           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
1358           clear_snap_button[pnr] = FALSE;
1359         }
1360
1361         if (clear_drop_button[pnr])
1362         {
1363           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1364           clear_drop_button[pnr] = FALSE;
1365         }
1366       }
1367
1368       if (key_status == KEY_PRESSED)
1369         stored_player[pnr].action |= key_action;
1370       else
1371         stored_player[pnr].action &= ~key_action;
1372
1373       if (tape.single_step && tape.recording && tape.pausing)
1374       {
1375         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1376         {
1377           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1378
1379           /* if snap key already pressed, don't snap when releasing (below) */
1380           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1381             element_snapped[pnr] = TRUE;
1382
1383           /* if drop key already pressed, don't drop when releasing (below) */
1384           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1385             element_dropped[pnr] = TRUE;
1386         }
1387 #if 1
1388         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1389         {
1390           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1391               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1392           {
1393 #if 0
1394             printf("::: drop key pressed\n");
1395 #endif
1396
1397             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1398                 getRedDiskReleaseFlag_SP() == 0)
1399               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1400
1401             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1402           }
1403         }
1404 #endif
1405         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1406         {
1407           if (key_action & KEY_BUTTON_SNAP)
1408           {
1409             /* if snap key was released without moving (see above), snap now */
1410             if (!element_snapped[pnr])
1411             {
1412               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1413
1414               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1415
1416               /* clear delayed snap button on next event */
1417               clear_snap_button[pnr] = TRUE;
1418             }
1419
1420             element_snapped[pnr] = FALSE;
1421           }
1422
1423 #if 1
1424           if (key_action & KEY_BUTTON_DROP &&
1425               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1426           {
1427             /* if drop key was released without moving (see above), drop now */
1428             if (!element_dropped[pnr])
1429             {
1430               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1431
1432               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1433                   getRedDiskReleaseFlag_SP() != 0)
1434                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1435
1436               /* clear delayed drop button on next event */
1437               clear_drop_button[pnr] = TRUE;
1438             }
1439
1440             element_dropped[pnr] = FALSE;
1441           }
1442 #endif
1443         }
1444       }
1445       else if (tape.recording && tape.pausing)
1446       {
1447         /* prevent key release events from un-pausing a paused game */
1448         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1449           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1450       }
1451     }
1452   }
1453   else
1454   {
1455     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1456       if (key == key_info[i].key_default)
1457         joy |= key_info[i].action;
1458   }
1459
1460   if (joy)
1461   {
1462     if (key_status == KEY_PRESSED)
1463       key_joystick_mapping |= joy;
1464     else
1465       key_joystick_mapping &= ~joy;
1466
1467     HandleJoystick();
1468   }
1469
1470   if (game_status != GAME_MODE_PLAYING)
1471     key_joystick_mapping = 0;
1472
1473   if (key_status == KEY_RELEASED)
1474     return;
1475
1476   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1477       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1478   {
1479     setup.fullscreen = !setup.fullscreen;
1480
1481 #if 0
1482     printf("::: %d\n", setup.window_scaling_percent);
1483 #endif
1484
1485     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1486
1487     if (game_status == GAME_MODE_SETUP)
1488       RedrawSetupScreenAfterFullscreenToggle();
1489
1490     return;
1491   }
1492
1493   if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1494       (GetKeyModState() & KMOD_Alt) && video.window_scaling_available &&
1495       !video.fullscreen_enabled)
1496   {
1497     if (key == KSYM_0)
1498       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1499     else
1500       setup.window_scaling_percent +=
1501         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1502
1503     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1504       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1505     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1506       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1507
1508     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1509
1510     if (game_status == GAME_MODE_SETUP)
1511       RedrawSetupScreenAfterFullscreenToggle();
1512
1513     return;
1514   }
1515
1516 #if 0
1517   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1518       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1519 #else
1520   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1521       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1522 #endif
1523   {
1524     GameEnd();
1525
1526     return;
1527   }
1528
1529   if (game_status == GAME_MODE_MAIN &&
1530       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1531   {
1532     StartGameActions(options.network, setup.autorecord, level.random_seed);
1533
1534     return;
1535   }
1536
1537   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1538   {
1539     if (key == setup.shortcut.save_game)
1540       TapeQuickSave();
1541     else if (key == setup.shortcut.load_game)
1542       TapeQuickLoad();
1543     else if (key == setup.shortcut.toggle_pause)
1544       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1545
1546     HandleTapeButtonKeys(key);
1547     HandleSoundButtonKeys(key);
1548   }
1549
1550   if (game_status == GAME_MODE_PLAYING && !network_playing)
1551   {
1552     int centered_player_nr_next = -999;
1553
1554     if (key == setup.shortcut.focus_player_all)
1555       centered_player_nr_next = -1;
1556     else
1557       for (i = 0; i < MAX_PLAYERS; i++)
1558         if (key == setup.shortcut.focus_player[i])
1559           centered_player_nr_next = i;
1560
1561     if (centered_player_nr_next != -999)
1562     {
1563       game.centered_player_nr_next = centered_player_nr_next;
1564       game.set_centered_player = TRUE;
1565
1566       if (tape.recording)
1567       {
1568         tape.centered_player_nr_next = game.centered_player_nr_next;
1569         tape.set_centered_player = TRUE;
1570       }
1571     }
1572   }
1573
1574   HandleKeysSpecial(key);
1575
1576   if (HandleGadgetsKeyInput(key))
1577   {
1578     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1579       key = KSYM_UNDEFINED;
1580   }
1581
1582   switch (game_status)
1583   {
1584     case GAME_MODE_PSEUDO_TYPENAME:
1585       HandleTypeName(0, key);
1586       break;
1587
1588     case GAME_MODE_TITLE:
1589     case GAME_MODE_MAIN:
1590     case GAME_MODE_LEVELS:
1591     case GAME_MODE_LEVELNR:
1592     case GAME_MODE_SETUP:
1593     case GAME_MODE_INFO:
1594     case GAME_MODE_SCORES:
1595       switch (key)
1596       {
1597         case KSYM_space:
1598         case KSYM_Return:
1599           if (game_status == GAME_MODE_TITLE)
1600             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1601           else if (game_status == GAME_MODE_MAIN)
1602             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1603           else if (game_status == GAME_MODE_LEVELS)
1604             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1605           else if (game_status == GAME_MODE_LEVELNR)
1606             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1607           else if (game_status == GAME_MODE_SETUP)
1608             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1609           else if (game_status == GAME_MODE_INFO)
1610             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1611           else if (game_status == GAME_MODE_SCORES)
1612             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1613           break;
1614
1615         case KSYM_Escape:
1616           if (game_status != GAME_MODE_MAIN)
1617             FadeSkipNextFadeIn();
1618
1619           if (game_status == GAME_MODE_TITLE)
1620             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1621           else if (game_status == GAME_MODE_LEVELS)
1622             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1623           else if (game_status == GAME_MODE_LEVELNR)
1624             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1625           else if (game_status == GAME_MODE_SETUP)
1626             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1627           else if (game_status == GAME_MODE_INFO)
1628             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1629           else if (game_status == GAME_MODE_SCORES)
1630             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1631           break;
1632
1633         case KSYM_Page_Up:
1634           if (game_status == GAME_MODE_LEVELS)
1635             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1636           else if (game_status == GAME_MODE_LEVELNR)
1637             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1638           else if (game_status == GAME_MODE_SETUP)
1639             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1640           else if (game_status == GAME_MODE_INFO)
1641             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1642           else if (game_status == GAME_MODE_SCORES)
1643             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1644           break;
1645
1646         case KSYM_Page_Down:
1647           if (game_status == GAME_MODE_LEVELS)
1648             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1649           else if (game_status == GAME_MODE_LEVELNR)
1650             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1651           else if (game_status == GAME_MODE_SETUP)
1652             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1653           else if (game_status == GAME_MODE_INFO)
1654             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1655           else if (game_status == GAME_MODE_SCORES)
1656             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1657           break;
1658
1659 #ifdef DEBUG
1660         case KSYM_0:
1661           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1662           break;
1663
1664         case KSYM_b:
1665           setup.sp_show_border_elements = !setup.sp_show_border_elements;
1666           printf("Supaplex border elements %s\n",
1667                  setup.sp_show_border_elements ? "enabled" : "disabled");
1668           break;
1669 #endif
1670
1671         default:
1672           break;
1673       }
1674       break;
1675
1676     case GAME_MODE_EDITOR:
1677       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1678         HandleLevelEditorKeyInput(key);
1679       break;
1680
1681     case GAME_MODE_PLAYING:
1682     {
1683       switch (key)
1684       {
1685         case KSYM_Escape:
1686           RequestQuitGame(setup.ask_on_escape);
1687           break;
1688
1689 #ifdef DEBUG
1690         case KSYM_0:
1691 #if 0
1692         case KSYM_1:
1693         case KSYM_2:
1694         case KSYM_3:
1695         case KSYM_4:
1696         case KSYM_5:
1697         case KSYM_6:
1698         case KSYM_7:
1699         case KSYM_8:
1700         case KSYM_9:
1701 #endif
1702           if (key == KSYM_0)
1703           {
1704             if (GameFrameDelay == 500)
1705               GameFrameDelay = GAME_FRAME_DELAY;
1706             else
1707               GameFrameDelay = 500;
1708           }
1709           else
1710             GameFrameDelay = (key - KSYM_0) * 10;
1711           printf("Game speed == %d%% (%d ms delay between two frames)\n",
1712                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1713           break;
1714
1715         case KSYM_d:
1716           if (options.debug)
1717           {
1718             options.debug = FALSE;
1719             printf("debug mode disabled\n");
1720           }
1721           else
1722           {
1723             options.debug = TRUE;
1724             printf("debug mode enabled\n");
1725           }
1726           break;
1727
1728 #if 0
1729         case KSYM_s:
1730           if (!global.fps_slowdown)
1731           {
1732             global.fps_slowdown = TRUE;
1733             global.fps_slowdown_factor = 2;
1734             printf("fps slowdown enabled -- display only every 2nd frame\n");
1735           }
1736           else if (global.fps_slowdown_factor == 2)
1737           {
1738             global.fps_slowdown_factor = 4;
1739             printf("fps slowdown enabled -- display only every 4th frame\n");
1740           }
1741           else
1742           {
1743             global.fps_slowdown = FALSE;
1744             global.fps_slowdown_factor = 1;
1745             printf("fps slowdown disabled\n");
1746           }
1747           break;
1748 #endif
1749
1750 #if 0
1751         case KSYM_f:
1752           ScrollStepSize = TILEX / 8;
1753           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1754           break;
1755
1756         case KSYM_g:
1757           ScrollStepSize = TILEX / 4;
1758           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1759           break;
1760
1761         case KSYM_h:
1762           ScrollStepSize = TILEX / 2;
1763           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1764           break;
1765
1766         case KSYM_l:
1767           ScrollStepSize = TILEX;
1768           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1769           break;
1770 #endif
1771
1772         case KSYM_v:
1773           printf("::: currently using game engine version %d\n",
1774                  game.engine_version);
1775           break;
1776 #endif
1777
1778         default:
1779           break;
1780       }
1781       break;
1782     }
1783
1784     default:
1785       if (key == KSYM_Escape)
1786       {
1787         game_status = GAME_MODE_MAIN;
1788         DrawMainMenu();
1789
1790         return;
1791       }
1792   }
1793 }
1794
1795 void HandleNoEvent()
1796 {
1797   if (button_status && game_status != GAME_MODE_PLAYING)
1798   {
1799     HandleButton(0, 0, -button_status, button_status);
1800
1801 #if 0
1802     return;
1803 #endif
1804   }
1805   else
1806   {
1807     HandleJoystick();
1808   }
1809
1810 #if defined(NETWORK_AVALIABLE)
1811   if (options.network)
1812     HandleNetworking();
1813 #endif
1814
1815   switch (game_status)
1816   {
1817     case GAME_MODE_MAIN:
1818       DrawPreviewLevelAnimation();
1819       DoAnimation();
1820       break;
1821
1822     case GAME_MODE_LEVELS:
1823     case GAME_MODE_LEVELNR:
1824     case GAME_MODE_SETUP:
1825     case GAME_MODE_INFO:
1826     case GAME_MODE_SCORES:
1827       DoAnimation();
1828       break;
1829
1830     case GAME_MODE_EDITOR:
1831       HandleLevelEditorIdle();
1832       break;
1833
1834     default:
1835       break;
1836   }
1837 }
1838
1839 static int HandleJoystickForAllPlayers()
1840 {
1841   int i;
1842   int result = 0;
1843
1844   for (i = 0; i < MAX_PLAYERS; i++)
1845   {
1846     byte joy_action = 0;
1847
1848     /*
1849     if (!setup.input[i].use_joystick)
1850       continue;
1851       */
1852
1853     joy_action = Joystick(i);
1854     result |= joy_action;
1855
1856     if (!setup.input[i].use_joystick)
1857       continue;
1858
1859     stored_player[i].action = joy_action;
1860   }
1861
1862   return result;
1863 }
1864
1865 void HandleJoystick()
1866 {
1867   int joystick  = HandleJoystickForAllPlayers();
1868   int keyboard  = key_joystick_mapping;
1869   int joy       = (joystick | keyboard);
1870   int left      = joy & JOY_LEFT;
1871   int right     = joy & JOY_RIGHT;
1872   int up        = joy & JOY_UP;
1873   int down      = joy & JOY_DOWN;
1874   int button    = joy & JOY_BUTTON;
1875   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1876   int dx        = (left ? -1    : right ? 1     : 0);
1877   int dy        = (up   ? -1    : down  ? 1     : 0);
1878
1879   switch (game_status)
1880   {
1881     case GAME_MODE_TITLE:
1882     case GAME_MODE_MAIN:
1883     case GAME_MODE_LEVELS:
1884     case GAME_MODE_LEVELNR:
1885     case GAME_MODE_SETUP:
1886     case GAME_MODE_INFO:
1887     {
1888       static unsigned int joystickmove_delay = 0;
1889
1890       if (joystick && !button &&
1891           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1892         newbutton = dx = dy = 0;
1893
1894       if (game_status == GAME_MODE_TITLE)
1895         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1896       else if (game_status == GAME_MODE_MAIN)
1897         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1898       else if (game_status == GAME_MODE_LEVELS)
1899         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1900       else if (game_status == GAME_MODE_LEVELNR)
1901         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1902       else if (game_status == GAME_MODE_SETUP)
1903         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1904       else if (game_status == GAME_MODE_INFO)
1905         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1906       break;
1907     }
1908
1909     case GAME_MODE_SCORES:
1910       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1911       break;
1912
1913 #if 0
1914     case GAME_MODE_EDITOR:
1915       HandleLevelEditorIdle();
1916       break;
1917 #endif
1918
1919     case GAME_MODE_PLAYING:
1920       if (tape.playing || keyboard)
1921         newbutton = ((joy & JOY_BUTTON) != 0);
1922
1923 #if 0
1924       if (newbutton && local_player->LevelSolved_GameEnd)
1925 #else
1926       if (newbutton && AllPlayersGone)
1927 #endif
1928       {
1929         GameEnd();
1930
1931         return;
1932       }
1933
1934       break;
1935
1936     default:
1937       break;
1938   }
1939 }