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