rnd-20060819-3-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 #endif
227
228     default:
229       break;
230   }
231 }
232
233 void ClearEventQueue()
234 {
235   while (PendingEvent())
236   {
237     Event event;
238
239     NextEvent(&event);
240
241     switch(event.type)
242     {
243       case EVENT_BUTTONRELEASE:
244         button_status = MB_RELEASED;
245         break;
246
247       case EVENT_KEYRELEASE:
248         key_joystick_mapping = 0;
249         break;
250
251       default:
252         HandleOtherEvents(&event);
253         break;
254     }
255   }
256 }
257
258 void ClearPlayerAction()
259 {
260   int i;
261
262   /* simulate key release events for still pressed keys */
263   key_joystick_mapping = 0;
264   for (i = 0; i < MAX_PLAYERS; i++)
265     stored_player[i].action = 0;
266 }
267
268 void SleepWhileUnmapped()
269 {
270   boolean window_unmapped = TRUE;
271
272   KeyboardAutoRepeatOn();
273
274   while (window_unmapped)
275   {
276     Event event;
277
278     NextEvent(&event);
279
280     switch(event.type)
281     {
282       case EVENT_BUTTONRELEASE:
283         button_status = MB_RELEASED;
284         break;
285
286       case EVENT_KEYRELEASE:
287         key_joystick_mapping = 0;
288         break;
289
290       case EVENT_MAPNOTIFY:
291         window_unmapped = FALSE;
292         break;
293
294       case EVENT_UNMAPNOTIFY:
295         /* this is only to surely prevent the 'should not happen' case
296          * of recursively looping between 'SleepWhileUnmapped()' and
297          * 'HandleOtherEvents()' which usually calls this funtion.
298          */
299         break;
300
301       default:
302         HandleOtherEvents(&event);
303         break;
304     }
305   }
306
307   if (game_status == GAME_MODE_PLAYING)
308     KeyboardAutoRepeatOffUnlessAutoplay();
309 }
310
311 void HandleExposeEvent(ExposeEvent *event)
312 {
313 #ifndef TARGET_SDL
314   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
315   FlushDisplay();
316 #endif
317 }
318
319 void HandleButtonEvent(ButtonEvent *event)
320 {
321 #if DEBUG_EVENTS
322   printf("::: BUTTON EVENT: button %d %s\n", event->button,
323          event->type == EVENT_BUTTONPRESS ? "pressed" : "released");
324 #endif
325
326   motion_status = FALSE;
327
328   if (event->type == EVENT_BUTTONPRESS)
329     button_status = event->button;
330   else
331     button_status = MB_RELEASED;
332
333   HandleButton(event->x, event->y, button_status, event->button);
334 }
335
336 void HandleMotionEvent(MotionEvent *event)
337 {
338   if (!PointerInWindow(window))
339     return;     /* window and pointer are on different screens */
340
341   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
342     return;
343
344   motion_status = TRUE;
345
346   HandleButton(event->x, event->y, button_status, button_status);
347 }
348
349 void HandleKeyEvent(KeyEvent *event)
350 {
351   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
352   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
353   Key key = GetEventKey(event, with_modifiers);
354   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
355
356 #if DEBUG_EVENTS
357   printf("::: KEY EVENT: %d %s\n", GetEventKey(event, TRUE),
358          event->type == EVENT_KEYPRESS ? "pressed" : "released");
359 #endif
360
361   HandleKeyModState(keymod, key_status);
362   HandleKey(key, key_status);
363 }
364
365 void HandleFocusEvent(FocusChangeEvent *event)
366 {
367   static int old_joystick_status = -1;
368
369   if (event->type == EVENT_FOCUSOUT)
370   {
371     KeyboardAutoRepeatOn();
372     old_joystick_status = joystick.status;
373     joystick.status = JOYSTICK_NOT_AVAILABLE;
374
375     ClearPlayerAction();
376   }
377   else if (event->type == EVENT_FOCUSIN)
378   {
379     /* When there are two Rocks'n'Diamonds windows which overlap and
380        the player moves the pointer from one game window to the other,
381        a 'FocusOut' event is generated for the window the pointer is
382        leaving and a 'FocusIn' event is generated for the window the
383        pointer is entering. In some cases, it can happen that the
384        'FocusIn' event is handled by the one game process before the
385        'FocusOut' event by the other game process. In this case the
386        X11 environment would end up with activated keyboard auto repeat,
387        because unfortunately this is a global setting and not (which
388        would be far better) set for each X11 window individually.
389        The effect would be keyboard auto repeat while playing the game
390        (game_status == GAME_MODE_PLAYING), which is not desired.
391        To avoid this special case, we just wait 1/10 second before
392        processing the 'FocusIn' event.
393     */
394
395     if (game_status == GAME_MODE_PLAYING)
396     {
397       Delay(100);
398       KeyboardAutoRepeatOffUnlessAutoplay();
399     }
400
401     if (old_joystick_status != -1)
402       joystick.status = old_joystick_status;
403   }
404 }
405
406 void HandleClientMessageEvent(ClientMessageEvent *event)
407 {
408   if (CheckCloseWindowEvent(event))
409     CloseAllAndExit(0);
410 }
411
412 void HandleButton(int mx, int my, int button, int button_nr)
413 {
414   static int old_mx = 0, old_my = 0;
415
416   if (button < 0)
417   {
418     mx = old_mx;
419     my = old_my;
420     button = -button;
421   }
422   else
423   {
424     old_mx = mx;
425     old_my = my;
426   }
427
428   if (HandleGadgets(mx, my, button))
429   {
430     /* do not handle this button event anymore */
431     mx = my = -32;      /* force mouse event to be outside screen tiles */
432   }
433
434   /* do not use scroll wheel button events for anything other than gadgets */
435   if (IS_WHEEL_BUTTON(button_nr))
436     return;
437
438   switch (game_status)
439   {
440     case GAME_MODE_TITLE:
441       HandleTitleScreen(mx, my, 0, 0, button);
442       break;
443
444     case GAME_MODE_MAIN:
445       HandleMainMenu(mx, my, 0, 0, button);
446       break;
447
448     case GAME_MODE_PSEUDO_TYPENAME:
449       HandleTypeName(0, KSYM_Return);
450       break;
451
452     case GAME_MODE_LEVELS:
453       HandleChooseLevel(mx, my, 0, 0, button);
454       break;
455
456     case GAME_MODE_SCORES:
457       HandleHallOfFame(0, 0, 0, 0, button);
458       break;
459
460     case GAME_MODE_EDITOR:
461       HandleLevelEditorIdle();
462       break;
463
464     case GAME_MODE_INFO:
465       HandleInfoScreen(mx, my, 0, 0, button);
466       break;
467
468     case GAME_MODE_SETUP:
469       HandleSetupScreen(mx, my, 0, 0, button);
470       break;
471
472     case GAME_MODE_PLAYING:
473 #ifdef DEBUG
474       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
475         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
476 #endif
477       break;
478
479     default:
480       break;
481   }
482 }
483
484 static boolean is_string_suffix(char *string, char *suffix)
485 {
486   int string_len = strlen(string);
487   int suffix_len = strlen(suffix);
488
489   if (suffix_len > string_len)
490     return FALSE;
491
492   return (strEqual(&string[string_len - suffix_len], suffix));
493 }
494
495 #define MAX_CHEAT_INPUT_LEN     32
496
497 static void HandleKeysSpecial(Key key)
498 {
499   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
500   char letter = getCharFromKey(key);
501   int cheat_input_len = strlen(cheat_input);
502   int i;
503
504   if (letter == 0)
505     return;
506
507   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
508   {
509     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
510       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
511
512     cheat_input_len = MAX_CHEAT_INPUT_LEN;
513   }
514
515   cheat_input[cheat_input_len++] = letter;
516   cheat_input[cheat_input_len] = '\0';
517
518 #if DEBUG_EVENTS
519   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
520 #endif
521
522   if (game_status == GAME_MODE_MAIN)
523   {
524     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
525         is_string_suffix(cheat_input, ":ist"))
526     {
527       InsertSolutionTape();
528     }
529     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
530              is_string_suffix(cheat_input, ":rg"))
531     {
532       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
533       DrawMainMenu();
534     }
535     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
536              is_string_suffix(cheat_input, ":rs"))
537     {
538       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
539       DrawMainMenu();
540     }
541     else if (is_string_suffix(cheat_input, ":reload-music") ||
542              is_string_suffix(cheat_input, ":rm"))
543     {
544       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
545       DrawMainMenu();
546     }
547     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
548              is_string_suffix(cheat_input, ":ra"))
549     {
550       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
551                           1 << ARTWORK_TYPE_SOUNDS |
552                           1 << ARTWORK_TYPE_MUSIC);
553       DrawMainMenu();
554     }
555     else if (is_string_suffix(cheat_input, ":dump-level") ||
556              is_string_suffix(cheat_input, ":dl"))
557     {
558       DumpLevel(&level);
559     }
560     else if (is_string_suffix(cheat_input, ":dump-tape") ||
561              is_string_suffix(cheat_input, ":dt"))
562     {
563       DumpTape(&tape);
564     }
565   }
566   else if (game_status == GAME_MODE_PLAYING)
567   {
568 #ifdef DEBUG
569     if (is_string_suffix(cheat_input, ".q"))
570       DEBUG_SetMaximumDynamite();
571 #endif
572   }
573   else if (game_status == GAME_MODE_EDITOR)
574   {
575     if (is_string_suffix(cheat_input, ":dump-brush") ||
576         is_string_suffix(cheat_input, ":DB"))
577     {
578       DumpBrush();
579     }
580     else if (is_string_suffix(cheat_input, ":DDB"))
581     {
582       DumpBrush_Small();
583     }
584   }
585 }
586
587 void HandleKey(Key key, int key_status)
588 {
589   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
590   static struct SetupKeyboardInfo custom_key;
591   static struct
592   {
593     Key *key_custom;
594     Key key_default;
595     byte action;
596   } key_info[] =
597   {
598     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
599     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
600     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
601     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
602     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
603     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
604   };
605   int joy = 0;
606   int i;
607
608   if (game_status == GAME_MODE_PLAYING)
609   {
610     /* only needed for single-step tape recording mode */
611     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
612     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
613     int pnr;
614
615     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
616     {
617       byte key_action = 0;
618
619       if (setup.input[pnr].use_joystick)
620         continue;
621
622       custom_key = setup.input[pnr].key;
623
624       for (i = 0; i < 6; i++)
625         if (key == *key_info[i].key_custom)
626           key_action |= key_info[i].action;
627
628       if (tape.single_step && clear_button_2[pnr])
629       {
630         stored_player[pnr].action &= ~KEY_BUTTON_2;
631         clear_button_2[pnr] = FALSE;
632       }
633
634       if (key_status == KEY_PRESSED)
635         stored_player[pnr].action |= key_action;
636       else
637         stored_player[pnr].action &= ~key_action;
638
639       if (tape.single_step && tape.recording && tape.pausing)
640       {
641         if (key_status == KEY_PRESSED &&
642             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
643         {
644           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
645
646           if (key_action & KEY_MOTION)
647           {
648             if (stored_player[pnr].action & KEY_BUTTON_2)
649               element_dropped[pnr] = TRUE;
650           }
651         }
652         else if (key_status == KEY_RELEASED &&
653                  (key_action & KEY_BUTTON_2))
654         {
655           if (!element_dropped[pnr])
656           {
657             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
658
659             stored_player[pnr].action |= KEY_BUTTON_2;
660             clear_button_2[pnr] = TRUE;
661           }
662
663           element_dropped[pnr] = FALSE;
664         }
665       }
666       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
667         TapeTogglePause(TAPE_TOGGLE_MANUAL);
668     }
669   }
670   else
671   {
672     for (i = 0; i < 6; i++)
673       if (key == key_info[i].key_default)
674         joy |= key_info[i].action;
675   }
676
677   if (joy)
678   {
679     if (key_status == KEY_PRESSED)
680       key_joystick_mapping |= joy;
681     else
682       key_joystick_mapping &= ~joy;
683
684     HandleJoystick();
685   }
686
687   if (game_status != GAME_MODE_PLAYING)
688     key_joystick_mapping = 0;
689
690   if (key_status == KEY_RELEASED)
691     return;
692
693   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
694       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
695   {
696     setup.fullscreen = !setup.fullscreen;
697
698     ToggleFullscreenIfNeeded();
699
700     return;
701   }
702
703   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
704       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
705   {
706     GameEnd();
707
708     return;
709   }
710
711   if (game_status == GAME_MODE_MAIN &&
712       (key == setup.shortcut.toggle_pause || key == KSYM_space))
713   {
714     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
715
716     return;
717   }
718
719   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
720   {
721     if (key == setup.shortcut.save_game)
722       TapeQuickSave();
723     else if (key == setup.shortcut.load_game)
724       TapeQuickLoad();
725     else if (key == setup.shortcut.toggle_pause)
726       TapeTogglePause(TAPE_TOGGLE_MANUAL);
727   }
728
729   if (game_status == GAME_MODE_PLAYING && !network_playing)
730   {
731     int centered_player_nr_next = -999;
732
733     if (key == setup.shortcut.focus_player_all)
734       centered_player_nr_next = -1;
735     else
736       for (i = 0; i < MAX_PLAYERS; i++)
737         if (key == setup.shortcut.focus_player[i])
738           centered_player_nr_next = i;
739
740     if (centered_player_nr_next != -999)
741     {
742       game.centered_player_nr_next = centered_player_nr_next;
743       game.set_centered_player = TRUE;
744
745       if (tape.recording)
746       {
747         tape.centered_player_nr_next = game.centered_player_nr_next;
748         tape.set_centered_player = TRUE;
749       }
750     }
751   }
752
753   HandleKeysSpecial(key);
754
755   if (HandleGadgetsKeyInput(key))
756   {
757     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
758       key = KSYM_UNDEFINED;
759   }
760
761   switch(game_status)
762   {
763     case GAME_MODE_PSEUDO_TYPENAME:
764       HandleTypeName(0, key);
765       break;
766
767     case GAME_MODE_TITLE:
768     case GAME_MODE_MAIN:
769     case GAME_MODE_LEVELS:
770     case GAME_MODE_SETUP:
771     case GAME_MODE_INFO:
772     case GAME_MODE_SCORES:
773       switch(key)
774       {
775         case KSYM_space:
776         case KSYM_Return:
777           if (game_status == GAME_MODE_TITLE)
778             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
779           else if (game_status == GAME_MODE_MAIN)
780             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
781           else if (game_status == GAME_MODE_LEVELS)
782             HandleChooseLevel(0, 0, 0, 0, MB_MENU_CHOICE);
783           else if (game_status == GAME_MODE_SETUP)
784             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
785           else if (game_status == GAME_MODE_INFO)
786             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
787           else if (game_status == GAME_MODE_SCORES)
788             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
789           break;
790
791         case KSYM_Escape:
792           if (game_status == GAME_MODE_TITLE)
793             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
794           else if (game_status == GAME_MODE_LEVELS)
795             HandleChooseLevel(0, 0, 0, 0, MB_MENU_LEAVE);
796           else if (game_status == GAME_MODE_SETUP)
797             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
798           else if (game_status == GAME_MODE_INFO)
799             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
800           else if (game_status == GAME_MODE_SCORES)
801             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
802           break;
803
804         case KSYM_Page_Up:
805           if (game_status == GAME_MODE_LEVELS)
806             HandleChooseLevel(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
807           else if (game_status == GAME_MODE_SETUP)
808             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
809           else if (game_status == GAME_MODE_INFO)
810             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
811           else if (game_status == GAME_MODE_SCORES)
812             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
813           break;
814
815         case KSYM_Page_Down:
816           if (game_status == GAME_MODE_LEVELS)
817             HandleChooseLevel(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
818           else if (game_status == GAME_MODE_SETUP)
819             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
820           else if (game_status == GAME_MODE_INFO)
821             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
822           else if (game_status == GAME_MODE_SCORES)
823             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
824           break;
825
826 #ifdef DEBUG
827         case KSYM_0:
828           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
829           break;
830 #endif
831
832         default:
833           break;
834       }
835       break;
836
837     case GAME_MODE_EDITOR:
838       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
839         HandleLevelEditorKeyInput(key);
840       break;
841
842     case GAME_MODE_PLAYING:
843     {
844       switch(key)
845       {
846         case KSYM_Escape:
847           RequestQuitGame(setup.ask_on_escape);
848           break;
849
850 #ifdef DEBUG
851         case KSYM_0:
852 #if 0
853         case KSYM_1:
854         case KSYM_2:
855         case KSYM_3:
856         case KSYM_4:
857         case KSYM_5:
858         case KSYM_6:
859         case KSYM_7:
860         case KSYM_8:
861         case KSYM_9:
862 #endif
863           if (key == KSYM_0)
864           {
865             if (GameFrameDelay == 500)
866               GameFrameDelay = GAME_FRAME_DELAY;
867             else
868               GameFrameDelay = 500;
869           }
870           else
871             GameFrameDelay = (key - KSYM_0) * 10;
872           printf("Game speed == %d%% (%d ms delay between two frames)\n",
873                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
874           break;
875
876         case KSYM_d:
877           if (options.debug)
878           {
879             options.debug = FALSE;
880             printf("debug mode disabled\n");
881           }
882           else
883           {
884             options.debug = TRUE;
885             printf("debug mode enabled\n");
886           }
887           break;
888
889         case KSYM_S:
890           if (!global.fps_slowdown)
891           {
892             global.fps_slowdown = TRUE;
893             global.fps_slowdown_factor = 2;
894             printf("fps slowdown enabled -- display only every 2nd frame\n");
895           }
896           else if (global.fps_slowdown_factor == 2)
897           {
898             global.fps_slowdown_factor = 4;
899             printf("fps slowdown enabled -- display only every 4th frame\n");
900           }
901           else
902           {
903             global.fps_slowdown = FALSE;
904             global.fps_slowdown_factor = 1;
905             printf("fps slowdown disabled\n");
906           }
907           break;
908
909         case KSYM_f:
910           ScrollStepSize = TILEX/8;
911           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
912           break;
913
914         case KSYM_g:
915           ScrollStepSize = TILEX/4;
916           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
917           break;
918
919         case KSYM_h:
920           ScrollStepSize = TILEX/2;
921           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
922           break;
923
924         case KSYM_l:
925           ScrollStepSize = TILEX;
926           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
927           break;
928
929         case KSYM_v:
930           printf("::: currently using game engine version %d\n",
931                  game.engine_version);
932           break;
933 #endif
934
935         default:
936           break;
937       }
938       break;
939     }
940
941     default:
942       if (key == KSYM_Escape)
943       {
944         game_status = GAME_MODE_MAIN;
945         DrawMainMenu();
946
947         return;
948       }
949   }
950 }
951
952 void HandleNoEvent()
953 {
954   if (button_status && game_status != GAME_MODE_PLAYING)
955   {
956     HandleButton(0, 0, -button_status, button_status);
957
958     return;
959   }
960
961 #if defined(NETWORK_AVALIABLE)
962   if (options.network)
963     HandleNetworking();
964 #endif
965
966   HandleJoystick();
967 }
968
969 static int HandleJoystickForAllPlayers()
970 {
971   int i;
972   int result = 0;
973
974   for (i = 0; i < MAX_PLAYERS; i++)
975   {
976     byte joy_action = 0;
977
978     /*
979     if (!setup.input[i].use_joystick)
980       continue;
981       */
982
983     joy_action = Joystick(i);
984     result |= joy_action;
985
986     if (!setup.input[i].use_joystick)
987       continue;
988
989     stored_player[i].action = joy_action;
990   }
991
992   return result;
993 }
994
995 void HandleJoystick()
996 {
997   int joystick  = HandleJoystickForAllPlayers();
998   int keyboard  = key_joystick_mapping;
999   int joy       = (joystick | keyboard);
1000   int left      = joy & JOY_LEFT;
1001   int right     = joy & JOY_RIGHT;
1002   int up        = joy & JOY_UP;
1003   int down      = joy & JOY_DOWN;
1004   int button    = joy & JOY_BUTTON;
1005   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1006   int dx        = (left ? -1    : right ? 1     : 0);
1007   int dy        = (up   ? -1    : down  ? 1     : 0);
1008
1009   switch(game_status)
1010   {
1011     case GAME_MODE_TITLE:
1012     case GAME_MODE_MAIN:
1013     case GAME_MODE_LEVELS:
1014     case GAME_MODE_SETUP:
1015     case GAME_MODE_INFO:
1016     {
1017       static unsigned long joystickmove_delay = 0;
1018
1019       if (joystick && !button &&
1020           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1021         newbutton = dx = dy = 0;
1022
1023       if (game_status == GAME_MODE_TITLE)
1024         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1025       else if (game_status == GAME_MODE_MAIN)
1026         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1027       else if (game_status == GAME_MODE_LEVELS)
1028         HandleChooseLevel(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1029       else if (game_status == GAME_MODE_SETUP)
1030         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1031       else if (game_status == GAME_MODE_INFO)
1032         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1033       break;
1034     }
1035
1036     case GAME_MODE_SCORES:
1037       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1038       break;
1039
1040     case GAME_MODE_EDITOR:
1041       HandleLevelEditorIdle();
1042       break;
1043
1044     case GAME_MODE_PLAYING:
1045       if (tape.playing || keyboard)
1046         newbutton = ((joy & JOY_BUTTON) != 0);
1047
1048       if (AllPlayersGone && newbutton)
1049       {
1050         GameEnd();
1051
1052         return;
1053       }
1054
1055       break;
1056
1057     default:
1058       break;
1059   }
1060 }