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