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