rnd-20140117-2-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 void HandleFingerEvent(FingerEvent *event)
518 {
519 #if 0
520   static int num_events = 0;
521   int max_events = 10;
522 #endif
523
524 #if 0
525 #if DEBUG_EVENTS
526   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
527         event->type == EVENT_FINGERPRESS ? "pressed" :
528         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
529         event->touchId,
530         event->fingerId,
531         event->x, event->y,
532         event->dx, event->dy,
533         event->pressure);
534 #endif
535 #endif
536
537 #if 0
538   int x = (int)(event->x * video.width);
539   int y = (int)(event->y * video.height);
540   int button = MB_LEFTBUTTON;
541
542   Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
543 #endif
544
545 #if 0
546   if (++num_events >= max_events)
547     CloseAllAndExit(0);
548 #endif
549
550 #if 1
551 #if 0
552   if (event->type == EVENT_FINGERPRESS ||
553       event->type == EVENT_FINGERMOTION)
554     button_status = button;
555   else
556     button_status = MB_RELEASED;
557
558   int max_x = SX + SXSIZE;
559   int max_y = SY + SYSIZE;
560 #endif
561
562 #if 1
563   if (game_status == GAME_MODE_PLAYING)
564 #else
565   if (game_status == GAME_MODE_PLAYING &&
566       x < max_x)
567 #endif
568   {
569     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
570                       KEY_PRESSED);
571 #if 1
572     Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
573                event->y > 2.0 / 3.0 ? setup.input[0].key.down :
574                event->x < 1.0 / 3.0 ? setup.input[0].key.left :
575                event->x > 2.0 / 3.0 ? setup.input[0].key.right :
576                setup.input[0].key.drop);
577 #else
578     Key key = (y <     max_y / 3 ? setup.input[0].key.up :
579                y > 2 * max_y / 3 ? setup.input[0].key.down :
580                x <     max_x / 3 ? setup.input[0].key.left :
581                x > 2 * max_x / 3 ? setup.input[0].key.right :
582                setup.input[0].key.drop);
583 #endif
584
585     Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
586
587     HandleKey(key, key_status);
588   }
589   else
590   {
591 #if 0
592     Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
593           button_status, button);
594
595     HandleButton(x, y, button_status, button);
596 #endif
597   }
598 #endif
599 }
600
601 static boolean checkTextInputKeyModState()
602 {
603   // when playing, only handle raw key events and ignore text input
604   if (game_status == GAME_MODE_PLAYING)
605     return FALSE;
606
607   return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
608 }
609
610 void HandleTextEvent(TextEvent *event)
611 {
612   char *text = event->text;
613   Key key = getKeyFromKeyName(text);
614
615 #if DEBUG_EVENTS
616   Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
617         text,
618         strlen(text),
619         text[0], (int)(text[0]),
620         key,
621         getKeyNameFromKey(key),
622         GetKeyModState());
623 #endif
624
625   // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
626   /*
627   if (game_status != GAME_MODE_PLAYING &&
628       (GetKeyModState() & KMOD_TextInput) != KMOD_None)
629   */
630   if (checkTextInputKeyModState())
631   {
632     HandleKey(key, KEY_PRESSED);
633     HandleKey(key, KEY_RELEASED);
634   }
635 }
636 #endif
637
638 void HandleKeyEvent(KeyEvent *event)
639 {
640   int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
641   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
642   Key key = GetEventKey(event, with_modifiers);
643   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
644
645 #if DEBUG_EVENTS
646   Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
647         event->type == EVENT_KEYPRESS ? "pressed" : "released",
648         event->keysym.scancode,
649         event->keysym.sym,
650         keymod,
651         GetKeyModState(),
652         key,
653         getKeyNameFromKey(key));
654 #endif
655
656 #if 0
657   if (key == KSYM_Menu)
658     Error(ERR_DEBUG, "menu key pressed");
659   else if (key == KSYM_Back)
660     Error(ERR_DEBUG, "back key pressed");
661 #endif
662
663 #if defined(PLATFORM_ANDROID)
664   // always map the "back" button to the "escape" key on Android devices
665   if (key == KSYM_Back)
666     key = KSYM_Escape;
667 #endif
668
669   HandleKeyModState(keymod, key_status);
670
671 #if defined(TARGET_SDL2)
672
673   // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
674   /*
675   if (game_status == GAME_MODE_PLAYING ||
676       (GetKeyModState() & KMOD_TextInput) == KMOD_None)
677   */
678   if (!checkTextInputKeyModState())
679     HandleKey(key, key_status);
680 #else
681   HandleKey(key, key_status);
682 #endif
683 }
684
685 void HandleFocusEvent(FocusChangeEvent *event)
686 {
687   static int old_joystick_status = -1;
688
689   if (event->type == EVENT_FOCUSOUT)
690   {
691     KeyboardAutoRepeatOn();
692     old_joystick_status = joystick.status;
693     joystick.status = JOYSTICK_NOT_AVAILABLE;
694
695     ClearPlayerAction();
696   }
697   else if (event->type == EVENT_FOCUSIN)
698   {
699     /* When there are two Rocks'n'Diamonds windows which overlap and
700        the player moves the pointer from one game window to the other,
701        a 'FocusOut' event is generated for the window the pointer is
702        leaving and a 'FocusIn' event is generated for the window the
703        pointer is entering. In some cases, it can happen that the
704        'FocusIn' event is handled by the one game process before the
705        'FocusOut' event by the other game process. In this case the
706        X11 environment would end up with activated keyboard auto repeat,
707        because unfortunately this is a global setting and not (which
708        would be far better) set for each X11 window individually.
709        The effect would be keyboard auto repeat while playing the game
710        (game_status == GAME_MODE_PLAYING), which is not desired.
711        To avoid this special case, we just wait 1/10 second before
712        processing the 'FocusIn' event.
713     */
714
715     if (game_status == GAME_MODE_PLAYING)
716     {
717       Delay(100);
718       KeyboardAutoRepeatOffUnlessAutoplay();
719     }
720
721     if (old_joystick_status != -1)
722       joystick.status = old_joystick_status;
723   }
724 }
725
726 void HandleClientMessageEvent(ClientMessageEvent *event)
727 {
728   if (CheckCloseWindowEvent(event))
729     CloseAllAndExit(0);
730 }
731
732 void HandleWindowManagerEvent(Event *event)
733 {
734 #if defined(TARGET_SDL)
735   SDLHandleWindowManagerEvent(event);
736 #endif
737 }
738
739 void HandleButton(int mx, int my, int button, int button_nr)
740 {
741   static int old_mx = 0, old_my = 0;
742
743   if (button < 0)
744   {
745     mx = old_mx;
746     my = old_my;
747     button = -button;
748   }
749   else
750   {
751     old_mx = mx;
752     old_my = my;
753   }
754
755   if (HandleGadgets(mx, my, button))
756   {
757     /* do not handle this button event anymore */
758     mx = my = -32;      /* force mouse event to be outside screen tiles */
759   }
760
761   /* do not use scroll wheel button events for anything other than gadgets */
762   if (IS_WHEEL_BUTTON(button_nr))
763     return;
764
765 #if 0
766   Error(ERR_DEBUG, "::: game_status == %d", game_status);
767 #endif
768
769   switch (game_status)
770   {
771     case GAME_MODE_TITLE:
772       HandleTitleScreen(mx, my, 0, 0, button);
773       break;
774
775     case GAME_MODE_MAIN:
776       HandleMainMenu(mx, my, 0, 0, button);
777       break;
778
779     case GAME_MODE_PSEUDO_TYPENAME:
780       HandleTypeName(0, KSYM_Return);
781       break;
782
783     case GAME_MODE_LEVELS:
784       HandleChooseLevelSet(mx, my, 0, 0, button);
785       break;
786
787     case GAME_MODE_LEVELNR:
788       HandleChooseLevelNr(mx, my, 0, 0, button);
789       break;
790
791     case GAME_MODE_SCORES:
792       HandleHallOfFame(0, 0, 0, 0, button);
793       break;
794
795     case GAME_MODE_EDITOR:
796       HandleLevelEditorIdle();
797       break;
798
799     case GAME_MODE_INFO:
800       HandleInfoScreen(mx, my, 0, 0, button);
801       break;
802
803     case GAME_MODE_SETUP:
804       HandleSetupScreen(mx, my, 0, 0, button);
805       break;
806
807     case GAME_MODE_PLAYING:
808 #ifdef DEBUG
809       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
810         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
811 #endif
812       break;
813
814     default:
815       break;
816   }
817 }
818
819 static boolean is_string_suffix(char *string, char *suffix)
820 {
821   int string_len = strlen(string);
822   int suffix_len = strlen(suffix);
823
824   if (suffix_len > string_len)
825     return FALSE;
826
827   return (strEqual(&string[string_len - suffix_len], suffix));
828 }
829
830 #define MAX_CHEAT_INPUT_LEN     32
831
832 static void HandleKeysSpecial(Key key)
833 {
834   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
835   char letter = getCharFromKey(key);
836   int cheat_input_len = strlen(cheat_input);
837   int i;
838
839   if (letter == 0)
840     return;
841
842   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
843   {
844     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
845       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
846
847     cheat_input_len = MAX_CHEAT_INPUT_LEN;
848   }
849
850   cheat_input[cheat_input_len++] = letter;
851   cheat_input[cheat_input_len] = '\0';
852
853 #if DEBUG_EVENTS
854   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
855 #endif
856
857   if (game_status == GAME_MODE_MAIN)
858   {
859     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
860         is_string_suffix(cheat_input, ":ist"))
861     {
862       InsertSolutionTape();
863     }
864     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
865              is_string_suffix(cheat_input, ":rg"))
866     {
867       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
868       DrawMainMenu();
869     }
870     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
871              is_string_suffix(cheat_input, ":rs"))
872     {
873       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
874       DrawMainMenu();
875     }
876     else if (is_string_suffix(cheat_input, ":reload-music") ||
877              is_string_suffix(cheat_input, ":rm"))
878     {
879       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
880       DrawMainMenu();
881     }
882     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
883              is_string_suffix(cheat_input, ":ra"))
884     {
885       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
886                           1 << ARTWORK_TYPE_SOUNDS |
887                           1 << ARTWORK_TYPE_MUSIC);
888       DrawMainMenu();
889     }
890     else if (is_string_suffix(cheat_input, ":dump-level") ||
891              is_string_suffix(cheat_input, ":dl"))
892     {
893       DumpLevel(&level);
894     }
895     else if (is_string_suffix(cheat_input, ":dump-tape") ||
896              is_string_suffix(cheat_input, ":dt"))
897     {
898       DumpTape(&tape);
899     }
900     else if (is_string_suffix(cheat_input, ":save-native-level") ||
901              is_string_suffix(cheat_input, ":snl"))
902     {
903       SaveNativeLevel(&level);
904     }
905   }
906   else if (game_status == GAME_MODE_PLAYING)
907   {
908 #ifdef DEBUG
909     if (is_string_suffix(cheat_input, ".q"))
910       DEBUG_SetMaximumDynamite();
911 #endif
912   }
913   else if (game_status == GAME_MODE_EDITOR)
914   {
915     if (is_string_suffix(cheat_input, ":dump-brush") ||
916         is_string_suffix(cheat_input, ":DB"))
917     {
918       DumpBrush();
919     }
920     else if (is_string_suffix(cheat_input, ":DDB"))
921     {
922       DumpBrush_Small();
923     }
924   }
925 }
926
927 void HandleKey(Key key, int key_status)
928 {
929   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
930   static struct SetupKeyboardInfo ski;
931   static struct SetupShortcutInfo ssi;
932   static struct
933   {
934     Key *key_custom;
935     Key *key_snap;
936     Key key_default;
937     byte action;
938   } key_info[] =
939   {
940     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
941     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
942     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
943     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
944     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
945     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
946   };
947   int joy = 0;
948   int i;
949
950   if (game_status == GAME_MODE_PLAYING)
951   {
952     /* only needed for single-step tape recording mode */
953     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
954     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
955     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
956     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
957     int pnr;
958
959     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
960     {
961       byte key_action = 0;
962
963       if (setup.input[pnr].use_joystick)
964         continue;
965
966       ski = setup.input[pnr].key;
967
968       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
969         if (key == *key_info[i].key_custom)
970           key_action |= key_info[i].action;
971
972       /* use combined snap+direction keys for the first player only */
973       if (pnr == 0)
974       {
975         ssi = setup.shortcut;
976
977         for (i = 0; i < NUM_DIRECTIONS; i++)
978           if (key == *key_info[i].key_snap)
979             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
980       }
981
982       /* clear delayed snap and drop actions in single step mode (see below) */
983       if (tape.single_step)
984       {
985         if (clear_snap_button[pnr])
986         {
987           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
988           clear_snap_button[pnr] = FALSE;
989         }
990
991         if (clear_drop_button[pnr])
992         {
993           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
994           clear_drop_button[pnr] = FALSE;
995         }
996       }
997
998       if (key_status == KEY_PRESSED)
999         stored_player[pnr].action |= key_action;
1000       else
1001         stored_player[pnr].action &= ~key_action;
1002
1003       if (tape.single_step && tape.recording && tape.pausing)
1004       {
1005         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1006         {
1007           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1008
1009           /* if snap key already pressed, don't snap when releasing (below) */
1010           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1011             element_snapped[pnr] = TRUE;
1012
1013           /* if drop key already pressed, don't drop when releasing (below) */
1014           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1015             element_dropped[pnr] = TRUE;
1016         }
1017 #if 1
1018         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1019         {
1020           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1021               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1022           {
1023 #if 0
1024             printf("::: drop key pressed\n");
1025 #endif
1026
1027             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1028                 getRedDiskReleaseFlag_SP() == 0)
1029               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1030
1031             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1032           }
1033         }
1034 #endif
1035         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1036         {
1037           if (key_action & KEY_BUTTON_SNAP)
1038           {
1039             /* if snap key was released without moving (see above), snap now */
1040             if (!element_snapped[pnr])
1041             {
1042               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1043
1044               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1045
1046               /* clear delayed snap button on next event */
1047               clear_snap_button[pnr] = TRUE;
1048             }
1049
1050             element_snapped[pnr] = FALSE;
1051           }
1052
1053 #if 1
1054           if (key_action & KEY_BUTTON_DROP &&
1055               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1056           {
1057             /* if drop key was released without moving (see above), drop now */
1058             if (!element_dropped[pnr])
1059             {
1060               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1061
1062               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1063                   getRedDiskReleaseFlag_SP() != 0)
1064                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1065
1066               /* clear delayed drop button on next event */
1067               clear_drop_button[pnr] = TRUE;
1068             }
1069
1070             element_dropped[pnr] = FALSE;
1071           }
1072 #endif
1073         }
1074       }
1075       else if (tape.recording && tape.pausing)
1076       {
1077         /* prevent key release events from un-pausing a paused game */
1078         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1079           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1080       }
1081     }
1082   }
1083   else
1084   {
1085     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1086       if (key == key_info[i].key_default)
1087         joy |= key_info[i].action;
1088   }
1089
1090   if (joy)
1091   {
1092     if (key_status == KEY_PRESSED)
1093       key_joystick_mapping |= joy;
1094     else
1095       key_joystick_mapping &= ~joy;
1096
1097     HandleJoystick();
1098   }
1099
1100   if (game_status != GAME_MODE_PLAYING)
1101     key_joystick_mapping = 0;
1102
1103   if (key_status == KEY_RELEASED)
1104     return;
1105
1106   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1107       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1108   {
1109     setup.fullscreen = !setup.fullscreen;
1110
1111 #if 0
1112     printf("::: %d\n", setup.window_scaling_percent);
1113 #endif
1114
1115     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1116
1117     if (game_status == GAME_MODE_SETUP)
1118       RedrawSetupScreenAfterFullscreenToggle();
1119
1120     return;
1121   }
1122
1123   if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1124       (GetKeyModState() & KMOD_Alt) && video.window_scaling_available &&
1125       !video.fullscreen_enabled)
1126   {
1127     if (key == KSYM_0)
1128       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1129     else
1130       setup.window_scaling_percent +=
1131         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1132
1133     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1134       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1135     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1136       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1137
1138     ToggleFullscreenOrChangeWindowScalingIfNeeded();
1139
1140     if (game_status == GAME_MODE_SETUP)
1141       RedrawSetupScreenAfterFullscreenToggle();
1142
1143     return;
1144   }
1145
1146 #if 0
1147   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1148       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1149 #else
1150   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1151       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1152 #endif
1153   {
1154     GameEnd();
1155
1156     return;
1157   }
1158
1159   if (game_status == GAME_MODE_MAIN &&
1160       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1161   {
1162     StartGameActions(options.network, setup.autorecord, level.random_seed);
1163
1164     return;
1165   }
1166
1167   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1168   {
1169     if (key == setup.shortcut.save_game)
1170       TapeQuickSave();
1171     else if (key == setup.shortcut.load_game)
1172       TapeQuickLoad();
1173     else if (key == setup.shortcut.toggle_pause)
1174       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1175
1176     HandleTapeButtonKeys(key);
1177     HandleSoundButtonKeys(key);
1178   }
1179
1180   if (game_status == GAME_MODE_PLAYING && !network_playing)
1181   {
1182     int centered_player_nr_next = -999;
1183
1184     if (key == setup.shortcut.focus_player_all)
1185       centered_player_nr_next = -1;
1186     else
1187       for (i = 0; i < MAX_PLAYERS; i++)
1188         if (key == setup.shortcut.focus_player[i])
1189           centered_player_nr_next = i;
1190
1191     if (centered_player_nr_next != -999)
1192     {
1193       game.centered_player_nr_next = centered_player_nr_next;
1194       game.set_centered_player = TRUE;
1195
1196       if (tape.recording)
1197       {
1198         tape.centered_player_nr_next = game.centered_player_nr_next;
1199         tape.set_centered_player = TRUE;
1200       }
1201     }
1202   }
1203
1204   HandleKeysSpecial(key);
1205
1206   if (HandleGadgetsKeyInput(key))
1207   {
1208     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1209       key = KSYM_UNDEFINED;
1210   }
1211
1212   switch (game_status)
1213   {
1214     case GAME_MODE_PSEUDO_TYPENAME:
1215       HandleTypeName(0, key);
1216       break;
1217
1218     case GAME_MODE_TITLE:
1219     case GAME_MODE_MAIN:
1220     case GAME_MODE_LEVELS:
1221     case GAME_MODE_LEVELNR:
1222     case GAME_MODE_SETUP:
1223     case GAME_MODE_INFO:
1224     case GAME_MODE_SCORES:
1225       switch (key)
1226       {
1227         case KSYM_space:
1228         case KSYM_Return:
1229           if (game_status == GAME_MODE_TITLE)
1230             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1231           else if (game_status == GAME_MODE_MAIN)
1232             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1233           else if (game_status == GAME_MODE_LEVELS)
1234             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1235           else if (game_status == GAME_MODE_LEVELNR)
1236             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1237           else if (game_status == GAME_MODE_SETUP)
1238             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1239           else if (game_status == GAME_MODE_INFO)
1240             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1241           else if (game_status == GAME_MODE_SCORES)
1242             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1243           break;
1244
1245         case KSYM_Escape:
1246           if (game_status != GAME_MODE_MAIN)
1247             FadeSkipNextFadeIn();
1248
1249           if (game_status == GAME_MODE_TITLE)
1250             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1251           else if (game_status == GAME_MODE_LEVELS)
1252             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1253           else if (game_status == GAME_MODE_LEVELNR)
1254             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1255           else if (game_status == GAME_MODE_SETUP)
1256             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1257           else if (game_status == GAME_MODE_INFO)
1258             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1259           else if (game_status == GAME_MODE_SCORES)
1260             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1261           break;
1262
1263         case KSYM_Page_Up:
1264           if (game_status == GAME_MODE_LEVELS)
1265             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1266           else if (game_status == GAME_MODE_LEVELNR)
1267             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1268           else if (game_status == GAME_MODE_SETUP)
1269             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1270           else if (game_status == GAME_MODE_INFO)
1271             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1272           else if (game_status == GAME_MODE_SCORES)
1273             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1274           break;
1275
1276         case KSYM_Page_Down:
1277           if (game_status == GAME_MODE_LEVELS)
1278             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1279           else if (game_status == GAME_MODE_LEVELNR)
1280             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1281           else if (game_status == GAME_MODE_SETUP)
1282             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1283           else if (game_status == GAME_MODE_INFO)
1284             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1285           else if (game_status == GAME_MODE_SCORES)
1286             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1287           break;
1288
1289 #ifdef DEBUG
1290         case KSYM_0:
1291           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1292           break;
1293
1294         case KSYM_b:
1295           setup.sp_show_border_elements = !setup.sp_show_border_elements;
1296           printf("Supaplex border elements %s\n",
1297                  setup.sp_show_border_elements ? "enabled" : "disabled");
1298           break;
1299 #endif
1300
1301         default:
1302           break;
1303       }
1304       break;
1305
1306     case GAME_MODE_EDITOR:
1307       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1308         HandleLevelEditorKeyInput(key);
1309       break;
1310
1311     case GAME_MODE_PLAYING:
1312     {
1313       switch (key)
1314       {
1315         case KSYM_Escape:
1316           RequestQuitGame(setup.ask_on_escape);
1317           break;
1318
1319 #ifdef DEBUG
1320         case KSYM_0:
1321 #if 0
1322         case KSYM_1:
1323         case KSYM_2:
1324         case KSYM_3:
1325         case KSYM_4:
1326         case KSYM_5:
1327         case KSYM_6:
1328         case KSYM_7:
1329         case KSYM_8:
1330         case KSYM_9:
1331 #endif
1332           if (key == KSYM_0)
1333           {
1334             if (GameFrameDelay == 500)
1335               GameFrameDelay = GAME_FRAME_DELAY;
1336             else
1337               GameFrameDelay = 500;
1338           }
1339           else
1340             GameFrameDelay = (key - KSYM_0) * 10;
1341           printf("Game speed == %d%% (%d ms delay between two frames)\n",
1342                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1343           break;
1344
1345         case KSYM_d:
1346           if (options.debug)
1347           {
1348             options.debug = FALSE;
1349             printf("debug mode disabled\n");
1350           }
1351           else
1352           {
1353             options.debug = TRUE;
1354             printf("debug mode enabled\n");
1355           }
1356           break;
1357
1358         case KSYM_s:
1359           if (!global.fps_slowdown)
1360           {
1361             global.fps_slowdown = TRUE;
1362             global.fps_slowdown_factor = 2;
1363             printf("fps slowdown enabled -- display only every 2nd frame\n");
1364           }
1365           else if (global.fps_slowdown_factor == 2)
1366           {
1367             global.fps_slowdown_factor = 4;
1368             printf("fps slowdown enabled -- display only every 4th frame\n");
1369           }
1370           else
1371           {
1372             global.fps_slowdown = FALSE;
1373             global.fps_slowdown_factor = 1;
1374             printf("fps slowdown disabled\n");
1375           }
1376           break;
1377
1378         case KSYM_f:
1379           ScrollStepSize = TILEX / 8;
1380           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1381           break;
1382
1383         case KSYM_g:
1384           ScrollStepSize = TILEX / 4;
1385           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1386           break;
1387
1388         case KSYM_h:
1389           ScrollStepSize = TILEX / 2;
1390           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1391           break;
1392
1393         case KSYM_l:
1394           ScrollStepSize = TILEX;
1395           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1396           break;
1397
1398         case KSYM_v:
1399           printf("::: currently using game engine version %d\n",
1400                  game.engine_version);
1401           break;
1402 #endif
1403
1404         default:
1405           break;
1406       }
1407       break;
1408     }
1409
1410     default:
1411       if (key == KSYM_Escape)
1412       {
1413         game_status = GAME_MODE_MAIN;
1414         DrawMainMenu();
1415
1416         return;
1417       }
1418   }
1419 }
1420
1421 void HandleNoEvent()
1422 {
1423   if (button_status && game_status != GAME_MODE_PLAYING)
1424   {
1425     HandleButton(0, 0, -button_status, button_status);
1426
1427 #if 0
1428     return;
1429 #endif
1430   }
1431   else
1432   {
1433     HandleJoystick();
1434   }
1435
1436 #if defined(NETWORK_AVALIABLE)
1437   if (options.network)
1438     HandleNetworking();
1439 #endif
1440
1441   switch (game_status)
1442   {
1443     case GAME_MODE_MAIN:
1444       DrawPreviewLevelAnimation();
1445       DoAnimation();
1446       break;
1447
1448     case GAME_MODE_LEVELS:
1449     case GAME_MODE_LEVELNR:
1450     case GAME_MODE_SETUP:
1451     case GAME_MODE_INFO:
1452     case GAME_MODE_SCORES:
1453       DoAnimation();
1454       break;
1455
1456     case GAME_MODE_EDITOR:
1457       HandleLevelEditorIdle();
1458       break;
1459
1460     default:
1461       break;
1462   }
1463 }
1464
1465 static int HandleJoystickForAllPlayers()
1466 {
1467   int i;
1468   int result = 0;
1469
1470   for (i = 0; i < MAX_PLAYERS; i++)
1471   {
1472     byte joy_action = 0;
1473
1474     /*
1475     if (!setup.input[i].use_joystick)
1476       continue;
1477       */
1478
1479     joy_action = Joystick(i);
1480     result |= joy_action;
1481
1482     if (!setup.input[i].use_joystick)
1483       continue;
1484
1485     stored_player[i].action = joy_action;
1486   }
1487
1488   return result;
1489 }
1490
1491 void HandleJoystick()
1492 {
1493   int joystick  = HandleJoystickForAllPlayers();
1494   int keyboard  = key_joystick_mapping;
1495   int joy       = (joystick | keyboard);
1496   int left      = joy & JOY_LEFT;
1497   int right     = joy & JOY_RIGHT;
1498   int up        = joy & JOY_UP;
1499   int down      = joy & JOY_DOWN;
1500   int button    = joy & JOY_BUTTON;
1501   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1502   int dx        = (left ? -1    : right ? 1     : 0);
1503   int dy        = (up   ? -1    : down  ? 1     : 0);
1504
1505   switch (game_status)
1506   {
1507     case GAME_MODE_TITLE:
1508     case GAME_MODE_MAIN:
1509     case GAME_MODE_LEVELS:
1510     case GAME_MODE_LEVELNR:
1511     case GAME_MODE_SETUP:
1512     case GAME_MODE_INFO:
1513     {
1514       static unsigned int joystickmove_delay = 0;
1515
1516       if (joystick && !button &&
1517           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1518         newbutton = dx = dy = 0;
1519
1520       if (game_status == GAME_MODE_TITLE)
1521         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1522       else if (game_status == GAME_MODE_MAIN)
1523         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1524       else if (game_status == GAME_MODE_LEVELS)
1525         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1526       else if (game_status == GAME_MODE_LEVELNR)
1527         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1528       else if (game_status == GAME_MODE_SETUP)
1529         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1530       else if (game_status == GAME_MODE_INFO)
1531         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1532       break;
1533     }
1534
1535     case GAME_MODE_SCORES:
1536       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1537       break;
1538
1539 #if 0
1540     case GAME_MODE_EDITOR:
1541       HandleLevelEditorIdle();
1542       break;
1543 #endif
1544
1545     case GAME_MODE_PLAYING:
1546       if (tape.playing || keyboard)
1547         newbutton = ((joy & JOY_BUTTON) != 0);
1548
1549 #if 0
1550       if (newbutton && local_player->LevelSolved_GameEnd)
1551 #else
1552       if (newbutton && AllPlayersGone)
1553 #endif
1554       {
1555         GameEnd();
1556
1557         return;
1558       }
1559
1560       break;
1561
1562     default:
1563       break;
1564   }
1565 }