rnd-20100417-2-src
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * events.c                                                 *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "events.h"
17 #include "init.h"
18 #include "screens.h"
19 #include "tools.h"
20 #include "game.h"
21 #include "editor.h"
22 #include "files.h"
23 #include "tape.h"
24 #include "network.h"
25
26
27 #define DEBUG_EVENTS            0
28
29
30 static boolean cursor_inside_playfield = FALSE;
31 static boolean playfield_cursor_set = FALSE;
32 static unsigned long 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 int FilterMouseMotionEvents(const Event *event)
40 {
41   MotionEvent *motion;
42
43   /* non-motion events are directly passed to event handler functions */
44   if (event->type != EVENT_MOTIONNOTIFY)
45     return 1;
46
47   motion = (MotionEvent *)event;
48   cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
49                              motion->y >= SY && motion->y < SY + SYSIZE);
50
51   if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
52   {
53     SetMouseCursor(CURSOR_DEFAULT);
54     playfield_cursor_set = FALSE;
55     DelayReached(&playfield_cursor_delay, 0);
56   }
57
58   /* skip mouse motion events without pressed button outside level editor */
59   if (button_status == MB_RELEASED &&
60       game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
61     return 0;
62   else
63     return 1;
64 }
65
66 /* to prevent delay problems, skip mouse motion events if the very next
67    event is also a mouse motion event (and therefore effectively only
68    handling the last of a row of mouse motion events in the event queue) */
69
70 boolean SkipPressedMouseMotionEvent(const Event *event)
71 {
72   /* nothing to do if the current event is not a mouse motion event */
73   if (event->type != EVENT_MOTIONNOTIFY)
74     return FALSE;
75
76   /* only skip motion events with pressed button outside level editor */
77   if (button_status == MB_RELEASED ||
78       game_status == GAME_MODE_EDITOR || game_status == GAME_MODE_PLAYING)
79     return FALSE;
80
81   if (PendingEvent())
82   {
83     Event next_event;
84
85     PeekEvent(&next_event);
86
87     /* if next event is also a mouse motion event, skip the current one */
88     if (next_event.type == EVENT_MOTIONNOTIFY)
89       return TRUE;
90   }
91
92   return FALSE;
93 }
94
95 /* this is only really needed for non-SDL targets to filter unwanted events;
96    when using SDL with properly installed event filter, this function can be
97    replaced with a simple "NextEvent()" call, but it doesn't hurt either */
98
99 static boolean NextValidEvent(Event *event)
100 {
101   while (PendingEvent())
102   {
103     boolean handle_this_event = FALSE;
104
105     NextEvent(event);
106
107     if (FilterMouseMotionEvents(event))
108       handle_this_event = TRUE;
109
110     if (SkipPressedMouseMotionEvent(event))
111       handle_this_event = FALSE;
112
113     if (handle_this_event)
114       return TRUE;
115   }
116
117   return FALSE;
118 }
119
120 void EventLoop(void)
121 {
122   while (1)
123   {
124     if (PendingEvent())         /* got event */
125     {
126       Event event;
127
128       while (NextValidEvent(&event))
129       {
130         switch (event.type)
131         {
132           case EVENT_BUTTONPRESS:
133           case EVENT_BUTTONRELEASE:
134             HandleButtonEvent((ButtonEvent *) &event);
135             break;
136   
137           case EVENT_MOTIONNOTIFY:
138             HandleMotionEvent((MotionEvent *) &event);
139             break;
140   
141           case EVENT_KEYPRESS:
142           case EVENT_KEYRELEASE:
143             HandleKeyEvent((KeyEvent *) &event);
144             break;
145   
146           default:
147             HandleOtherEvents(&event);
148             break;
149         }
150       }
151     }
152     else
153     {
154       /* when playing, display a special mouse pointer inside the playfield */
155       if (game_status == GAME_MODE_PLAYING && !tape.pausing)
156       {
157         if (!playfield_cursor_set && cursor_inside_playfield &&
158             DelayReached(&playfield_cursor_delay, 1000))
159         {
160           SetMouseCursor(CURSOR_PLAYFIELD);
161           playfield_cursor_set = TRUE;
162         }
163       }
164       else if (playfield_cursor_set)
165       {
166         SetMouseCursor(CURSOR_DEFAULT);
167         playfield_cursor_set = FALSE;
168       }
169
170       HandleNoEvent();
171     }
172
173     /* don't use all CPU time when idle; the main loop while playing
174        has its own synchronization and is CPU friendly, too */
175
176     if (game_status == GAME_MODE_PLAYING)
177     {
178       HandleGameActions();
179     }
180     else
181     {
182       SyncDisplay();
183       if (!PendingEvent())      /* delay only if no pending events */
184         Delay(10);
185     }
186
187     /* refresh window contents from drawing buffer, if needed */
188     BackToFront();
189
190     if (game_status == GAME_MODE_QUIT)
191       return;
192   }
193 }
194
195 void HandleOtherEvents(Event *event)
196 {
197   switch (event->type)
198   {
199     case EVENT_EXPOSE:
200       HandleExposeEvent((ExposeEvent *) event);
201       break;
202
203     case EVENT_UNMAPNOTIFY:
204 #if 0
205       /* This causes the game to stop not only when iconified, but also
206          when on another virtual desktop, which might be not desired. */
207       SleepWhileUnmapped();
208 #endif
209       break;
210
211     case EVENT_FOCUSIN:
212     case EVENT_FOCUSOUT:
213       HandleFocusEvent((FocusChangeEvent *) event);
214       break;
215
216     case EVENT_CLIENTMESSAGE:
217       HandleClientMessageEvent((ClientMessageEvent *) event);
218       break;
219
220 #if defined(TARGET_SDL)
221     case SDL_JOYAXISMOTION:
222     case SDL_JOYBUTTONDOWN:
223     case SDL_JOYBUTTONUP:
224       HandleJoystickEvent(event);
225       break;
226
227     case SDL_SYSWMEVENT:
228       HandleWindowManagerEvent(event);
229       break;
230 #endif
231
232     default:
233       break;
234   }
235 }
236
237 void ClearEventQueue()
238 {
239   while (PendingEvent())
240   {
241     Event event;
242
243     NextEvent(&event);
244
245     switch (event.type)
246     {
247       case EVENT_BUTTONRELEASE:
248         button_status = MB_RELEASED;
249         break;
250
251       case EVENT_KEYRELEASE:
252 #if 1
253         ClearPlayerAction();
254 #else
255         key_joystick_mapping = 0;
256 #endif
257         break;
258
259       default:
260         HandleOtherEvents(&event);
261         break;
262     }
263   }
264 }
265
266 void ClearPlayerAction()
267 {
268   int i;
269
270   /* simulate key release events for still pressed keys */
271   key_joystick_mapping = 0;
272   for (i = 0; i < MAX_PLAYERS; i++)
273     stored_player[i].action = 0;
274 }
275
276 void SleepWhileUnmapped()
277 {
278   boolean window_unmapped = TRUE;
279
280   KeyboardAutoRepeatOn();
281
282   while (window_unmapped)
283   {
284     Event event;
285
286     NextEvent(&event);
287
288     switch (event.type)
289     {
290       case EVENT_BUTTONRELEASE:
291         button_status = MB_RELEASED;
292         break;
293
294       case EVENT_KEYRELEASE:
295         key_joystick_mapping = 0;
296         break;
297
298       case EVENT_MAPNOTIFY:
299         window_unmapped = FALSE;
300         break;
301
302       case EVENT_UNMAPNOTIFY:
303         /* this is only to surely prevent the 'should not happen' case
304          * of recursively looping between 'SleepWhileUnmapped()' and
305          * 'HandleOtherEvents()' which usually calls this funtion.
306          */
307         break;
308
309       default:
310         HandleOtherEvents(&event);
311         break;
312     }
313   }
314
315   if (game_status == GAME_MODE_PLAYING)
316     KeyboardAutoRepeatOffUnlessAutoplay();
317 }
318
319 void HandleExposeEvent(ExposeEvent *event)
320 {
321 #ifndef TARGET_SDL
322   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
323   FlushDisplay();
324 #endif
325 }
326
327 void HandleButtonEvent(ButtonEvent *event)
328 {
329 #if DEBUG_EVENTS
330   printf("::: BUTTON EVENT: button %d %s\n", event->button,
331          event->type == EVENT_BUTTONPRESS ? "pressed" : "released");
332 #endif
333
334   motion_status = FALSE;
335
336   if (event->type == EVENT_BUTTONPRESS)
337     button_status = event->button;
338   else
339     button_status = MB_RELEASED;
340
341   HandleButton(event->x, event->y, button_status, event->button);
342 }
343
344 void HandleMotionEvent(MotionEvent *event)
345 {
346   if (!PointerInWindow(window))
347     return;     /* window and pointer are on different screens */
348
349   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
350     return;
351
352   motion_status = TRUE;
353
354   HandleButton(event->x, event->y, button_status, button_status);
355 }
356
357 void HandleKeyEvent(KeyEvent *event)
358 {
359   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
360   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
361   Key key = GetEventKey(event, with_modifiers);
362   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
363
364 #if DEBUG_EVENTS
365   printf("::: KEY EVENT: %d %s\n", GetEventKey(event, TRUE),
366          event->type == EVENT_KEYPRESS ? "pressed" : "released");
367 #endif
368
369   HandleKeyModState(keymod, key_status);
370   HandleKey(key, key_status);
371 }
372
373 void HandleFocusEvent(FocusChangeEvent *event)
374 {
375   static int old_joystick_status = -1;
376
377   if (event->type == EVENT_FOCUSOUT)
378   {
379     KeyboardAutoRepeatOn();
380     old_joystick_status = joystick.status;
381     joystick.status = JOYSTICK_NOT_AVAILABLE;
382
383     ClearPlayerAction();
384   }
385   else if (event->type == EVENT_FOCUSIN)
386   {
387     /* When there are two Rocks'n'Diamonds windows which overlap and
388        the player moves the pointer from one game window to the other,
389        a 'FocusOut' event is generated for the window the pointer is
390        leaving and a 'FocusIn' event is generated for the window the
391        pointer is entering. In some cases, it can happen that the
392        'FocusIn' event is handled by the one game process before the
393        'FocusOut' event by the other game process. In this case the
394        X11 environment would end up with activated keyboard auto repeat,
395        because unfortunately this is a global setting and not (which
396        would be far better) set for each X11 window individually.
397        The effect would be keyboard auto repeat while playing the game
398        (game_status == GAME_MODE_PLAYING), which is not desired.
399        To avoid this special case, we just wait 1/10 second before
400        processing the 'FocusIn' event.
401     */
402
403     if (game_status == GAME_MODE_PLAYING)
404     {
405       Delay(100);
406       KeyboardAutoRepeatOffUnlessAutoplay();
407     }
408
409     if (old_joystick_status != -1)
410       joystick.status = old_joystick_status;
411   }
412 }
413
414 void HandleClientMessageEvent(ClientMessageEvent *event)
415 {
416   if (CheckCloseWindowEvent(event))
417     CloseAllAndExit(0);
418 }
419
420 void HandleWindowManagerEvent(Event *event)
421 {
422 #if defined(TARGET_SDL)
423   SDLHandleWindowManagerEvent(event);
424 #endif
425 }
426
427 void HandleButton(int mx, int my, int button, int button_nr)
428 {
429   static int old_mx = 0, old_my = 0;
430
431   if (button < 0)
432   {
433     mx = old_mx;
434     my = old_my;
435     button = -button;
436   }
437   else
438   {
439     old_mx = mx;
440     old_my = my;
441   }
442
443   if (HandleGadgets(mx, my, button))
444   {
445     /* do not handle this button event anymore */
446     mx = my = -32;      /* force mouse event to be outside screen tiles */
447   }
448
449   /* do not use scroll wheel button events for anything other than gadgets */
450   if (IS_WHEEL_BUTTON(button_nr))
451     return;
452
453   switch (game_status)
454   {
455     case GAME_MODE_TITLE:
456       HandleTitleScreen(mx, my, 0, 0, button);
457       break;
458
459     case GAME_MODE_MAIN:
460       HandleMainMenu(mx, my, 0, 0, button);
461       break;
462
463     case GAME_MODE_PSEUDO_TYPENAME:
464       HandleTypeName(0, KSYM_Return);
465       break;
466
467     case GAME_MODE_LEVELS:
468       HandleChooseLevel(mx, my, 0, 0, button);
469       break;
470
471     case GAME_MODE_SCORES:
472       HandleHallOfFame(0, 0, 0, 0, button);
473       break;
474
475     case GAME_MODE_EDITOR:
476       HandleLevelEditorIdle();
477       break;
478
479     case GAME_MODE_INFO:
480       HandleInfoScreen(mx, my, 0, 0, button);
481       break;
482
483     case GAME_MODE_SETUP:
484       HandleSetupScreen(mx, my, 0, 0, button);
485       break;
486
487     case GAME_MODE_PLAYING:
488 #ifdef DEBUG
489       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
490         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
491 #endif
492       break;
493
494     default:
495       break;
496   }
497 }
498
499 static boolean is_string_suffix(char *string, char *suffix)
500 {
501   int string_len = strlen(string);
502   int suffix_len = strlen(suffix);
503
504   if (suffix_len > string_len)
505     return FALSE;
506
507   return (strEqual(&string[string_len - suffix_len], suffix));
508 }
509
510 #define MAX_CHEAT_INPUT_LEN     32
511
512 static void HandleKeysSpecial(Key key)
513 {
514   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
515   char letter = getCharFromKey(key);
516   int cheat_input_len = strlen(cheat_input);
517   int i;
518
519   if (letter == 0)
520     return;
521
522   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
523   {
524     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
525       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
526
527     cheat_input_len = MAX_CHEAT_INPUT_LEN;
528   }
529
530   cheat_input[cheat_input_len++] = letter;
531   cheat_input[cheat_input_len] = '\0';
532
533 #if DEBUG_EVENTS
534   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
535 #endif
536
537   if (game_status == GAME_MODE_MAIN)
538   {
539     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
540         is_string_suffix(cheat_input, ":ist"))
541     {
542       InsertSolutionTape();
543     }
544     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
545              is_string_suffix(cheat_input, ":rg"))
546     {
547       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
548       DrawMainMenu();
549     }
550     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
551              is_string_suffix(cheat_input, ":rs"))
552     {
553       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
554       DrawMainMenu();
555     }
556     else if (is_string_suffix(cheat_input, ":reload-music") ||
557              is_string_suffix(cheat_input, ":rm"))
558     {
559       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
560       DrawMainMenu();
561     }
562     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
563              is_string_suffix(cheat_input, ":ra"))
564     {
565       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
566                           1 << ARTWORK_TYPE_SOUNDS |
567                           1 << ARTWORK_TYPE_MUSIC);
568       DrawMainMenu();
569     }
570     else if (is_string_suffix(cheat_input, ":dump-level") ||
571              is_string_suffix(cheat_input, ":dl"))
572     {
573       DumpLevel(&level);
574     }
575     else if (is_string_suffix(cheat_input, ":dump-tape") ||
576              is_string_suffix(cheat_input, ":dt"))
577     {
578       DumpTape(&tape);
579     }
580     else if (is_string_suffix(cheat_input, ":save-native-level") ||
581              is_string_suffix(cheat_input, ":snl"))
582     {
583       SaveNativeLevel(&level);
584     }
585   }
586   else if (game_status == GAME_MODE_PLAYING)
587   {
588 #ifdef DEBUG
589     if (is_string_suffix(cheat_input, ".q"))
590       DEBUG_SetMaximumDynamite();
591 #endif
592   }
593   else if (game_status == GAME_MODE_EDITOR)
594   {
595     if (is_string_suffix(cheat_input, ":dump-brush") ||
596         is_string_suffix(cheat_input, ":DB"))
597     {
598       DumpBrush();
599     }
600     else if (is_string_suffix(cheat_input, ":DDB"))
601     {
602       DumpBrush_Small();
603     }
604   }
605 }
606
607 void HandleKey(Key key, int key_status)
608 {
609   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
610   static struct SetupKeyboardInfo custom_key;
611   static struct
612   {
613     Key *key_custom;
614     Key key_default;
615     byte action;
616   } key_info[] =
617   {
618     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
619     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
620     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
621     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
622     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
623     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
624   };
625   int joy = 0;
626   int i;
627
628   if (game_status == GAME_MODE_PLAYING)
629   {
630     /* only needed for single-step tape recording mode */
631     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
632     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
633     int pnr;
634
635     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
636     {
637       byte key_action = 0;
638
639       if (setup.input[pnr].use_joystick)
640         continue;
641
642       custom_key = setup.input[pnr].key;
643
644       for (i = 0; i < 6; i++)
645         if (key == *key_info[i].key_custom)
646           key_action |= key_info[i].action;
647
648       if (tape.single_step && clear_button_2[pnr])
649       {
650         stored_player[pnr].action &= ~KEY_BUTTON_2;
651         clear_button_2[pnr] = FALSE;
652       }
653
654       if (key_status == KEY_PRESSED)
655         stored_player[pnr].action |= key_action;
656       else
657         stored_player[pnr].action &= ~key_action;
658
659       if (tape.single_step && tape.recording && tape.pausing)
660       {
661         if (key_status == KEY_PRESSED &&
662             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
663         {
664           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
665
666           if (key_action & KEY_MOTION)
667           {
668             if (stored_player[pnr].action & KEY_BUTTON_2)
669               element_dropped[pnr] = TRUE;
670           }
671         }
672         else if (key_status == KEY_RELEASED &&
673                  (key_action & KEY_BUTTON_2))
674         {
675           if (!element_dropped[pnr])
676           {
677             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
678
679             stored_player[pnr].action |= KEY_BUTTON_2;
680             clear_button_2[pnr] = TRUE;
681           }
682
683           element_dropped[pnr] = FALSE;
684         }
685       }
686 #if 1
687       else if (tape.recording && tape.pausing)
688       {
689         /* prevent key release events from un-pausing a paused game */
690         if (key_status == KEY_PRESSED &&
691             (key_action & KEY_ACTION))
692           TapeTogglePause(TAPE_TOGGLE_MANUAL);
693       }
694 #else
695       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
696         TapeTogglePause(TAPE_TOGGLE_MANUAL);
697 #endif
698     }
699   }
700   else
701   {
702     for (i = 0; i < 6; i++)
703       if (key == key_info[i].key_default)
704         joy |= key_info[i].action;
705   }
706
707   if (joy)
708   {
709     if (key_status == KEY_PRESSED)
710       key_joystick_mapping |= joy;
711     else
712       key_joystick_mapping &= ~joy;
713
714     HandleJoystick();
715   }
716
717   if (game_status != GAME_MODE_PLAYING)
718     key_joystick_mapping = 0;
719
720   if (key_status == KEY_RELEASED)
721     return;
722
723   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
724       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
725   {
726     setup.fullscreen = !setup.fullscreen;
727
728     ToggleFullscreenIfNeeded();
729
730     if (game_status == GAME_MODE_SETUP)
731       RedrawSetupScreenAfterFullscreenToggle();
732
733     return;
734   }
735
736 #if 0
737   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
738       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
739 #else
740   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
741       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
742 #endif
743   {
744     GameEnd();
745
746     return;
747   }
748
749   if (game_status == GAME_MODE_MAIN &&
750       (key == setup.shortcut.toggle_pause || key == KSYM_space))
751   {
752     StartGameActions(options.network, setup.autorecord, level.random_seed);
753
754     return;
755   }
756
757   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
758   {
759     if (key == setup.shortcut.save_game)
760       TapeQuickSave();
761     else if (key == setup.shortcut.load_game)
762       TapeQuickLoad();
763     else if (key == setup.shortcut.toggle_pause)
764       TapeTogglePause(TAPE_TOGGLE_MANUAL);
765
766     HandleTapeButtonKeys(key);
767     HandleSoundButtonKeys(key);
768   }
769
770   if (game_status == GAME_MODE_PLAYING && !network_playing)
771   {
772     int centered_player_nr_next = -999;
773
774     if (key == setup.shortcut.focus_player_all)
775       centered_player_nr_next = -1;
776     else
777       for (i = 0; i < MAX_PLAYERS; i++)
778         if (key == setup.shortcut.focus_player[i])
779           centered_player_nr_next = i;
780
781     if (centered_player_nr_next != -999)
782     {
783       game.centered_player_nr_next = centered_player_nr_next;
784       game.set_centered_player = TRUE;
785
786       if (tape.recording)
787       {
788         tape.centered_player_nr_next = game.centered_player_nr_next;
789         tape.set_centered_player = TRUE;
790       }
791     }
792   }
793
794   HandleKeysSpecial(key);
795
796   if (HandleGadgetsKeyInput(key))
797   {
798     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
799       key = KSYM_UNDEFINED;
800   }
801
802   switch (game_status)
803   {
804     case GAME_MODE_PSEUDO_TYPENAME:
805       HandleTypeName(0, key);
806       break;
807
808     case GAME_MODE_TITLE:
809     case GAME_MODE_MAIN:
810     case GAME_MODE_LEVELS:
811     case GAME_MODE_SETUP:
812     case GAME_MODE_INFO:
813     case GAME_MODE_SCORES:
814       switch (key)
815       {
816         case KSYM_space:
817         case KSYM_Return:
818           if (game_status == GAME_MODE_TITLE)
819             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
820           else if (game_status == GAME_MODE_MAIN)
821             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
822           else if (game_status == GAME_MODE_LEVELS)
823             HandleChooseLevel(0, 0, 0, 0, MB_MENU_CHOICE);
824           else if (game_status == GAME_MODE_SETUP)
825             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
826           else if (game_status == GAME_MODE_INFO)
827             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
828           else if (game_status == GAME_MODE_SCORES)
829             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
830           break;
831
832         case KSYM_Escape:
833           if (game_status != GAME_MODE_MAIN)
834             FadeSkipNextFadeIn();
835
836           if (game_status == GAME_MODE_TITLE)
837             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
838           else if (game_status == GAME_MODE_LEVELS)
839             HandleChooseLevel(0, 0, 0, 0, MB_MENU_LEAVE);
840           else if (game_status == GAME_MODE_SETUP)
841             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
842           else if (game_status == GAME_MODE_INFO)
843             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
844           else if (game_status == GAME_MODE_SCORES)
845             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
846           break;
847
848         case KSYM_Page_Up:
849           if (game_status == GAME_MODE_LEVELS)
850             HandleChooseLevel(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
851           else if (game_status == GAME_MODE_SETUP)
852             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
853           else if (game_status == GAME_MODE_INFO)
854             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
855           else if (game_status == GAME_MODE_SCORES)
856             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
857           break;
858
859         case KSYM_Page_Down:
860           if (game_status == GAME_MODE_LEVELS)
861             HandleChooseLevel(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
862           else if (game_status == GAME_MODE_SETUP)
863             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
864           else if (game_status == GAME_MODE_INFO)
865             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
866           else if (game_status == GAME_MODE_SCORES)
867             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
868           break;
869
870 #ifdef DEBUG
871         case KSYM_0:
872           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
873           break;
874
875         case KSYM_b:
876           setup.sp_show_border_elements = !setup.sp_show_border_elements;
877           printf("Supaplex border elements %s\n",
878                  setup.sp_show_border_elements ? "enabled" : "disabled");
879           break;
880 #endif
881
882         default:
883           break;
884       }
885       break;
886
887     case GAME_MODE_EDITOR:
888       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
889         HandleLevelEditorKeyInput(key);
890       break;
891
892     case GAME_MODE_PLAYING:
893     {
894       switch (key)
895       {
896         case KSYM_Escape:
897           RequestQuitGame(setup.ask_on_escape);
898           break;
899
900 #ifdef DEBUG
901         case KSYM_0:
902 #if 0
903         case KSYM_1:
904         case KSYM_2:
905         case KSYM_3:
906         case KSYM_4:
907         case KSYM_5:
908         case KSYM_6:
909         case KSYM_7:
910         case KSYM_8:
911         case KSYM_9:
912 #endif
913           if (key == KSYM_0)
914           {
915             if (GameFrameDelay == 500)
916               GameFrameDelay = GAME_FRAME_DELAY;
917             else
918               GameFrameDelay = 500;
919           }
920           else
921             GameFrameDelay = (key - KSYM_0) * 10;
922           printf("Game speed == %d%% (%d ms delay between two frames)\n",
923                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
924           break;
925
926         case KSYM_d:
927           if (options.debug)
928           {
929             options.debug = FALSE;
930             printf("debug mode disabled\n");
931           }
932           else
933           {
934             options.debug = TRUE;
935             printf("debug mode enabled\n");
936           }
937           break;
938
939         case KSYM_S:
940           if (!global.fps_slowdown)
941           {
942             global.fps_slowdown = TRUE;
943             global.fps_slowdown_factor = 2;
944             printf("fps slowdown enabled -- display only every 2nd frame\n");
945           }
946           else if (global.fps_slowdown_factor == 2)
947           {
948             global.fps_slowdown_factor = 4;
949             printf("fps slowdown enabled -- display only every 4th frame\n");
950           }
951           else
952           {
953             global.fps_slowdown = FALSE;
954             global.fps_slowdown_factor = 1;
955             printf("fps slowdown disabled\n");
956           }
957           break;
958
959         case KSYM_f:
960           ScrollStepSize = TILEX/8;
961           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
962           break;
963
964         case KSYM_g:
965           ScrollStepSize = TILEX/4;
966           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
967           break;
968
969         case KSYM_h:
970           ScrollStepSize = TILEX/2;
971           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
972           break;
973
974         case KSYM_l:
975           ScrollStepSize = TILEX;
976           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
977           break;
978
979         case KSYM_v:
980           printf("::: currently using game engine version %d\n",
981                  game.engine_version);
982           break;
983 #endif
984
985         default:
986           break;
987       }
988       break;
989     }
990
991     default:
992       if (key == KSYM_Escape)
993       {
994         game_status = GAME_MODE_MAIN;
995         DrawMainMenu();
996
997         return;
998       }
999   }
1000 }
1001
1002 void HandleNoEvent()
1003 {
1004   if (button_status && game_status != GAME_MODE_PLAYING)
1005   {
1006     HandleButton(0, 0, -button_status, button_status);
1007
1008     return;
1009   }
1010
1011 #if defined(NETWORK_AVALIABLE)
1012   if (options.network)
1013     HandleNetworking();
1014 #endif
1015
1016   HandleJoystick();
1017 }
1018
1019 static int HandleJoystickForAllPlayers()
1020 {
1021   int i;
1022   int result = 0;
1023
1024   for (i = 0; i < MAX_PLAYERS; i++)
1025   {
1026     byte joy_action = 0;
1027
1028     /*
1029     if (!setup.input[i].use_joystick)
1030       continue;
1031       */
1032
1033     joy_action = Joystick(i);
1034     result |= joy_action;
1035
1036     if (!setup.input[i].use_joystick)
1037       continue;
1038
1039     stored_player[i].action = joy_action;
1040   }
1041
1042   return result;
1043 }
1044
1045 void HandleJoystick()
1046 {
1047   int joystick  = HandleJoystickForAllPlayers();
1048   int keyboard  = key_joystick_mapping;
1049   int joy       = (joystick | keyboard);
1050   int left      = joy & JOY_LEFT;
1051   int right     = joy & JOY_RIGHT;
1052   int up        = joy & JOY_UP;
1053   int down      = joy & JOY_DOWN;
1054   int button    = joy & JOY_BUTTON;
1055   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1056   int dx        = (left ? -1    : right ? 1     : 0);
1057   int dy        = (up   ? -1    : down  ? 1     : 0);
1058
1059   switch (game_status)
1060   {
1061     case GAME_MODE_TITLE:
1062     case GAME_MODE_MAIN:
1063     case GAME_MODE_LEVELS:
1064     case GAME_MODE_SETUP:
1065     case GAME_MODE_INFO:
1066     {
1067       static unsigned long joystickmove_delay = 0;
1068
1069       if (joystick && !button &&
1070           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1071         newbutton = dx = dy = 0;
1072
1073       if (game_status == GAME_MODE_TITLE)
1074         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1075       else if (game_status == GAME_MODE_MAIN)
1076         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1077       else if (game_status == GAME_MODE_LEVELS)
1078         HandleChooseLevel(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1079       else if (game_status == GAME_MODE_SETUP)
1080         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1081       else if (game_status == GAME_MODE_INFO)
1082         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1083       break;
1084     }
1085
1086     case GAME_MODE_SCORES:
1087       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1088       break;
1089
1090     case GAME_MODE_EDITOR:
1091       HandleLevelEditorIdle();
1092       break;
1093
1094     case GAME_MODE_PLAYING:
1095       if (tape.playing || keyboard)
1096         newbutton = ((joy & JOY_BUTTON) != 0);
1097
1098 #if 0
1099       if (local_player->LevelSolved_GameEnd && newbutton)
1100 #else
1101       if (AllPlayersGone && newbutton)
1102 #endif
1103       {
1104         GameEnd();
1105
1106         return;
1107       }
1108
1109       break;
1110
1111     default:
1112       break;
1113   }
1114 }