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