rnd-20080802-1-src
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * events.c                                                 *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "events.h"
17 #include "init.h"
18 #include "screens.h"
19 #include "tools.h"
20 #include "game.h"
21 #include "editor.h"
22 #include "files.h"
23 #include "tape.h"
24 #include "network.h"
25
26
27 #define DEBUG_EVENTS            0
28
29
30 static boolean cursor_inside_playfield = FALSE;
31 static boolean playfield_cursor_set = FALSE;
32 static unsigned 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   }
581   else if (game_status == GAME_MODE_PLAYING)
582   {
583 #ifdef DEBUG
584     if (is_string_suffix(cheat_input, ".q"))
585       DEBUG_SetMaximumDynamite();
586 #endif
587   }
588   else if (game_status == GAME_MODE_EDITOR)
589   {
590     if (is_string_suffix(cheat_input, ":dump-brush") ||
591         is_string_suffix(cheat_input, ":DB"))
592     {
593       DumpBrush();
594     }
595     else if (is_string_suffix(cheat_input, ":DDB"))
596     {
597       DumpBrush_Small();
598     }
599   }
600 }
601
602 void HandleKey(Key key, int key_status)
603 {
604   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
605   static struct SetupKeyboardInfo custom_key;
606   static struct
607   {
608     Key *key_custom;
609     Key key_default;
610     byte action;
611   } key_info[] =
612   {
613     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
614     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
615     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
616     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
617     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
618     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
619   };
620   int joy = 0;
621   int i;
622
623   if (game_status == GAME_MODE_PLAYING)
624   {
625     /* only needed for single-step tape recording mode */
626     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
627     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
628     int pnr;
629
630     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
631     {
632       byte key_action = 0;
633
634       if (setup.input[pnr].use_joystick)
635         continue;
636
637       custom_key = setup.input[pnr].key;
638
639       for (i = 0; i < 6; i++)
640         if (key == *key_info[i].key_custom)
641           key_action |= key_info[i].action;
642
643       if (tape.single_step && clear_button_2[pnr])
644       {
645         stored_player[pnr].action &= ~KEY_BUTTON_2;
646         clear_button_2[pnr] = FALSE;
647       }
648
649       if (key_status == KEY_PRESSED)
650         stored_player[pnr].action |= key_action;
651       else
652         stored_player[pnr].action &= ~key_action;
653
654       if (tape.single_step && tape.recording && tape.pausing)
655       {
656         if (key_status == KEY_PRESSED &&
657             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
658         {
659           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
660
661           if (key_action & KEY_MOTION)
662           {
663             if (stored_player[pnr].action & KEY_BUTTON_2)
664               element_dropped[pnr] = TRUE;
665           }
666         }
667         else if (key_status == KEY_RELEASED &&
668                  (key_action & KEY_BUTTON_2))
669         {
670           if (!element_dropped[pnr])
671           {
672             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
673
674             stored_player[pnr].action |= KEY_BUTTON_2;
675             clear_button_2[pnr] = TRUE;
676           }
677
678           element_dropped[pnr] = FALSE;
679         }
680       }
681 #if 1
682       else if (tape.recording && tape.pausing)
683       {
684         /* prevent key release events from un-pausing a paused game */
685         if (key_status == KEY_PRESSED &&
686             (key_action & KEY_ACTION))
687           TapeTogglePause(TAPE_TOGGLE_MANUAL);
688       }
689 #else
690       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
691         TapeTogglePause(TAPE_TOGGLE_MANUAL);
692 #endif
693     }
694   }
695   else
696   {
697     for (i = 0; i < 6; i++)
698       if (key == key_info[i].key_default)
699         joy |= key_info[i].action;
700   }
701
702   if (joy)
703   {
704     if (key_status == KEY_PRESSED)
705       key_joystick_mapping |= joy;
706     else
707       key_joystick_mapping &= ~joy;
708
709     HandleJoystick();
710   }
711
712   if (game_status != GAME_MODE_PLAYING)
713     key_joystick_mapping = 0;
714
715   if (key_status == KEY_RELEASED)
716     return;
717
718   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
719       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
720   {
721     setup.fullscreen = !setup.fullscreen;
722
723     ToggleFullscreenIfNeeded();
724
725     if (game_status == GAME_MODE_SETUP)
726       RedrawSetupScreenAfterFullscreenToggle();
727
728     return;
729   }
730
731 #if 0
732   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
733       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
734 #else
735   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
736       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
737 #endif
738   {
739     GameEnd();
740
741     return;
742   }
743
744   if (game_status == GAME_MODE_MAIN &&
745       (key == setup.shortcut.toggle_pause || key == KSYM_space))
746   {
747     StartGameActions(options.network, setup.autorecord, level.random_seed);
748
749     return;
750   }
751
752   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
753   {
754     if (key == setup.shortcut.save_game)
755       TapeQuickSave();
756     else if (key == setup.shortcut.load_game)
757       TapeQuickLoad();
758     else if (key == setup.shortcut.toggle_pause)
759       TapeTogglePause(TAPE_TOGGLE_MANUAL);
760   }
761
762   if (game_status == GAME_MODE_PLAYING && !network_playing)
763   {
764     int centered_player_nr_next = -999;
765
766     if (key == setup.shortcut.focus_player_all)
767       centered_player_nr_next = -1;
768     else
769       for (i = 0; i < MAX_PLAYERS; i++)
770         if (key == setup.shortcut.focus_player[i])
771           centered_player_nr_next = i;
772
773     if (centered_player_nr_next != -999)
774     {
775       game.centered_player_nr_next = centered_player_nr_next;
776       game.set_centered_player = TRUE;
777
778       if (tape.recording)
779       {
780         tape.centered_player_nr_next = game.centered_player_nr_next;
781         tape.set_centered_player = TRUE;
782       }
783     }
784   }
785
786   HandleKeysSpecial(key);
787
788   if (HandleGadgetsKeyInput(key))
789   {
790     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
791       key = KSYM_UNDEFINED;
792   }
793
794   switch (game_status)
795   {
796     case GAME_MODE_PSEUDO_TYPENAME:
797       HandleTypeName(0, key);
798       break;
799
800     case GAME_MODE_TITLE:
801     case GAME_MODE_MAIN:
802     case GAME_MODE_LEVELS:
803     case GAME_MODE_SETUP:
804     case GAME_MODE_INFO:
805     case GAME_MODE_SCORES:
806       switch (key)
807       {
808         case KSYM_space:
809         case KSYM_Return:
810           if (game_status == GAME_MODE_TITLE)
811             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
812           else if (game_status == GAME_MODE_MAIN)
813             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
814           else if (game_status == GAME_MODE_LEVELS)
815             HandleChooseLevel(0, 0, 0, 0, MB_MENU_CHOICE);
816           else if (game_status == GAME_MODE_SETUP)
817             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
818           else if (game_status == GAME_MODE_INFO)
819             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
820           else if (game_status == GAME_MODE_SCORES)
821             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
822           break;
823
824         case KSYM_Escape:
825           if (game_status != GAME_MODE_MAIN)
826             FadeSkipNextFadeIn();
827
828           if (game_status == GAME_MODE_TITLE)
829             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
830           else if (game_status == GAME_MODE_LEVELS)
831             HandleChooseLevel(0, 0, 0, 0, MB_MENU_LEAVE);
832           else if (game_status == GAME_MODE_SETUP)
833             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
834           else if (game_status == GAME_MODE_INFO)
835             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
836           else if (game_status == GAME_MODE_SCORES)
837             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
838           break;
839
840         case KSYM_Page_Up:
841           if (game_status == GAME_MODE_LEVELS)
842             HandleChooseLevel(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
843           else if (game_status == GAME_MODE_SETUP)
844             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
845           else if (game_status == GAME_MODE_INFO)
846             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
847           else if (game_status == GAME_MODE_SCORES)
848             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
849           break;
850
851         case KSYM_Page_Down:
852           if (game_status == GAME_MODE_LEVELS)
853             HandleChooseLevel(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
854           else if (game_status == GAME_MODE_SETUP)
855             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
856           else if (game_status == GAME_MODE_INFO)
857             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
858           else if (game_status == GAME_MODE_SCORES)
859             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
860           break;
861
862 #ifdef DEBUG
863         case KSYM_0:
864           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
865           break;
866 #endif
867
868         default:
869           break;
870       }
871       break;
872
873     case GAME_MODE_EDITOR:
874       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
875         HandleLevelEditorKeyInput(key);
876       break;
877
878     case GAME_MODE_PLAYING:
879     {
880       switch (key)
881       {
882         case KSYM_Escape:
883           RequestQuitGame(setup.ask_on_escape);
884           break;
885
886 #ifdef DEBUG
887         case KSYM_0:
888 #if 0
889         case KSYM_1:
890         case KSYM_2:
891         case KSYM_3:
892         case KSYM_4:
893         case KSYM_5:
894         case KSYM_6:
895         case KSYM_7:
896         case KSYM_8:
897         case KSYM_9:
898 #endif
899           if (key == KSYM_0)
900           {
901             if (GameFrameDelay == 500)
902               GameFrameDelay = GAME_FRAME_DELAY;
903             else
904               GameFrameDelay = 500;
905           }
906           else
907             GameFrameDelay = (key - KSYM_0) * 10;
908           printf("Game speed == %d%% (%d ms delay between two frames)\n",
909                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
910           break;
911
912         case KSYM_d:
913           if (options.debug)
914           {
915             options.debug = FALSE;
916             printf("debug mode disabled\n");
917           }
918           else
919           {
920             options.debug = TRUE;
921             printf("debug mode enabled\n");
922           }
923           break;
924
925         case KSYM_S:
926           if (!global.fps_slowdown)
927           {
928             global.fps_slowdown = TRUE;
929             global.fps_slowdown_factor = 2;
930             printf("fps slowdown enabled -- display only every 2nd frame\n");
931           }
932           else if (global.fps_slowdown_factor == 2)
933           {
934             global.fps_slowdown_factor = 4;
935             printf("fps slowdown enabled -- display only every 4th frame\n");
936           }
937           else
938           {
939             global.fps_slowdown = FALSE;
940             global.fps_slowdown_factor = 1;
941             printf("fps slowdown disabled\n");
942           }
943           break;
944
945         case KSYM_f:
946           ScrollStepSize = TILEX/8;
947           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
948           break;
949
950         case KSYM_g:
951           ScrollStepSize = TILEX/4;
952           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
953           break;
954
955         case KSYM_h:
956           ScrollStepSize = TILEX/2;
957           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
958           break;
959
960         case KSYM_l:
961           ScrollStepSize = TILEX;
962           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
963           break;
964
965         case KSYM_v:
966           printf("::: currently using game engine version %d\n",
967                  game.engine_version);
968           break;
969 #endif
970
971         default:
972           break;
973       }
974       break;
975     }
976
977     default:
978       if (key == KSYM_Escape)
979       {
980         game_status = GAME_MODE_MAIN;
981         DrawMainMenu();
982
983         return;
984       }
985   }
986 }
987
988 void HandleNoEvent()
989 {
990   if (button_status && game_status != GAME_MODE_PLAYING)
991   {
992     HandleButton(0, 0, -button_status, button_status);
993
994     return;
995   }
996
997 #if defined(NETWORK_AVALIABLE)
998   if (options.network)
999     HandleNetworking();
1000 #endif
1001
1002   HandleJoystick();
1003 }
1004
1005 static int HandleJoystickForAllPlayers()
1006 {
1007   int i;
1008   int result = 0;
1009
1010   for (i = 0; i < MAX_PLAYERS; i++)
1011   {
1012     byte joy_action = 0;
1013
1014     /*
1015     if (!setup.input[i].use_joystick)
1016       continue;
1017       */
1018
1019     joy_action = Joystick(i);
1020     result |= joy_action;
1021
1022     if (!setup.input[i].use_joystick)
1023       continue;
1024
1025     stored_player[i].action = joy_action;
1026   }
1027
1028   return result;
1029 }
1030
1031 void HandleJoystick()
1032 {
1033   int joystick  = HandleJoystickForAllPlayers();
1034   int keyboard  = key_joystick_mapping;
1035   int joy       = (joystick | keyboard);
1036   int left      = joy & JOY_LEFT;
1037   int right     = joy & JOY_RIGHT;
1038   int up        = joy & JOY_UP;
1039   int down      = joy & JOY_DOWN;
1040   int button    = joy & JOY_BUTTON;
1041   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1042   int dx        = (left ? -1    : right ? 1     : 0);
1043   int dy        = (up   ? -1    : down  ? 1     : 0);
1044
1045   switch (game_status)
1046   {
1047     case GAME_MODE_TITLE:
1048     case GAME_MODE_MAIN:
1049     case GAME_MODE_LEVELS:
1050     case GAME_MODE_SETUP:
1051     case GAME_MODE_INFO:
1052     {
1053       static unsigned long joystickmove_delay = 0;
1054
1055       if (joystick && !button &&
1056           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1057         newbutton = dx = dy = 0;
1058
1059       if (game_status == GAME_MODE_TITLE)
1060         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1061       else if (game_status == GAME_MODE_MAIN)
1062         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1063       else if (game_status == GAME_MODE_LEVELS)
1064         HandleChooseLevel(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1065       else if (game_status == GAME_MODE_SETUP)
1066         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1067       else if (game_status == GAME_MODE_INFO)
1068         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1069       break;
1070     }
1071
1072     case GAME_MODE_SCORES:
1073       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1074       break;
1075
1076     case GAME_MODE_EDITOR:
1077       HandleLevelEditorIdle();
1078       break;
1079
1080     case GAME_MODE_PLAYING:
1081       if (tape.playing || keyboard)
1082         newbutton = ((joy & JOY_BUTTON) != 0);
1083
1084 #if 0
1085       if (local_player->LevelSolved_GameEnd && newbutton)
1086 #else
1087       if (AllPlayersGone && newbutton)
1088 #endif
1089       {
1090         GameEnd();
1091
1092         return;
1093       }
1094
1095       break;
1096
1097     default:
1098       break;
1099   }
1100 }