f428eeb316f5b49587097ec58240fecbf4a69eb9
[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       HandleNoEvent();
210     }
211
212     /* don't use all CPU time when idle; the main loop while playing
213        has its own synchronization and is CPU friendly, too */
214
215     if (game_status == GAME_MODE_PLAYING)
216     {
217       HandleGameActions();
218     }
219     else
220     {
221       SyncDisplay();
222       if (!PendingEvent())      /* delay only if no pending events */
223         Delay(10);
224     }
225
226     /* refresh window contents from drawing buffer, if needed */
227     BackToFront();
228
229     if (game_status == GAME_MODE_QUIT)
230       return;
231   }
232 }
233
234 void HandleOtherEvents(Event *event)
235 {
236   switch (event->type)
237   {
238     case EVENT_EXPOSE:
239       HandleExposeEvent((ExposeEvent *) event);
240       break;
241
242     case EVENT_UNMAPNOTIFY:
243 #if 0
244       /* This causes the game to stop not only when iconified, but also
245          when on another virtual desktop, which might be not desired. */
246       SleepWhileUnmapped();
247 #endif
248       break;
249
250     case EVENT_FOCUSIN:
251     case EVENT_FOCUSOUT:
252       HandleFocusEvent((FocusChangeEvent *) event);
253       break;
254
255     case EVENT_CLIENTMESSAGE:
256       HandleClientMessageEvent((ClientMessageEvent *) event);
257       break;
258
259 #if defined(TARGET_SDL)
260     case SDL_JOYAXISMOTION:
261     case SDL_JOYBUTTONDOWN:
262     case SDL_JOYBUTTONUP:
263       HandleJoystickEvent(event);
264       break;
265
266     case SDL_SYSWMEVENT:
267       HandleWindowManagerEvent(event);
268       break;
269 #endif
270
271     default:
272       break;
273   }
274 }
275
276 void ClearEventQueue()
277 {
278   while (PendingEvent())
279   {
280     Event event;
281
282     NextEvent(&event);
283
284     switch (event.type)
285     {
286       case EVENT_BUTTONRELEASE:
287         button_status = MB_RELEASED;
288         break;
289
290       case EVENT_KEYRELEASE:
291 #if 1
292         ClearPlayerAction();
293 #else
294         key_joystick_mapping = 0;
295 #endif
296         break;
297
298       default:
299         HandleOtherEvents(&event);
300         break;
301     }
302   }
303 }
304
305 void ClearPlayerAction()
306 {
307   int i;
308
309   /* simulate key release events for still pressed keys */
310   key_joystick_mapping = 0;
311   for (i = 0; i < MAX_PLAYERS; i++)
312     stored_player[i].action = 0;
313 }
314
315 void SleepWhileUnmapped()
316 {
317   boolean window_unmapped = TRUE;
318
319   KeyboardAutoRepeatOn();
320
321   while (window_unmapped)
322   {
323     Event event;
324
325     NextEvent(&event);
326
327     switch (event.type)
328     {
329       case EVENT_BUTTONRELEASE:
330         button_status = MB_RELEASED;
331         break;
332
333       case EVENT_KEYRELEASE:
334         key_joystick_mapping = 0;
335         break;
336
337       case EVENT_MAPNOTIFY:
338         window_unmapped = FALSE;
339         break;
340
341       case EVENT_UNMAPNOTIFY:
342         /* this is only to surely prevent the 'should not happen' case
343          * of recursively looping between 'SleepWhileUnmapped()' and
344          * 'HandleOtherEvents()' which usually calls this funtion.
345          */
346         break;
347
348       default:
349         HandleOtherEvents(&event);
350         break;
351     }
352   }
353
354   if (game_status == GAME_MODE_PLAYING)
355     KeyboardAutoRepeatOffUnlessAutoplay();
356 }
357
358 void HandleExposeEvent(ExposeEvent *event)
359 {
360 #if !defined(TARGET_SDL)
361   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
362   FlushDisplay();
363 #endif
364 }
365
366 void HandleButtonEvent(ButtonEvent *event)
367 {
368 #if DEBUG_EVENTS
369   Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
370         event->button,
371         event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
372         event->x, event->y);
373 #endif
374
375   motion_status = FALSE;
376
377   if (event->type == EVENT_BUTTONPRESS)
378     button_status = event->button;
379   else
380     button_status = MB_RELEASED;
381
382   HandleButton(event->x, event->y, button_status, event->button);
383 }
384
385 void HandleMotionEvent(MotionEvent *event)
386 {
387   if (!PointerInWindow(window))
388     return;     /* window and pointer are on different screens */
389
390   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
391     return;
392
393   motion_status = TRUE;
394
395 #if DEBUG_EVENTS
396   Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
397         button_status, event->x, event->y);
398 #endif
399
400   HandleButton(event->x, event->y, button_status, button_status);
401 }
402
403 #if defined(TARGET_SDL2)
404 void HandleWindowEvent(WindowEvent *event)
405 {
406 #if DEBUG_EVENTS
407   int subtype = event->event;
408
409   char *event_name =
410     (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
411      subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
412      subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
413      subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
414      subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
415      subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
416      subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
417      subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
418      subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
419      subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
420      subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
421      subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
422      subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
423      subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
424      "(UNKNOWN)");
425
426   Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
427         event_name, event->data1, event->data2);
428 #endif
429
430   if (event->event == SDL_WINDOWEVENT_EXPOSED)
431     SDLRedrawWindow();
432
433 #if 0
434   if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
435   {
436     // if game started in fullscreen mode, window will also get fullscreen size
437     if (!video.fullscreen_enabled && video.fullscreen_initial)
438     {
439       SDLSetWindowScaling(setup.window_scaling_percent);
440
441       // only do this correction once
442       video.fullscreen_initial = FALSE;
443     }
444   }
445 #endif
446
447   if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
448   {
449 #if 1
450     int new_window_width  = event->data1;
451     int new_window_height = event->data2;
452
453     printf("::: RESIZED from %d, %d to %d, %d\n",
454            video.window_width, video.window_height,
455            new_window_width, new_window_height);
456
457     // if window size has changed after resizing, calculate new scaling factor
458     if (new_window_width  != video.window_width ||
459         new_window_height != video.window_height)
460     {
461       int new_xpercent = (100 * new_window_width  / video.width);
462       int new_ypercent = (100 * new_window_height / video.height);
463
464       setup.window_scaling_percent = video.window_scaling_percent =
465         MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
466             MAX_WINDOW_SCALING_PERCENT);
467
468       video.window_width  = new_window_width;
469       video.window_height = new_window_height;
470
471       printf("::: setup.window_scaling_percent set to %d\n",
472              setup.window_scaling_percent);
473
474       if (game_status == GAME_MODE_SETUP)
475         RedrawSetupScreenAfterFullscreenToggle();
476     }
477 #else
478     // prevent slightly wrong scaling factor due to rounding differences
479     float scaling_factor = (float)setup.window_scaling_percent / 100;
480     int old_xsize = (int)(scaling_factor * video.width);
481     int old_ysize = (int)(scaling_factor * video.height);
482     int new_xsize = event->data1;
483     int new_ysize = event->data2;
484
485     // window size is unchanged when going from fullscreen to window mode,
486     // but reverse calculation of scaling factor might result in a scaling
487     // factor that is slightly different due to rounding differences;
488     // therefore compare old/new window size and not old/new scaling factor
489     if (old_xsize != new_xsize ||
490         old_ysize != new_ysize)
491     {
492       int new_xpercent = (100 * new_xsize / video.width);
493       int new_ypercent = (100 * new_ysize / video.height);
494
495       setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
496
497       if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
498         setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
499       else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
500         setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
501
502       printf("::: setup.window_scaling_percent set to %d\n",
503              setup.window_scaling_percent);
504     }
505 #endif
506   }
507 }
508
509 void HandleFingerEvent(FingerEvent *event)
510 {
511 #if 0
512   static int num_events = 0;
513   int max_events = 10;
514 #endif
515
516 #if DEBUG_EVENTS
517   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
518         event->type == EVENT_FINGERPRESS ? "pressed" :
519         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
520         event->touchId,
521         event->fingerId,
522         event->x, event->y,
523         event->dx, event->dy,
524         event->pressure);
525 #endif
526
527 #if 0
528   int x = (int)(event->x * video.width);
529   int y = (int)(event->y * video.height);
530   int button = MB_LEFTBUTTON;
531
532   Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
533 #endif
534
535 #if 0
536   if (++num_events >= max_events)
537     CloseAllAndExit(0);
538 #endif
539
540 #if 1
541 #if 0
542   if (event->type == EVENT_FINGERPRESS ||
543       event->type == EVENT_FINGERMOTION)
544     button_status = button;
545   else
546     button_status = MB_RELEASED;
547
548   int max_x = SX + SXSIZE;
549   int max_y = SY + SYSIZE;
550 #endif
551
552 #if 1
553   if (game_status == GAME_MODE_PLAYING)
554 #else
555   if (game_status == GAME_MODE_PLAYING &&
556       x < max_x)
557 #endif
558   {
559     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
560                       KEY_PRESSED);
561 #if 1
562     Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
563                event->y > 2.0 / 3.0 ? setup.input[0].key.down :
564                event->x < 1.0 / 3.0 ? setup.input[0].key.left :
565                event->x > 2.0 / 3.0 ? setup.input[0].key.right :
566                setup.input[0].key.drop);
567 #else
568     Key key = (y <     max_y / 3 ? setup.input[0].key.up :
569                y > 2 * max_y / 3 ? setup.input[0].key.down :
570                x <     max_x / 3 ? setup.input[0].key.left :
571                x > 2 * max_x / 3 ? setup.input[0].key.right :
572                setup.input[0].key.drop);
573 #endif
574
575     Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
576
577     HandleKey(key, key_status);
578   }
579   else
580   {
581 #if 0
582     Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
583           button_status, button);
584
585     HandleButton(x, y, button_status, button);
586 #endif
587   }
588 #endif
589 }
590
591 static boolean checkTextInputKeyModState()
592 {
593   // when playing, only handle raw key events and ignore text input
594   if (game_status == GAME_MODE_PLAYING)
595     return FALSE;
596
597   return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
598 }
599
600 void HandleTextEvent(TextEvent *event)
601 {
602   char *text = event->text;
603   Key key = getKeyFromKeyName(text);
604
605 #if DEBUG_EVENTS
606   Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s)",
607         text,
608         strlen(text),
609         text[0], (int)(text[0]),
610         key,
611         getKeyNameFromKey(key));
612 #endif
613
614   // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
615   /*
616   if (game_status != GAME_MODE_PLAYING &&
617       (GetKeyModState() & KMOD_TextInput) != KMOD_None)
618   */
619   if (checkTextInputKeyModState())
620   {
621     HandleKey(key, KEY_PRESSED);
622     HandleKey(key, KEY_RELEASED);
623   }
624 }
625 #endif
626
627 void HandleKeyEvent(KeyEvent *event)
628 {
629   int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
630   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
631   Key key = GetEventKey(event, with_modifiers);
632   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
633
634 #if DEBUG_EVENTS
635   Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
636         event->type == EVENT_KEYPRESS ? "pressed" : "released",
637         event->keysym.scancode,
638         event->keysym.sym,
639         keymod,
640         GetKeyModState(),
641         key,
642         getKeyNameFromKey(key));
643 #endif
644
645 #if 0
646   if (key == KSYM_Menu)
647     Error(ERR_DEBUG, "menu key pressed");
648   else if (key == KSYM_Back)
649     Error(ERR_DEBUG, "back key pressed");
650 #endif
651
652 #if defined(PLATFORM_ANDROID)
653   // always map the "back" button to the "escape" key on Android devices
654   if (key == KSYM_Back)
655     key = KSYM_Escape;
656 #endif
657
658   HandleKeyModState(keymod, key_status);
659
660 #if defined(TARGET_SDL2)
661
662   // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
663   /*
664   if (game_status == GAME_MODE_PLAYING ||
665       (GetKeyModState() & KMOD_TextInput) == KMOD_None)
666   */
667   if (!checkTextInputKeyModState())
668     HandleKey(key, key_status);
669 #else
670   HandleKey(key, key_status);
671 #endif
672 }
673
674 void HandleFocusEvent(FocusChangeEvent *event)
675 {
676   static int old_joystick_status = -1;
677
678   if (event->type == EVENT_FOCUSOUT)
679   {
680     KeyboardAutoRepeatOn();
681     old_joystick_status = joystick.status;
682     joystick.status = JOYSTICK_NOT_AVAILABLE;
683
684     ClearPlayerAction();
685   }
686   else if (event->type == EVENT_FOCUSIN)
687   {
688     /* When there are two Rocks'n'Diamonds windows which overlap and
689        the player moves the pointer from one game window to the other,
690        a 'FocusOut' event is generated for the window the pointer is
691        leaving and a 'FocusIn' event is generated for the window the
692        pointer is entering. In some cases, it can happen that the
693        'FocusIn' event is handled by the one game process before the
694        'FocusOut' event by the other game process. In this case the
695        X11 environment would end up with activated keyboard auto repeat,
696        because unfortunately this is a global setting and not (which
697        would be far better) set for each X11 window individually.
698        The effect would be keyboard auto repeat while playing the game
699        (game_status == GAME_MODE_PLAYING), which is not desired.
700        To avoid this special case, we just wait 1/10 second before
701        processing the 'FocusIn' event.
702     */
703
704     if (game_status == GAME_MODE_PLAYING)
705     {
706       Delay(100);
707       KeyboardAutoRepeatOffUnlessAutoplay();
708     }
709
710     if (old_joystick_status != -1)
711       joystick.status = old_joystick_status;
712   }
713 }
714
715 void HandleClientMessageEvent(ClientMessageEvent *event)
716 {
717   if (CheckCloseWindowEvent(event))
718     CloseAllAndExit(0);
719 }
720
721 void HandleWindowManagerEvent(Event *event)
722 {
723 #if defined(TARGET_SDL)
724   SDLHandleWindowManagerEvent(event);
725 #endif
726 }
727
728 void HandleButton(int mx, int my, int button, int button_nr)
729 {
730   static int old_mx = 0, old_my = 0;
731
732   if (button < 0)
733   {
734     mx = old_mx;
735     my = old_my;
736     button = -button;
737   }
738   else
739   {
740     old_mx = mx;
741     old_my = my;
742   }
743
744   if (HandleGadgets(mx, my, button))
745   {
746     /* do not handle this button event anymore */
747     mx = my = -32;      /* force mouse event to be outside screen tiles */
748   }
749
750   /* do not use scroll wheel button events for anything other than gadgets */
751   if (IS_WHEEL_BUTTON(button_nr))
752     return;
753
754 #if 0
755   Error(ERR_DEBUG, "::: game_status == %d", game_status);
756 #endif
757
758   switch (game_status)
759   {
760     case GAME_MODE_TITLE:
761       HandleTitleScreen(mx, my, 0, 0, button);
762       break;
763
764     case GAME_MODE_MAIN:
765       HandleMainMenu(mx, my, 0, 0, button);
766       break;
767
768     case GAME_MODE_PSEUDO_TYPENAME:
769       HandleTypeName(0, KSYM_Return);
770       break;
771
772     case GAME_MODE_LEVELS:
773       HandleChooseLevelSet(mx, my, 0, 0, button);
774       break;
775
776     case GAME_MODE_LEVELNR:
777       HandleChooseLevelNr(mx, my, 0, 0, button);
778       break;
779
780     case GAME_MODE_SCORES:
781       HandleHallOfFame(0, 0, 0, 0, button);
782       break;
783
784     case GAME_MODE_EDITOR:
785       HandleLevelEditorIdle();
786       break;
787
788     case GAME_MODE_INFO:
789       HandleInfoScreen(mx, my, 0, 0, button);
790       break;
791
792     case GAME_MODE_SETUP:
793       HandleSetupScreen(mx, my, 0, 0, button);
794       break;
795
796     case GAME_MODE_PLAYING:
797 #ifdef DEBUG
798       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
799         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
800 #endif
801       break;
802
803     default:
804       break;
805   }
806 }
807
808 static boolean is_string_suffix(char *string, char *suffix)
809 {
810   int string_len = strlen(string);
811   int suffix_len = strlen(suffix);
812
813   if (suffix_len > string_len)
814     return FALSE;
815
816   return (strEqual(&string[string_len - suffix_len], suffix));
817 }
818
819 #define MAX_CHEAT_INPUT_LEN     32
820
821 static void HandleKeysSpecial(Key key)
822 {
823   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
824   char letter = getCharFromKey(key);
825   int cheat_input_len = strlen(cheat_input);
826   int i;
827
828   if (letter == 0)
829     return;
830
831   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
832   {
833     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
834       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
835
836     cheat_input_len = MAX_CHEAT_INPUT_LEN;
837   }
838
839   cheat_input[cheat_input_len++] = letter;
840   cheat_input[cheat_input_len] = '\0';
841
842 #if DEBUG_EVENTS
843   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
844 #endif
845
846   if (game_status == GAME_MODE_MAIN)
847   {
848     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
849         is_string_suffix(cheat_input, ":ist"))
850     {
851       InsertSolutionTape();
852     }
853     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
854              is_string_suffix(cheat_input, ":rg"))
855     {
856       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
857       DrawMainMenu();
858     }
859     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
860              is_string_suffix(cheat_input, ":rs"))
861     {
862       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
863       DrawMainMenu();
864     }
865     else if (is_string_suffix(cheat_input, ":reload-music") ||
866              is_string_suffix(cheat_input, ":rm"))
867     {
868       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
869       DrawMainMenu();
870     }
871     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
872              is_string_suffix(cheat_input, ":ra"))
873     {
874       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
875                           1 << ARTWORK_TYPE_SOUNDS |
876                           1 << ARTWORK_TYPE_MUSIC);
877       DrawMainMenu();
878     }
879     else if (is_string_suffix(cheat_input, ":dump-level") ||
880              is_string_suffix(cheat_input, ":dl"))
881     {
882       DumpLevel(&level);
883     }
884     else if (is_string_suffix(cheat_input, ":dump-tape") ||
885              is_string_suffix(cheat_input, ":dt"))
886     {
887       DumpTape(&tape);
888     }
889     else if (is_string_suffix(cheat_input, ":save-native-level") ||
890              is_string_suffix(cheat_input, ":snl"))
891     {
892       SaveNativeLevel(&level);
893     }
894   }
895   else if (game_status == GAME_MODE_PLAYING)
896   {
897 #ifdef DEBUG
898     if (is_string_suffix(cheat_input, ".q"))
899       DEBUG_SetMaximumDynamite();
900 #endif
901   }
902   else if (game_status == GAME_MODE_EDITOR)
903   {
904     if (is_string_suffix(cheat_input, ":dump-brush") ||
905         is_string_suffix(cheat_input, ":DB"))
906     {
907       DumpBrush();
908     }
909     else if (is_string_suffix(cheat_input, ":DDB"))
910     {
911       DumpBrush_Small();
912     }
913   }
914 }
915
916 void HandleKey(Key key, int key_status)
917 {
918   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
919   static struct SetupKeyboardInfo ski;
920   static struct SetupShortcutInfo ssi;
921   static struct
922   {
923     Key *key_custom;
924     Key *key_snap;
925     Key key_default;
926     byte action;
927   } key_info[] =
928   {
929     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
930     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
931     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
932     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
933     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
934     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
935   };
936   int joy = 0;
937   int i;
938
939   if (game_status == GAME_MODE_PLAYING)
940   {
941     /* only needed for single-step tape recording mode */
942     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
943     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
944     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
945     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
946     int pnr;
947
948     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
949     {
950       byte key_action = 0;
951
952       if (setup.input[pnr].use_joystick)
953         continue;
954
955       ski = setup.input[pnr].key;
956
957       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
958         if (key == *key_info[i].key_custom)
959           key_action |= key_info[i].action;
960
961       /* use combined snap+direction keys for the first player only */
962       if (pnr == 0)
963       {
964         ssi = setup.shortcut;
965
966         for (i = 0; i < NUM_DIRECTIONS; i++)
967           if (key == *key_info[i].key_snap)
968             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
969       }
970
971       /* clear delayed snap and drop actions in single step mode (see below) */
972       if (tape.single_step)
973       {
974         if (clear_snap_button[pnr])
975         {
976           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
977           clear_snap_button[pnr] = FALSE;
978         }
979
980         if (clear_drop_button[pnr])
981         {
982           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
983           clear_drop_button[pnr] = FALSE;
984         }
985       }
986
987       if (key_status == KEY_PRESSED)
988         stored_player[pnr].action |= key_action;
989       else
990         stored_player[pnr].action &= ~key_action;
991
992       if (tape.single_step && tape.recording && tape.pausing)
993       {
994         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
995         {
996           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
997
998           /* if snap key already pressed, don't snap when releasing (below) */
999           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1000             element_snapped[pnr] = TRUE;
1001
1002           /* if drop key already pressed, don't drop when releasing (below) */
1003           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1004             element_dropped[pnr] = TRUE;
1005         }
1006 #if 1
1007         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1008         {
1009           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1010               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1011           {
1012 #if 0
1013             printf("::: drop key pressed\n");
1014 #endif
1015
1016             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1017                 getRedDiskReleaseFlag_SP() == 0)
1018               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1019
1020             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1021           }
1022         }
1023 #endif
1024         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1025         {
1026           if (key_action & KEY_BUTTON_SNAP)
1027           {
1028             /* if snap key was released without moving (see above), snap now */
1029             if (!element_snapped[pnr])
1030             {
1031               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1032
1033               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1034
1035               /* clear delayed snap button on next event */
1036               clear_snap_button[pnr] = TRUE;
1037             }
1038
1039             element_snapped[pnr] = FALSE;
1040           }
1041
1042 #if 1
1043           if (key_action & KEY_BUTTON_DROP &&
1044               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1045           {
1046             /* if drop key was released without moving (see above), drop now */
1047             if (!element_dropped[pnr])
1048             {
1049               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1050
1051               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1052                   getRedDiskReleaseFlag_SP() != 0)
1053                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1054
1055               /* clear delayed drop button on next event */
1056               clear_drop_button[pnr] = TRUE;
1057             }
1058
1059             element_dropped[pnr] = FALSE;
1060           }
1061 #endif
1062         }
1063       }
1064       else if (tape.recording && tape.pausing)
1065       {
1066         /* prevent key release events from un-pausing a paused game */
1067         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1068           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1069       }
1070     }
1071   }
1072   else
1073   {
1074     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1075       if (key == key_info[i].key_default)
1076         joy |= key_info[i].action;
1077   }
1078
1079   if (joy)
1080   {
1081     if (key_status == KEY_PRESSED)
1082       key_joystick_mapping |= joy;
1083     else
1084       key_joystick_mapping &= ~joy;
1085
1086     HandleJoystick();
1087   }
1088
1089   if (game_status != GAME_MODE_PLAYING)
1090     key_joystick_mapping = 0;
1091
1092   if (key_status == KEY_RELEASED)
1093     return;
1094
1095   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1096       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1097   {
1098     setup.fullscreen = !setup.fullscreen;
1099
1100 #if 0
1101     printf("::: %d\n", setup.window_scaling_percent);
1102 #endif
1103
1104     ToggleFullscreenIfNeeded();
1105
1106     if (game_status == GAME_MODE_SETUP)
1107       RedrawSetupScreenAfterFullscreenToggle();
1108
1109     return;
1110   }
1111
1112   if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1113       (GetKeyModState() & KMOD_Control) && video.window_scaling_available &&
1114       !video.fullscreen_enabled)
1115   {
1116     if (key == KSYM_0)
1117       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1118     else
1119       setup.window_scaling_percent +=
1120         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1121
1122     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1123       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1124     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1125       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1126
1127     ToggleFullscreenIfNeeded();
1128
1129     if (game_status == GAME_MODE_SETUP)
1130       RedrawSetupScreenAfterFullscreenToggle();
1131
1132     return;
1133   }
1134
1135 #if 0
1136   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1137       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1138 #else
1139   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1140       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1141 #endif
1142   {
1143     GameEnd();
1144
1145     return;
1146   }
1147
1148   if (game_status == GAME_MODE_MAIN &&
1149       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1150   {
1151     StartGameActions(options.network, setup.autorecord, level.random_seed);
1152
1153     return;
1154   }
1155
1156   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1157   {
1158     if (key == setup.shortcut.save_game)
1159       TapeQuickSave();
1160     else if (key == setup.shortcut.load_game)
1161       TapeQuickLoad();
1162     else if (key == setup.shortcut.toggle_pause)
1163       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1164
1165     HandleTapeButtonKeys(key);
1166     HandleSoundButtonKeys(key);
1167   }
1168
1169   if (game_status == GAME_MODE_PLAYING && !network_playing)
1170   {
1171     int centered_player_nr_next = -999;
1172
1173     if (key == setup.shortcut.focus_player_all)
1174       centered_player_nr_next = -1;
1175     else
1176       for (i = 0; i < MAX_PLAYERS; i++)
1177         if (key == setup.shortcut.focus_player[i])
1178           centered_player_nr_next = i;
1179
1180     if (centered_player_nr_next != -999)
1181     {
1182       game.centered_player_nr_next = centered_player_nr_next;
1183       game.set_centered_player = TRUE;
1184
1185       if (tape.recording)
1186       {
1187         tape.centered_player_nr_next = game.centered_player_nr_next;
1188         tape.set_centered_player = TRUE;
1189       }
1190     }
1191   }
1192
1193   HandleKeysSpecial(key);
1194
1195   if (HandleGadgetsKeyInput(key))
1196   {
1197     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1198       key = KSYM_UNDEFINED;
1199   }
1200
1201   switch (game_status)
1202   {
1203     case GAME_MODE_PSEUDO_TYPENAME:
1204       HandleTypeName(0, key);
1205       break;
1206
1207     case GAME_MODE_TITLE:
1208     case GAME_MODE_MAIN:
1209     case GAME_MODE_LEVELS:
1210     case GAME_MODE_LEVELNR:
1211     case GAME_MODE_SETUP:
1212     case GAME_MODE_INFO:
1213     case GAME_MODE_SCORES:
1214       switch (key)
1215       {
1216         case KSYM_space:
1217         case KSYM_Return:
1218           if (game_status == GAME_MODE_TITLE)
1219             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1220           else if (game_status == GAME_MODE_MAIN)
1221             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1222           else if (game_status == GAME_MODE_LEVELS)
1223             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1224           else if (game_status == GAME_MODE_LEVELNR)
1225             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1226           else if (game_status == GAME_MODE_SETUP)
1227             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1228           else if (game_status == GAME_MODE_INFO)
1229             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1230           else if (game_status == GAME_MODE_SCORES)
1231             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1232           break;
1233
1234         case KSYM_Escape:
1235           if (game_status != GAME_MODE_MAIN)
1236             FadeSkipNextFadeIn();
1237
1238           if (game_status == GAME_MODE_TITLE)
1239             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1240           else if (game_status == GAME_MODE_LEVELS)
1241             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1242           else if (game_status == GAME_MODE_LEVELNR)
1243             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1244           else if (game_status == GAME_MODE_SETUP)
1245             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1246           else if (game_status == GAME_MODE_INFO)
1247             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1248           else if (game_status == GAME_MODE_SCORES)
1249             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1250           break;
1251
1252         case KSYM_Page_Up:
1253           if (game_status == GAME_MODE_LEVELS)
1254             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1255           else if (game_status == GAME_MODE_LEVELNR)
1256             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1257           else if (game_status == GAME_MODE_SETUP)
1258             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1259           else if (game_status == GAME_MODE_INFO)
1260             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1261           else if (game_status == GAME_MODE_SCORES)
1262             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1263           break;
1264
1265         case KSYM_Page_Down:
1266           if (game_status == GAME_MODE_LEVELS)
1267             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1268           else if (game_status == GAME_MODE_LEVELNR)
1269             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1270           else if (game_status == GAME_MODE_SETUP)
1271             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1272           else if (game_status == GAME_MODE_INFO)
1273             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1274           else if (game_status == GAME_MODE_SCORES)
1275             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1276           break;
1277
1278 #ifdef DEBUG
1279         case KSYM_0:
1280           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1281           break;
1282
1283         case KSYM_b:
1284           setup.sp_show_border_elements = !setup.sp_show_border_elements;
1285           printf("Supaplex border elements %s\n",
1286                  setup.sp_show_border_elements ? "enabled" : "disabled");
1287           break;
1288 #endif
1289
1290         default:
1291           break;
1292       }
1293       break;
1294
1295     case GAME_MODE_EDITOR:
1296       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1297         HandleLevelEditorKeyInput(key);
1298       break;
1299
1300     case GAME_MODE_PLAYING:
1301     {
1302       switch (key)
1303       {
1304         case KSYM_Escape:
1305           RequestQuitGame(setup.ask_on_escape);
1306           break;
1307
1308 #ifdef DEBUG
1309         case KSYM_0:
1310 #if 0
1311         case KSYM_1:
1312         case KSYM_2:
1313         case KSYM_3:
1314         case KSYM_4:
1315         case KSYM_5:
1316         case KSYM_6:
1317         case KSYM_7:
1318         case KSYM_8:
1319         case KSYM_9:
1320 #endif
1321           if (key == KSYM_0)
1322           {
1323             if (GameFrameDelay == 500)
1324               GameFrameDelay = GAME_FRAME_DELAY;
1325             else
1326               GameFrameDelay = 500;
1327           }
1328           else
1329             GameFrameDelay = (key - KSYM_0) * 10;
1330           printf("Game speed == %d%% (%d ms delay between two frames)\n",
1331                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1332           break;
1333
1334         case KSYM_d:
1335           if (options.debug)
1336           {
1337             options.debug = FALSE;
1338             printf("debug mode disabled\n");
1339           }
1340           else
1341           {
1342             options.debug = TRUE;
1343             printf("debug mode enabled\n");
1344           }
1345           break;
1346
1347         case KSYM_s:
1348           if (!global.fps_slowdown)
1349           {
1350             global.fps_slowdown = TRUE;
1351             global.fps_slowdown_factor = 2;
1352             printf("fps slowdown enabled -- display only every 2nd frame\n");
1353           }
1354           else if (global.fps_slowdown_factor == 2)
1355           {
1356             global.fps_slowdown_factor = 4;
1357             printf("fps slowdown enabled -- display only every 4th frame\n");
1358           }
1359           else
1360           {
1361             global.fps_slowdown = FALSE;
1362             global.fps_slowdown_factor = 1;
1363             printf("fps slowdown disabled\n");
1364           }
1365           break;
1366
1367         case KSYM_f:
1368           ScrollStepSize = TILEX / 8;
1369           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1370           break;
1371
1372         case KSYM_g:
1373           ScrollStepSize = TILEX / 4;
1374           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1375           break;
1376
1377         case KSYM_h:
1378           ScrollStepSize = TILEX / 2;
1379           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1380           break;
1381
1382         case KSYM_l:
1383           ScrollStepSize = TILEX;
1384           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1385           break;
1386
1387         case KSYM_v:
1388           printf("::: currently using game engine version %d\n",
1389                  game.engine_version);
1390           break;
1391 #endif
1392
1393         default:
1394           break;
1395       }
1396       break;
1397     }
1398
1399     default:
1400       if (key == KSYM_Escape)
1401       {
1402         game_status = GAME_MODE_MAIN;
1403         DrawMainMenu();
1404
1405         return;
1406       }
1407   }
1408 }
1409
1410 void HandleNoEvent()
1411 {
1412   if (button_status && game_status != GAME_MODE_PLAYING)
1413   {
1414     HandleButton(0, 0, -button_status, button_status);
1415
1416     return;
1417   }
1418
1419 #if defined(NETWORK_AVALIABLE)
1420   if (options.network)
1421     HandleNetworking();
1422 #endif
1423
1424   HandleJoystick();
1425 }
1426
1427 static int HandleJoystickForAllPlayers()
1428 {
1429   int i;
1430   int result = 0;
1431
1432   for (i = 0; i < MAX_PLAYERS; i++)
1433   {
1434     byte joy_action = 0;
1435
1436     /*
1437     if (!setup.input[i].use_joystick)
1438       continue;
1439       */
1440
1441     joy_action = Joystick(i);
1442     result |= joy_action;
1443
1444     if (!setup.input[i].use_joystick)
1445       continue;
1446
1447     stored_player[i].action = joy_action;
1448   }
1449
1450   return result;
1451 }
1452
1453 void HandleJoystick()
1454 {
1455   int joystick  = HandleJoystickForAllPlayers();
1456   int keyboard  = key_joystick_mapping;
1457   int joy       = (joystick | keyboard);
1458   int left      = joy & JOY_LEFT;
1459   int right     = joy & JOY_RIGHT;
1460   int up        = joy & JOY_UP;
1461   int down      = joy & JOY_DOWN;
1462   int button    = joy & JOY_BUTTON;
1463   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1464   int dx        = (left ? -1    : right ? 1     : 0);
1465   int dy        = (up   ? -1    : down  ? 1     : 0);
1466
1467   switch (game_status)
1468   {
1469     case GAME_MODE_TITLE:
1470     case GAME_MODE_MAIN:
1471     case GAME_MODE_LEVELS:
1472     case GAME_MODE_LEVELNR:
1473     case GAME_MODE_SETUP:
1474     case GAME_MODE_INFO:
1475     {
1476       static unsigned int joystickmove_delay = 0;
1477
1478       if (joystick && !button &&
1479           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1480         newbutton = dx = dy = 0;
1481
1482       if (game_status == GAME_MODE_TITLE)
1483         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1484       else if (game_status == GAME_MODE_MAIN)
1485         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1486       else if (game_status == GAME_MODE_LEVELS)
1487         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1488       else if (game_status == GAME_MODE_LEVELNR)
1489         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1490       else if (game_status == GAME_MODE_SETUP)
1491         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1492       else if (game_status == GAME_MODE_INFO)
1493         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1494       break;
1495     }
1496
1497     case GAME_MODE_SCORES:
1498       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1499       break;
1500
1501     case GAME_MODE_EDITOR:
1502       HandleLevelEditorIdle();
1503       break;
1504
1505     case GAME_MODE_PLAYING:
1506       if (tape.playing || keyboard)
1507         newbutton = ((joy & JOY_BUTTON) != 0);
1508
1509 #if 0
1510       if (local_player->LevelSolved_GameEnd && newbutton)
1511 #else
1512       if (AllPlayersGone && newbutton)
1513 #endif
1514       {
1515         GameEnd();
1516
1517         return;
1518       }
1519
1520       break;
1521
1522     default:
1523       break;
1524   }
1525 }