rnd-20140108-1-src
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * events.c                                                 *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "events.h"
17 #include "init.h"
18 #include "screens.h"
19 #include "tools.h"
20 #include "game.h"
21 #include "editor.h"
22 #include "files.h"
23 #include "tape.h"
24 #include "network.h"
25
26
27 #define DEBUG_EVENTS            0
28
29
30 static boolean cursor_inside_playfield = FALSE;
31 static boolean playfield_cursor_set = FALSE;
32 static unsigned int playfield_cursor_delay = 0;
33
34
35 /* event filter especially needed for SDL event filtering due to
36    delay problems with lots of mouse motion events when mouse button
37    not pressed (X11 can handle this with 'PointerMotionHintMask') */
38
39 /* event filter addition for SDL2: as SDL2 does not have a function to enable
40    or disable keyboard auto-repeat, filter repeated keyboard events instead */
41
42 static int FilterEventsExt(const Event *event)
43 {
44   MotionEvent *motion;
45
46 #if defined(TARGET_SDL2)
47   /* skip repeated key press events if keyboard auto-repeat is disabled */
48   if (event->type == EVENT_KEYPRESS &&
49       event->key.repeat &&
50       !keyrepeat_status)
51     return 0;
52 #endif
53
54   /* non-motion events are directly passed to event handler functions */
55   if (event->type != EVENT_MOTIONNOTIFY)
56     return 1;
57
58   motion = (MotionEvent *)event;
59   cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
60                              motion->y >= SY && motion->y < SY + SYSIZE);
61
62   if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
63   {
64     SetMouseCursor(CURSOR_DEFAULT);
65     playfield_cursor_set = FALSE;
66     DelayReached(&playfield_cursor_delay, 0);
67   }
68
69   /* skip mouse motion events without pressed button outside level editor */
70   if (button_status == MB_RELEASED &&
71       game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
72     return 0;
73
74   return 1;
75 }
76
77 #if defined(TARGET_SDL2)
78 int FilterEvents(void *userdata, Event *event)
79 {
80   return FilterEventsExt(event);
81 }
82 #else
83 int FilterEvents(const Event *event)
84 {
85   return FilterEventsExt(event);
86 }
87 #endif
88
89 /* to prevent delay problems, skip mouse motion events if the very next
90    event is also a mouse motion event (and therefore effectively only
91    handling the last of a row of mouse motion events in the event queue) */
92
93 boolean SkipPressedMouseMotionEvent(const Event *event)
94 {
95   /* nothing to do if the current event is not a mouse motion event */
96   if (event->type != EVENT_MOTIONNOTIFY)
97     return FALSE;
98
99   /* only skip motion events with pressed button outside level editor */
100   if (button_status == MB_RELEASED ||
101       game_status == GAME_MODE_EDITOR || game_status == GAME_MODE_PLAYING)
102     return FALSE;
103
104   if (PendingEvent())
105   {
106     Event next_event;
107
108     PeekEvent(&next_event);
109
110     /* if next event is also a mouse motion event, skip the current one */
111     if (next_event.type == EVENT_MOTIONNOTIFY)
112       return TRUE;
113   }
114
115   return FALSE;
116 }
117
118 /* this is only really needed for non-SDL targets to filter unwanted events;
119    when using SDL with properly installed event filter, this function can be
120    replaced with a simple "NextEvent()" call, but it doesn't hurt either */
121
122 static boolean NextValidEvent(Event *event)
123 {
124   while (PendingEvent())
125   {
126     boolean handle_this_event = FALSE;
127
128     NextEvent(event);
129
130     if (FilterEventsExt(event))
131       handle_this_event = TRUE;
132
133     if (SkipPressedMouseMotionEvent(event))
134       handle_this_event = FALSE;
135
136     if (handle_this_event)
137       return TRUE;
138   }
139
140   return FALSE;
141 }
142
143 void EventLoop(void)
144 {
145   while (1)
146   {
147     if (PendingEvent())         /* got event */
148     {
149       Event event;
150
151       while (NextValidEvent(&event))
152       {
153         switch (event.type)
154         {
155           case EVENT_BUTTONPRESS:
156           case EVENT_BUTTONRELEASE:
157             HandleButtonEvent((ButtonEvent *) &event);
158             break;
159   
160           case EVENT_MOTIONNOTIFY:
161             HandleMotionEvent((MotionEvent *) &event);
162             break;
163
164 #if defined(TARGET_SDL2)
165           case SDL_WINDOWEVENT:
166             HandleWindowEvent((WindowEvent *) &event);
167             break;
168
169           case EVENT_FINGERPRESS:
170           case EVENT_FINGERRELEASE:
171           case EVENT_FINGERMOTION:
172             HandleFingerEvent((FingerEvent *) &event);
173             break;
174
175           case EVENT_TEXTINPUT:
176             HandleTextEvent((TextEvent *) &event);
177             break;
178 #endif
179
180           case EVENT_KEYPRESS:
181           case EVENT_KEYRELEASE:
182             HandleKeyEvent((KeyEvent *) &event);
183             break;
184
185           default:
186             HandleOtherEvents(&event);
187             break;
188         }
189       }
190     }
191     else
192     {
193       /* when playing, display a special mouse pointer inside the playfield */
194       if (game_status == GAME_MODE_PLAYING && !tape.pausing)
195       {
196         if (!playfield_cursor_set && cursor_inside_playfield &&
197             DelayReached(&playfield_cursor_delay, 1000))
198         {
199           SetMouseCursor(CURSOR_PLAYFIELD);
200           playfield_cursor_set = TRUE;
201         }
202       }
203       else if (playfield_cursor_set)
204       {
205         SetMouseCursor(CURSOR_DEFAULT);
206         playfield_cursor_set = FALSE;
207       }
208
209       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   int subtype = event->event;
407
408   char *event_name =
409     (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
410      subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
411      subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
412      subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
413      subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
414      subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
415      subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
416      subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
417      subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
418      subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
419      subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
420      subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
421      subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
422      subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
423      "(UNKNOWN)");
424
425   Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
426         event_name, event->data1, event->data2);
427
428   if (event->event == SDL_WINDOWEVENT_EXPOSED)
429     SDLRedrawWindow();
430
431 #if 0
432   if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
433   {
434     // if game started in fullscreen mode, window will also get fullscreen size
435     if (!video.fullscreen_enabled && video.fullscreen_initial)
436     {
437       SDLSetWindowScaling(setup.window_scaling_percent);
438
439       // only do this correction once
440       video.fullscreen_initial = FALSE;
441     }
442   }
443 #endif
444
445   if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
446   {
447 #if 1
448     int new_window_width  = event->data1;
449     int new_window_height = event->data2;
450
451     printf("::: RESIZED from %d, %d to %d, %d\n",
452            video.window_width, video.window_height,
453            new_window_width, new_window_height);
454
455     // if window size has changed after resizing, calculate new scaling factor
456     if (new_window_width  != video.window_width ||
457         new_window_height != video.window_height)
458     {
459       int new_xpercent = (100 * new_window_width  / video.width);
460       int new_ypercent = (100 * new_window_height / video.height);
461
462       setup.window_scaling_percent = video.window_scaling_percent =
463         MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
464             MAX_WINDOW_SCALING_PERCENT);
465
466       video.window_width  = new_window_width;
467       video.window_height = new_window_height;
468
469       printf("::: setup.window_scaling_percent set to %d\n",
470              setup.window_scaling_percent);
471     }
472 #else
473     // prevent slightly wrong scaling factor due to rounding differences
474     float scaling_factor = (float)setup.window_scaling_percent / 100;
475     int old_xsize = (int)(scaling_factor * video.width);
476     int old_ysize = (int)(scaling_factor * video.height);
477     int new_xsize = event->data1;
478     int new_ysize = event->data2;
479
480     // window size is unchanged when going from fullscreen to window mode,
481     // but reverse calculation of scaling factor might result in a scaling
482     // factor that is slightly different due to rounding differences;
483     // therefore compare old/new window size and not old/new scaling factor
484     if (old_xsize != new_xsize ||
485         old_ysize != new_ysize)
486     {
487       int new_xpercent = (100 * new_xsize / video.width);
488       int new_ypercent = (100 * new_ysize / video.height);
489
490       setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
491
492       if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
493         setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
494       else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
495         setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
496
497       printf("::: setup.window_scaling_percent set to %d\n",
498              setup.window_scaling_percent);
499     }
500 #endif
501   }
502 }
503
504 void HandleFingerEvent(FingerEvent *event)
505 {
506 #if 0
507   static int num_events = 0;
508   int max_events = 10;
509 #endif
510
511 #if DEBUG_EVENTS
512   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
513         event->type == EVENT_FINGERPRESS ? "pressed" :
514         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
515         event->touchId,
516         event->fingerId,
517         event->x, event->y,
518         event->dx, event->dy,
519         event->pressure);
520 #endif
521
522 #if 0
523   int x = (int)(event->x * video.width);
524   int y = (int)(event->y * video.height);
525   int button = MB_LEFTBUTTON;
526
527   Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
528 #endif
529
530 #if 0
531   if (++num_events >= max_events)
532     CloseAllAndExit(0);
533 #endif
534
535 #if 1
536 #if 0
537   if (event->type == EVENT_FINGERPRESS ||
538       event->type == EVENT_FINGERMOTION)
539     button_status = button;
540   else
541     button_status = MB_RELEASED;
542
543   int max_x = SX + SXSIZE;
544   int max_y = SY + SYSIZE;
545 #endif
546
547 #if 1
548   if (game_status == GAME_MODE_PLAYING)
549 #else
550   if (game_status == GAME_MODE_PLAYING &&
551       x < max_x)
552 #endif
553   {
554     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
555                       KEY_PRESSED);
556 #if 1
557     Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
558                event->y > 2.0 / 3.0 ? setup.input[0].key.down :
559                event->x < 1.0 / 3.0 ? setup.input[0].key.left :
560                event->x > 2.0 / 3.0 ? setup.input[0].key.right :
561                setup.input[0].key.drop);
562 #else
563     Key key = (y <     max_y / 3 ? setup.input[0].key.up :
564                y > 2 * max_y / 3 ? setup.input[0].key.down :
565                x <     max_x / 3 ? setup.input[0].key.left :
566                x > 2 * max_x / 3 ? setup.input[0].key.right :
567                setup.input[0].key.drop);
568 #endif
569
570     Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
571
572     HandleKey(key, key_status);
573   }
574   else
575   {
576 #if 0
577     Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
578           button_status, button);
579
580     HandleButton(x, y, button_status, button);
581 #endif
582   }
583 #endif
584 }
585
586 static boolean checkTextInputKeyModState()
587 {
588   // when playing, only handle raw key events and ignore text input
589   if (game_status == GAME_MODE_PLAYING)
590     return FALSE;
591
592   return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
593 }
594
595 void HandleTextEvent(TextEvent *event)
596 {
597   char *text = event->text;
598   Key key = getKeyFromKeyName(text);
599
600 #if DEBUG_EVENTS
601   Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s)",
602         text,
603         strlen(text),
604         text[0], (int)(text[0]),
605         key,
606         getKeyNameFromKey(key));
607 #endif
608
609   // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
610   /*
611   if (game_status != GAME_MODE_PLAYING &&
612       (GetKeyModState() & KMOD_TextInput) != KMOD_None)
613   */
614   if (checkTextInputKeyModState())
615   {
616     HandleKey(key, KEY_PRESSED);
617     HandleKey(key, KEY_RELEASED);
618   }
619 }
620 #endif
621
622 void HandleKeyEvent(KeyEvent *event)
623 {
624   int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
625   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
626   Key key = GetEventKey(event, with_modifiers);
627   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
628
629 #if DEBUG_EVENTS
630   Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
631         event->type == EVENT_KEYPRESS ? "pressed" : "released",
632         event->keysym.scancode,
633         event->keysym.sym,
634         keymod,
635         GetKeyModState(),
636         key,
637         getKeyNameFromKey(key));
638 #endif
639
640 #if 0
641   if (key == KSYM_Menu)
642     Error(ERR_DEBUG, "menu key pressed");
643   else if (key == KSYM_Back)
644     Error(ERR_DEBUG, "back key pressed");
645 #endif
646
647 #if defined(PLATFORM_ANDROID)
648   // always map the "back" button to the "escape" key on Android devices
649   if (key == KSYM_Back)
650     key = KSYM_Escape;
651 #endif
652
653   HandleKeyModState(keymod, key_status);
654
655 #if defined(TARGET_SDL2)
656
657   // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
658   /*
659   if (game_status == GAME_MODE_PLAYING ||
660       (GetKeyModState() & KMOD_TextInput) == KMOD_None)
661   */
662   if (!checkTextInputKeyModState())
663     HandleKey(key, key_status);
664 #else
665   HandleKey(key, key_status);
666 #endif
667 }
668
669 void HandleFocusEvent(FocusChangeEvent *event)
670 {
671   static int old_joystick_status = -1;
672
673   if (event->type == EVENT_FOCUSOUT)
674   {
675     KeyboardAutoRepeatOn();
676     old_joystick_status = joystick.status;
677     joystick.status = JOYSTICK_NOT_AVAILABLE;
678
679     ClearPlayerAction();
680   }
681   else if (event->type == EVENT_FOCUSIN)
682   {
683     /* When there are two Rocks'n'Diamonds windows which overlap and
684        the player moves the pointer from one game window to the other,
685        a 'FocusOut' event is generated for the window the pointer is
686        leaving and a 'FocusIn' event is generated for the window the
687        pointer is entering. In some cases, it can happen that the
688        'FocusIn' event is handled by the one game process before the
689        'FocusOut' event by the other game process. In this case the
690        X11 environment would end up with activated keyboard auto repeat,
691        because unfortunately this is a global setting and not (which
692        would be far better) set for each X11 window individually.
693        The effect would be keyboard auto repeat while playing the game
694        (game_status == GAME_MODE_PLAYING), which is not desired.
695        To avoid this special case, we just wait 1/10 second before
696        processing the 'FocusIn' event.
697     */
698
699     if (game_status == GAME_MODE_PLAYING)
700     {
701       Delay(100);
702       KeyboardAutoRepeatOffUnlessAutoplay();
703     }
704
705     if (old_joystick_status != -1)
706       joystick.status = old_joystick_status;
707   }
708 }
709
710 void HandleClientMessageEvent(ClientMessageEvent *event)
711 {
712   if (CheckCloseWindowEvent(event))
713     CloseAllAndExit(0);
714 }
715
716 void HandleWindowManagerEvent(Event *event)
717 {
718 #if defined(TARGET_SDL)
719   SDLHandleWindowManagerEvent(event);
720 #endif
721 }
722
723 void HandleButton(int mx, int my, int button, int button_nr)
724 {
725   static int old_mx = 0, old_my = 0;
726
727   if (button < 0)
728   {
729     mx = old_mx;
730     my = old_my;
731     button = -button;
732   }
733   else
734   {
735     old_mx = mx;
736     old_my = my;
737   }
738
739   if (HandleGadgets(mx, my, button))
740   {
741     /* do not handle this button event anymore */
742     mx = my = -32;      /* force mouse event to be outside screen tiles */
743   }
744
745   /* do not use scroll wheel button events for anything other than gadgets */
746   if (IS_WHEEL_BUTTON(button_nr))
747     return;
748
749 #if 0
750   Error(ERR_DEBUG, "::: game_status == %d", game_status);
751 #endif
752
753   switch (game_status)
754   {
755     case GAME_MODE_TITLE:
756       HandleTitleScreen(mx, my, 0, 0, button);
757       break;
758
759     case GAME_MODE_MAIN:
760       HandleMainMenu(mx, my, 0, 0, button);
761       break;
762
763     case GAME_MODE_PSEUDO_TYPENAME:
764       HandleTypeName(0, KSYM_Return);
765       break;
766
767     case GAME_MODE_LEVELS:
768       HandleChooseLevelSet(mx, my, 0, 0, button);
769       break;
770
771     case GAME_MODE_LEVELNR:
772       HandleChooseLevelNr(mx, my, 0, 0, button);
773       break;
774
775     case GAME_MODE_SCORES:
776       HandleHallOfFame(0, 0, 0, 0, button);
777       break;
778
779     case GAME_MODE_EDITOR:
780       HandleLevelEditorIdle();
781       break;
782
783     case GAME_MODE_INFO:
784       HandleInfoScreen(mx, my, 0, 0, button);
785       break;
786
787     case GAME_MODE_SETUP:
788       HandleSetupScreen(mx, my, 0, 0, button);
789       break;
790
791     case GAME_MODE_PLAYING:
792 #ifdef DEBUG
793       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
794         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
795 #endif
796       break;
797
798     default:
799       break;
800   }
801 }
802
803 static boolean is_string_suffix(char *string, char *suffix)
804 {
805   int string_len = strlen(string);
806   int suffix_len = strlen(suffix);
807
808   if (suffix_len > string_len)
809     return FALSE;
810
811   return (strEqual(&string[string_len - suffix_len], suffix));
812 }
813
814 #define MAX_CHEAT_INPUT_LEN     32
815
816 static void HandleKeysSpecial(Key key)
817 {
818   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
819   char letter = getCharFromKey(key);
820   int cheat_input_len = strlen(cheat_input);
821   int i;
822
823   if (letter == 0)
824     return;
825
826   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
827   {
828     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
829       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
830
831     cheat_input_len = MAX_CHEAT_INPUT_LEN;
832   }
833
834   cheat_input[cheat_input_len++] = letter;
835   cheat_input[cheat_input_len] = '\0';
836
837 #if DEBUG_EVENTS
838   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
839 #endif
840
841   if (game_status == GAME_MODE_MAIN)
842   {
843     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
844         is_string_suffix(cheat_input, ":ist"))
845     {
846       InsertSolutionTape();
847     }
848     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
849              is_string_suffix(cheat_input, ":rg"))
850     {
851       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
852       DrawMainMenu();
853     }
854     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
855              is_string_suffix(cheat_input, ":rs"))
856     {
857       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
858       DrawMainMenu();
859     }
860     else if (is_string_suffix(cheat_input, ":reload-music") ||
861              is_string_suffix(cheat_input, ":rm"))
862     {
863       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
864       DrawMainMenu();
865     }
866     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
867              is_string_suffix(cheat_input, ":ra"))
868     {
869       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
870                           1 << ARTWORK_TYPE_SOUNDS |
871                           1 << ARTWORK_TYPE_MUSIC);
872       DrawMainMenu();
873     }
874     else if (is_string_suffix(cheat_input, ":dump-level") ||
875              is_string_suffix(cheat_input, ":dl"))
876     {
877       DumpLevel(&level);
878     }
879     else if (is_string_suffix(cheat_input, ":dump-tape") ||
880              is_string_suffix(cheat_input, ":dt"))
881     {
882       DumpTape(&tape);
883     }
884     else if (is_string_suffix(cheat_input, ":save-native-level") ||
885              is_string_suffix(cheat_input, ":snl"))
886     {
887       SaveNativeLevel(&level);
888     }
889   }
890   else if (game_status == GAME_MODE_PLAYING)
891   {
892 #ifdef DEBUG
893     if (is_string_suffix(cheat_input, ".q"))
894       DEBUG_SetMaximumDynamite();
895 #endif
896   }
897   else if (game_status == GAME_MODE_EDITOR)
898   {
899     if (is_string_suffix(cheat_input, ":dump-brush") ||
900         is_string_suffix(cheat_input, ":DB"))
901     {
902       DumpBrush();
903     }
904     else if (is_string_suffix(cheat_input, ":DDB"))
905     {
906       DumpBrush_Small();
907     }
908   }
909 }
910
911 void HandleKey(Key key, int key_status)
912 {
913   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
914   static struct SetupKeyboardInfo ski;
915   static struct SetupShortcutInfo ssi;
916   static struct
917   {
918     Key *key_custom;
919     Key *key_snap;
920     Key key_default;
921     byte action;
922   } key_info[] =
923   {
924     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
925     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
926     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
927     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
928     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
929     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
930   };
931   int joy = 0;
932   int i;
933
934   if (game_status == GAME_MODE_PLAYING)
935   {
936     /* only needed for single-step tape recording mode */
937     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
938     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
939     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
940     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
941     int pnr;
942
943     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
944     {
945       byte key_action = 0;
946
947       if (setup.input[pnr].use_joystick)
948         continue;
949
950       ski = setup.input[pnr].key;
951
952       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
953         if (key == *key_info[i].key_custom)
954           key_action |= key_info[i].action;
955
956       /* use combined snap+direction keys for the first player only */
957       if (pnr == 0)
958       {
959         ssi = setup.shortcut;
960
961         for (i = 0; i < NUM_DIRECTIONS; i++)
962           if (key == *key_info[i].key_snap)
963             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
964       }
965
966       /* clear delayed snap and drop actions in single step mode (see below) */
967       if (tape.single_step)
968       {
969         if (clear_snap_button[pnr])
970         {
971           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
972           clear_snap_button[pnr] = FALSE;
973         }
974
975         if (clear_drop_button[pnr])
976         {
977           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
978           clear_drop_button[pnr] = FALSE;
979         }
980       }
981
982       if (key_status == KEY_PRESSED)
983         stored_player[pnr].action |= key_action;
984       else
985         stored_player[pnr].action &= ~key_action;
986
987       if (tape.single_step && tape.recording && tape.pausing)
988       {
989         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
990         {
991           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
992
993           /* if snap key already pressed, don't snap when releasing (below) */
994           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
995             element_snapped[pnr] = TRUE;
996
997           /* if drop key already pressed, don't drop when releasing (below) */
998           if (stored_player[pnr].action & KEY_BUTTON_DROP)
999             element_dropped[pnr] = TRUE;
1000         }
1001 #if 1
1002         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1003         {
1004           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1005               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1006           {
1007 #if 0
1008             printf("::: drop key pressed\n");
1009 #endif
1010
1011             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1012                 getRedDiskReleaseFlag_SP() == 0)
1013               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1014
1015             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1016           }
1017         }
1018 #endif
1019         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1020         {
1021           if (key_action & KEY_BUTTON_SNAP)
1022           {
1023             /* if snap key was released without moving (see above), snap now */
1024             if (!element_snapped[pnr])
1025             {
1026               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1027
1028               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1029
1030               /* clear delayed snap button on next event */
1031               clear_snap_button[pnr] = TRUE;
1032             }
1033
1034             element_snapped[pnr] = FALSE;
1035           }
1036
1037 #if 1
1038           if (key_action & KEY_BUTTON_DROP &&
1039               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1040           {
1041             /* if drop key was released without moving (see above), drop now */
1042             if (!element_dropped[pnr])
1043             {
1044               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1045
1046               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1047                   getRedDiskReleaseFlag_SP() != 0)
1048                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1049
1050               /* clear delayed drop button on next event */
1051               clear_drop_button[pnr] = TRUE;
1052             }
1053
1054             element_dropped[pnr] = FALSE;
1055           }
1056 #endif
1057         }
1058       }
1059       else if (tape.recording && tape.pausing)
1060       {
1061         /* prevent key release events from un-pausing a paused game */
1062         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1063           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1064       }
1065     }
1066   }
1067   else
1068   {
1069     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1070       if (key == key_info[i].key_default)
1071         joy |= key_info[i].action;
1072   }
1073
1074   if (joy)
1075   {
1076     if (key_status == KEY_PRESSED)
1077       key_joystick_mapping |= joy;
1078     else
1079       key_joystick_mapping &= ~joy;
1080
1081     HandleJoystick();
1082   }
1083
1084   if (game_status != GAME_MODE_PLAYING)
1085     key_joystick_mapping = 0;
1086
1087   if (key_status == KEY_RELEASED)
1088     return;
1089
1090   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1091       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1092   {
1093     setup.fullscreen = !setup.fullscreen;
1094
1095     printf("::: %d\n", setup.window_scaling_percent);
1096
1097     ToggleFullscreenIfNeeded();
1098
1099     if (game_status == GAME_MODE_SETUP)
1100       RedrawSetupScreenAfterFullscreenToggle();
1101
1102     return;
1103   }
1104
1105   if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1106       (GetKeyModState() & KMOD_Control) && video.window_scaling_available &&
1107       !video.fullscreen_enabled)
1108   {
1109     if (key == KSYM_0)
1110       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1111     else
1112       setup.window_scaling_percent +=
1113         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1114
1115     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1116       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1117     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1118       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1119
1120     ToggleFullscreenIfNeeded();
1121
1122     if (game_status == GAME_MODE_SETUP)
1123       RedrawSetupScreenAfterFullscreenToggle();
1124
1125     return;
1126   }
1127
1128 #if 0
1129   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1130       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1131 #else
1132   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1133       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1134 #endif
1135   {
1136     GameEnd();
1137
1138     return;
1139   }
1140
1141   if (game_status == GAME_MODE_MAIN &&
1142       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1143   {
1144     StartGameActions(options.network, setup.autorecord, level.random_seed);
1145
1146     return;
1147   }
1148
1149   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1150   {
1151     if (key == setup.shortcut.save_game)
1152       TapeQuickSave();
1153     else if (key == setup.shortcut.load_game)
1154       TapeQuickLoad();
1155     else if (key == setup.shortcut.toggle_pause)
1156       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1157
1158     HandleTapeButtonKeys(key);
1159     HandleSoundButtonKeys(key);
1160   }
1161
1162   if (game_status == GAME_MODE_PLAYING && !network_playing)
1163   {
1164     int centered_player_nr_next = -999;
1165
1166     if (key == setup.shortcut.focus_player_all)
1167       centered_player_nr_next = -1;
1168     else
1169       for (i = 0; i < MAX_PLAYERS; i++)
1170         if (key == setup.shortcut.focus_player[i])
1171           centered_player_nr_next = i;
1172
1173     if (centered_player_nr_next != -999)
1174     {
1175       game.centered_player_nr_next = centered_player_nr_next;
1176       game.set_centered_player = TRUE;
1177
1178       if (tape.recording)
1179       {
1180         tape.centered_player_nr_next = game.centered_player_nr_next;
1181         tape.set_centered_player = TRUE;
1182       }
1183     }
1184   }
1185
1186   HandleKeysSpecial(key);
1187
1188   if (HandleGadgetsKeyInput(key))
1189   {
1190     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1191       key = KSYM_UNDEFINED;
1192   }
1193
1194   switch (game_status)
1195   {
1196     case GAME_MODE_PSEUDO_TYPENAME:
1197       HandleTypeName(0, key);
1198       break;
1199
1200     case GAME_MODE_TITLE:
1201     case GAME_MODE_MAIN:
1202     case GAME_MODE_LEVELS:
1203     case GAME_MODE_LEVELNR:
1204     case GAME_MODE_SETUP:
1205     case GAME_MODE_INFO:
1206     case GAME_MODE_SCORES:
1207       switch (key)
1208       {
1209         case KSYM_space:
1210         case KSYM_Return:
1211           if (game_status == GAME_MODE_TITLE)
1212             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1213           else if (game_status == GAME_MODE_MAIN)
1214             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1215           else if (game_status == GAME_MODE_LEVELS)
1216             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1217           else if (game_status == GAME_MODE_LEVELNR)
1218             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1219           else if (game_status == GAME_MODE_SETUP)
1220             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1221           else if (game_status == GAME_MODE_INFO)
1222             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1223           else if (game_status == GAME_MODE_SCORES)
1224             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1225           break;
1226
1227         case KSYM_Escape:
1228           if (game_status != GAME_MODE_MAIN)
1229             FadeSkipNextFadeIn();
1230
1231           if (game_status == GAME_MODE_TITLE)
1232             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1233           else if (game_status == GAME_MODE_LEVELS)
1234             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1235           else if (game_status == GAME_MODE_LEVELNR)
1236             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1237           else if (game_status == GAME_MODE_SETUP)
1238             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1239           else if (game_status == GAME_MODE_INFO)
1240             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1241           else if (game_status == GAME_MODE_SCORES)
1242             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1243           break;
1244
1245         case KSYM_Page_Up:
1246           if (game_status == GAME_MODE_LEVELS)
1247             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1248           else if (game_status == GAME_MODE_LEVELNR)
1249             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1250           else if (game_status == GAME_MODE_SETUP)
1251             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1252           else if (game_status == GAME_MODE_INFO)
1253             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1254           else if (game_status == GAME_MODE_SCORES)
1255             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1256           break;
1257
1258         case KSYM_Page_Down:
1259           if (game_status == GAME_MODE_LEVELS)
1260             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1261           else if (game_status == GAME_MODE_LEVELNR)
1262             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1263           else if (game_status == GAME_MODE_SETUP)
1264             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1265           else if (game_status == GAME_MODE_INFO)
1266             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1267           else if (game_status == GAME_MODE_SCORES)
1268             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1269           break;
1270
1271 #ifdef DEBUG
1272         case KSYM_0:
1273           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1274           break;
1275
1276         case KSYM_b:
1277           setup.sp_show_border_elements = !setup.sp_show_border_elements;
1278           printf("Supaplex border elements %s\n",
1279                  setup.sp_show_border_elements ? "enabled" : "disabled");
1280           break;
1281 #endif
1282
1283         default:
1284           break;
1285       }
1286       break;
1287
1288     case GAME_MODE_EDITOR:
1289       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1290         HandleLevelEditorKeyInput(key);
1291       break;
1292
1293     case GAME_MODE_PLAYING:
1294     {
1295       switch (key)
1296       {
1297         case KSYM_Escape:
1298           RequestQuitGame(setup.ask_on_escape);
1299           break;
1300
1301 #ifdef DEBUG
1302         case KSYM_0:
1303 #if 0
1304         case KSYM_1:
1305         case KSYM_2:
1306         case KSYM_3:
1307         case KSYM_4:
1308         case KSYM_5:
1309         case KSYM_6:
1310         case KSYM_7:
1311         case KSYM_8:
1312         case KSYM_9:
1313 #endif
1314           if (key == KSYM_0)
1315           {
1316             if (GameFrameDelay == 500)
1317               GameFrameDelay = GAME_FRAME_DELAY;
1318             else
1319               GameFrameDelay = 500;
1320           }
1321           else
1322             GameFrameDelay = (key - KSYM_0) * 10;
1323           printf("Game speed == %d%% (%d ms delay between two frames)\n",
1324                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1325           break;
1326
1327         case KSYM_d:
1328           if (options.debug)
1329           {
1330             options.debug = FALSE;
1331             printf("debug mode disabled\n");
1332           }
1333           else
1334           {
1335             options.debug = TRUE;
1336             printf("debug mode enabled\n");
1337           }
1338           break;
1339
1340         case KSYM_s:
1341           if (!global.fps_slowdown)
1342           {
1343             global.fps_slowdown = TRUE;
1344             global.fps_slowdown_factor = 2;
1345             printf("fps slowdown enabled -- display only every 2nd frame\n");
1346           }
1347           else if (global.fps_slowdown_factor == 2)
1348           {
1349             global.fps_slowdown_factor = 4;
1350             printf("fps slowdown enabled -- display only every 4th frame\n");
1351           }
1352           else
1353           {
1354             global.fps_slowdown = FALSE;
1355             global.fps_slowdown_factor = 1;
1356             printf("fps slowdown disabled\n");
1357           }
1358           break;
1359
1360         case KSYM_f:
1361           ScrollStepSize = TILEX / 8;
1362           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1363           break;
1364
1365         case KSYM_g:
1366           ScrollStepSize = TILEX / 4;
1367           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1368           break;
1369
1370         case KSYM_h:
1371           ScrollStepSize = TILEX / 2;
1372           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1373           break;
1374
1375         case KSYM_l:
1376           ScrollStepSize = TILEX;
1377           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1378           break;
1379
1380         case KSYM_v:
1381           printf("::: currently using game engine version %d\n",
1382                  game.engine_version);
1383           break;
1384 #endif
1385
1386         default:
1387           break;
1388       }
1389       break;
1390     }
1391
1392     default:
1393       if (key == KSYM_Escape)
1394       {
1395         game_status = GAME_MODE_MAIN;
1396         DrawMainMenu();
1397
1398         return;
1399       }
1400   }
1401 }
1402
1403 void HandleNoEvent()
1404 {
1405   if (button_status && game_status != GAME_MODE_PLAYING)
1406   {
1407     HandleButton(0, 0, -button_status, button_status);
1408
1409     return;
1410   }
1411
1412 #if defined(NETWORK_AVALIABLE)
1413   if (options.network)
1414     HandleNetworking();
1415 #endif
1416
1417   HandleJoystick();
1418 }
1419
1420 static int HandleJoystickForAllPlayers()
1421 {
1422   int i;
1423   int result = 0;
1424
1425   for (i = 0; i < MAX_PLAYERS; i++)
1426   {
1427     byte joy_action = 0;
1428
1429     /*
1430     if (!setup.input[i].use_joystick)
1431       continue;
1432       */
1433
1434     joy_action = Joystick(i);
1435     result |= joy_action;
1436
1437     if (!setup.input[i].use_joystick)
1438       continue;
1439
1440     stored_player[i].action = joy_action;
1441   }
1442
1443   return result;
1444 }
1445
1446 void HandleJoystick()
1447 {
1448   int joystick  = HandleJoystickForAllPlayers();
1449   int keyboard  = key_joystick_mapping;
1450   int joy       = (joystick | keyboard);
1451   int left      = joy & JOY_LEFT;
1452   int right     = joy & JOY_RIGHT;
1453   int up        = joy & JOY_UP;
1454   int down      = joy & JOY_DOWN;
1455   int button    = joy & JOY_BUTTON;
1456   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1457   int dx        = (left ? -1    : right ? 1     : 0);
1458   int dy        = (up   ? -1    : down  ? 1     : 0);
1459
1460   switch (game_status)
1461   {
1462     case GAME_MODE_TITLE:
1463     case GAME_MODE_MAIN:
1464     case GAME_MODE_LEVELS:
1465     case GAME_MODE_LEVELNR:
1466     case GAME_MODE_SETUP:
1467     case GAME_MODE_INFO:
1468     {
1469       static unsigned int joystickmove_delay = 0;
1470
1471       if (joystick && !button &&
1472           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1473         newbutton = dx = dy = 0;
1474
1475       if (game_status == GAME_MODE_TITLE)
1476         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1477       else if (game_status == GAME_MODE_MAIN)
1478         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1479       else if (game_status == GAME_MODE_LEVELS)
1480         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1481       else if (game_status == GAME_MODE_LEVELNR)
1482         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1483       else if (game_status == GAME_MODE_SETUP)
1484         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1485       else if (game_status == GAME_MODE_INFO)
1486         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1487       break;
1488     }
1489
1490     case GAME_MODE_SCORES:
1491       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1492       break;
1493
1494     case GAME_MODE_EDITOR:
1495       HandleLevelEditorIdle();
1496       break;
1497
1498     case GAME_MODE_PLAYING:
1499       if (tape.playing || keyboard)
1500         newbutton = ((joy & JOY_BUTTON) != 0);
1501
1502 #if 0
1503       if (local_player->LevelSolved_GameEnd && newbutton)
1504 #else
1505       if (AllPlayersGone && newbutton)
1506 #endif
1507       {
1508         GameEnd();
1509
1510         return;
1511       }
1512
1513       break;
1514
1515     default:
1516       break;
1517   }
1518 }