rnd-20111007-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 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 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       HandleChooseLevelSet(mx, my, 0, 0, button);
469       break;
470
471     case GAME_MODE_LEVELNR:
472       HandleChooseLevelNr(mx, my, 0, 0, button);
473       break;
474
475     case GAME_MODE_SCORES:
476       HandleHallOfFame(0, 0, 0, 0, button);
477       break;
478
479     case GAME_MODE_EDITOR:
480       HandleLevelEditorIdle();
481       break;
482
483     case GAME_MODE_INFO:
484       HandleInfoScreen(mx, my, 0, 0, button);
485       break;
486
487     case GAME_MODE_SETUP:
488       HandleSetupScreen(mx, my, 0, 0, button);
489       break;
490
491     case GAME_MODE_PLAYING:
492 #ifdef DEBUG
493       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
494         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
495 #endif
496       break;
497
498     default:
499       break;
500   }
501 }
502
503 static boolean is_string_suffix(char *string, char *suffix)
504 {
505   int string_len = strlen(string);
506   int suffix_len = strlen(suffix);
507
508   if (suffix_len > string_len)
509     return FALSE;
510
511   return (strEqual(&string[string_len - suffix_len], suffix));
512 }
513
514 #define MAX_CHEAT_INPUT_LEN     32
515
516 static void HandleKeysSpecial(Key key)
517 {
518   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
519   char letter = getCharFromKey(key);
520   int cheat_input_len = strlen(cheat_input);
521   int i;
522
523   if (letter == 0)
524     return;
525
526   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
527   {
528     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
529       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
530
531     cheat_input_len = MAX_CHEAT_INPUT_LEN;
532   }
533
534   cheat_input[cheat_input_len++] = letter;
535   cheat_input[cheat_input_len] = '\0';
536
537 #if DEBUG_EVENTS
538   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
539 #endif
540
541   if (game_status == GAME_MODE_MAIN)
542   {
543     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
544         is_string_suffix(cheat_input, ":ist"))
545     {
546       InsertSolutionTape();
547     }
548     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
549              is_string_suffix(cheat_input, ":rg"))
550     {
551       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
552       DrawMainMenu();
553     }
554     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
555              is_string_suffix(cheat_input, ":rs"))
556     {
557       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
558       DrawMainMenu();
559     }
560     else if (is_string_suffix(cheat_input, ":reload-music") ||
561              is_string_suffix(cheat_input, ":rm"))
562     {
563       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
564       DrawMainMenu();
565     }
566     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
567              is_string_suffix(cheat_input, ":ra"))
568     {
569       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
570                           1 << ARTWORK_TYPE_SOUNDS |
571                           1 << ARTWORK_TYPE_MUSIC);
572       DrawMainMenu();
573     }
574     else if (is_string_suffix(cheat_input, ":dump-level") ||
575              is_string_suffix(cheat_input, ":dl"))
576     {
577       DumpLevel(&level);
578     }
579     else if (is_string_suffix(cheat_input, ":dump-tape") ||
580              is_string_suffix(cheat_input, ":dt"))
581     {
582       DumpTape(&tape);
583     }
584     else if (is_string_suffix(cheat_input, ":save-native-level") ||
585              is_string_suffix(cheat_input, ":snl"))
586     {
587       SaveNativeLevel(&level);
588     }
589   }
590   else if (game_status == GAME_MODE_PLAYING)
591   {
592 #ifdef DEBUG
593     if (is_string_suffix(cheat_input, ".q"))
594       DEBUG_SetMaximumDynamite();
595 #endif
596   }
597   else if (game_status == GAME_MODE_EDITOR)
598   {
599     if (is_string_suffix(cheat_input, ":dump-brush") ||
600         is_string_suffix(cheat_input, ":DB"))
601     {
602       DumpBrush();
603     }
604     else if (is_string_suffix(cheat_input, ":DDB"))
605     {
606       DumpBrush_Small();
607     }
608   }
609 }
610
611 void HandleKey(Key key, int key_status)
612 {
613   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
614   static struct SetupKeyboardInfo ski;
615   static struct SetupShortcutInfo ssi;
616   static struct
617   {
618     Key *key_custom;
619     Key *key_snap;
620     Key key_default;
621     byte action;
622   } key_info[] =
623   {
624     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
625     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
626     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
627     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
628     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
629     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
630   };
631   int joy = 0;
632   int i;
633
634   if (game_status == GAME_MODE_PLAYING)
635   {
636     /* only needed for single-step tape recording mode */
637     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
638     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
639     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
640     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
641     int pnr;
642
643     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
644     {
645       byte key_action = 0;
646
647       if (setup.input[pnr].use_joystick)
648         continue;
649
650       ski = setup.input[pnr].key;
651
652       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
653         if (key == *key_info[i].key_custom)
654           key_action |= key_info[i].action;
655
656       /* use combined snap+direction keys for the first player only */
657       if (pnr == 0)
658       {
659         ssi = setup.shortcut;
660
661         for (i = 0; i < NUM_DIRECTIONS; i++)
662           if (key == *key_info[i].key_snap)
663             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
664       }
665
666       /* clear delayed snap and drop actions in single step mode (see below) */
667       if (tape.single_step)
668       {
669         if (clear_snap_button[pnr])
670         {
671           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
672           clear_snap_button[pnr] = FALSE;
673         }
674
675         if (clear_drop_button[pnr])
676         {
677           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
678           clear_drop_button[pnr] = FALSE;
679         }
680       }
681
682       if (key_status == KEY_PRESSED)
683         stored_player[pnr].action |= key_action;
684       else
685         stored_player[pnr].action &= ~key_action;
686
687       if (tape.single_step && tape.recording && tape.pausing)
688       {
689         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
690         {
691           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
692
693           /* if snap key already pressed, don't snap when releasing (below) */
694           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
695             element_snapped[pnr] = TRUE;
696
697           /* if drop key already pressed, don't drop when releasing (below) */
698           if (stored_player[pnr].action & KEY_BUTTON_DROP)
699             element_dropped[pnr] = TRUE;
700         }
701 #if 1
702         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
703         {
704           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
705               level.game_engine_type == GAME_ENGINE_TYPE_SP)
706           {
707 #if 0
708             printf("::: drop key pressed\n");
709 #endif
710
711             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
712                 getRedDiskReleaseFlag_SP() == 0)
713               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
714
715             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
716           }
717         }
718 #endif
719         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
720         {
721           if (key_action & KEY_BUTTON_SNAP)
722           {
723             /* if snap key was released without moving (see above), snap now */
724             if (!element_snapped[pnr])
725             {
726               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
727
728               stored_player[pnr].action |= KEY_BUTTON_SNAP;
729
730               /* clear delayed snap button on next event */
731               clear_snap_button[pnr] = TRUE;
732             }
733
734             element_snapped[pnr] = FALSE;
735           }
736
737 #if 1
738           if (key_action & KEY_BUTTON_DROP &&
739               level.game_engine_type == GAME_ENGINE_TYPE_RND)
740           {
741             /* if drop key was released without moving (see above), drop now */
742             if (!element_dropped[pnr])
743             {
744               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
745
746               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
747                   getRedDiskReleaseFlag_SP() != 0)
748                 stored_player[pnr].action |= KEY_BUTTON_DROP;
749
750               /* clear delayed drop button on next event */
751               clear_drop_button[pnr] = TRUE;
752             }
753
754             element_dropped[pnr] = FALSE;
755           }
756 #endif
757         }
758       }
759       else if (tape.recording && tape.pausing)
760       {
761         /* prevent key release events from un-pausing a paused game */
762         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
763           TapeTogglePause(TAPE_TOGGLE_MANUAL);
764       }
765     }
766   }
767   else
768   {
769     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
770       if (key == key_info[i].key_default)
771         joy |= key_info[i].action;
772   }
773
774   if (joy)
775   {
776     if (key_status == KEY_PRESSED)
777       key_joystick_mapping |= joy;
778     else
779       key_joystick_mapping &= ~joy;
780
781     HandleJoystick();
782   }
783
784   if (game_status != GAME_MODE_PLAYING)
785     key_joystick_mapping = 0;
786
787   if (key_status == KEY_RELEASED)
788     return;
789
790   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
791       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
792   {
793     setup.fullscreen = !setup.fullscreen;
794
795     ToggleFullscreenIfNeeded();
796
797     if (game_status == GAME_MODE_SETUP)
798       RedrawSetupScreenAfterFullscreenToggle();
799
800     return;
801   }
802
803 #if 0
804   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
805       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
806 #else
807   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
808       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
809 #endif
810   {
811     GameEnd();
812
813     return;
814   }
815
816   if (game_status == GAME_MODE_MAIN &&
817       (key == setup.shortcut.toggle_pause || key == KSYM_space))
818   {
819     StartGameActions(options.network, setup.autorecord, level.random_seed);
820
821     return;
822   }
823
824   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
825   {
826     if (key == setup.shortcut.save_game)
827       TapeQuickSave();
828     else if (key == setup.shortcut.load_game)
829       TapeQuickLoad();
830     else if (key == setup.shortcut.toggle_pause)
831       TapeTogglePause(TAPE_TOGGLE_MANUAL);
832
833     HandleTapeButtonKeys(key);
834     HandleSoundButtonKeys(key);
835   }
836
837   if (game_status == GAME_MODE_PLAYING && !network_playing)
838   {
839     int centered_player_nr_next = -999;
840
841     if (key == setup.shortcut.focus_player_all)
842       centered_player_nr_next = -1;
843     else
844       for (i = 0; i < MAX_PLAYERS; i++)
845         if (key == setup.shortcut.focus_player[i])
846           centered_player_nr_next = i;
847
848     if (centered_player_nr_next != -999)
849     {
850       game.centered_player_nr_next = centered_player_nr_next;
851       game.set_centered_player = TRUE;
852
853       if (tape.recording)
854       {
855         tape.centered_player_nr_next = game.centered_player_nr_next;
856         tape.set_centered_player = TRUE;
857       }
858     }
859   }
860
861   HandleKeysSpecial(key);
862
863   if (HandleGadgetsKeyInput(key))
864   {
865     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
866       key = KSYM_UNDEFINED;
867   }
868
869   switch (game_status)
870   {
871     case GAME_MODE_PSEUDO_TYPENAME:
872       HandleTypeName(0, key);
873       break;
874
875     case GAME_MODE_TITLE:
876     case GAME_MODE_MAIN:
877     case GAME_MODE_LEVELS:
878     case GAME_MODE_LEVELNR:
879     case GAME_MODE_SETUP:
880     case GAME_MODE_INFO:
881     case GAME_MODE_SCORES:
882       switch (key)
883       {
884         case KSYM_space:
885         case KSYM_Return:
886           if (game_status == GAME_MODE_TITLE)
887             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
888           else if (game_status == GAME_MODE_MAIN)
889             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
890           else if (game_status == GAME_MODE_LEVELS)
891             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
892           else if (game_status == GAME_MODE_LEVELNR)
893             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
894           else if (game_status == GAME_MODE_SETUP)
895             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
896           else if (game_status == GAME_MODE_INFO)
897             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
898           else if (game_status == GAME_MODE_SCORES)
899             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
900           break;
901
902         case KSYM_Escape:
903           if (game_status != GAME_MODE_MAIN)
904             FadeSkipNextFadeIn();
905
906           if (game_status == GAME_MODE_TITLE)
907             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
908           else if (game_status == GAME_MODE_LEVELS)
909             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
910           else if (game_status == GAME_MODE_LEVELNR)
911             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
912           else if (game_status == GAME_MODE_SETUP)
913             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
914           else if (game_status == GAME_MODE_INFO)
915             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
916           else if (game_status == GAME_MODE_SCORES)
917             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
918           break;
919
920         case KSYM_Page_Up:
921           if (game_status == GAME_MODE_LEVELS)
922             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
923           else if (game_status == GAME_MODE_LEVELNR)
924             HandleChooseLevelNr(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         case KSYM_Page_Down:
934           if (game_status == GAME_MODE_LEVELS)
935             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
936           else if (game_status == GAME_MODE_LEVELNR)
937             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
938           else if (game_status == GAME_MODE_SETUP)
939             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
940           else if (game_status == GAME_MODE_INFO)
941             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
942           else if (game_status == GAME_MODE_SCORES)
943             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
944           break;
945
946 #ifdef DEBUG
947         case KSYM_0:
948           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
949           break;
950
951         case KSYM_b:
952           setup.sp_show_border_elements = !setup.sp_show_border_elements;
953           printf("Supaplex border elements %s\n",
954                  setup.sp_show_border_elements ? "enabled" : "disabled");
955           break;
956 #endif
957
958         default:
959           break;
960       }
961       break;
962
963     case GAME_MODE_EDITOR:
964       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
965         HandleLevelEditorKeyInput(key);
966       break;
967
968     case GAME_MODE_PLAYING:
969     {
970       switch (key)
971       {
972         case KSYM_Escape:
973           RequestQuitGame(setup.ask_on_escape);
974           break;
975
976 #ifdef DEBUG
977         case KSYM_0:
978 #if 0
979         case KSYM_1:
980         case KSYM_2:
981         case KSYM_3:
982         case KSYM_4:
983         case KSYM_5:
984         case KSYM_6:
985         case KSYM_7:
986         case KSYM_8:
987         case KSYM_9:
988 #endif
989           if (key == KSYM_0)
990           {
991             if (GameFrameDelay == 500)
992               GameFrameDelay = GAME_FRAME_DELAY;
993             else
994               GameFrameDelay = 500;
995           }
996           else
997             GameFrameDelay = (key - KSYM_0) * 10;
998           printf("Game speed == %d%% (%d ms delay between two frames)\n",
999                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1000           break;
1001
1002         case KSYM_d:
1003           if (options.debug)
1004           {
1005             options.debug = FALSE;
1006             printf("debug mode disabled\n");
1007           }
1008           else
1009           {
1010             options.debug = TRUE;
1011             printf("debug mode enabled\n");
1012           }
1013           break;
1014
1015         case KSYM_s:
1016           if (!global.fps_slowdown)
1017           {
1018             global.fps_slowdown = TRUE;
1019             global.fps_slowdown_factor = 2;
1020             printf("fps slowdown enabled -- display only every 2nd frame\n");
1021           }
1022           else if (global.fps_slowdown_factor == 2)
1023           {
1024             global.fps_slowdown_factor = 4;
1025             printf("fps slowdown enabled -- display only every 4th frame\n");
1026           }
1027           else
1028           {
1029             global.fps_slowdown = FALSE;
1030             global.fps_slowdown_factor = 1;
1031             printf("fps slowdown disabled\n");
1032           }
1033           break;
1034
1035         case KSYM_f:
1036           ScrollStepSize = TILEX / 8;
1037           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1038           break;
1039
1040         case KSYM_g:
1041           ScrollStepSize = TILEX / 4;
1042           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1043           break;
1044
1045         case KSYM_h:
1046           ScrollStepSize = TILEX / 2;
1047           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1048           break;
1049
1050         case KSYM_l:
1051           ScrollStepSize = TILEX;
1052           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1053           break;
1054
1055         case KSYM_v:
1056           printf("::: currently using game engine version %d\n",
1057                  game.engine_version);
1058           break;
1059 #endif
1060
1061         default:
1062           break;
1063       }
1064       break;
1065     }
1066
1067     default:
1068       if (key == KSYM_Escape)
1069       {
1070         game_status = GAME_MODE_MAIN;
1071         DrawMainMenu();
1072
1073         return;
1074       }
1075   }
1076 }
1077
1078 void HandleNoEvent()
1079 {
1080   if (button_status && game_status != GAME_MODE_PLAYING)
1081   {
1082     HandleButton(0, 0, -button_status, button_status);
1083
1084     return;
1085   }
1086
1087 #if defined(NETWORK_AVALIABLE)
1088   if (options.network)
1089     HandleNetworking();
1090 #endif
1091
1092   HandleJoystick();
1093 }
1094
1095 static int HandleJoystickForAllPlayers()
1096 {
1097   int i;
1098   int result = 0;
1099
1100   for (i = 0; i < MAX_PLAYERS; i++)
1101   {
1102     byte joy_action = 0;
1103
1104     /*
1105     if (!setup.input[i].use_joystick)
1106       continue;
1107       */
1108
1109     joy_action = Joystick(i);
1110     result |= joy_action;
1111
1112     if (!setup.input[i].use_joystick)
1113       continue;
1114
1115     stored_player[i].action = joy_action;
1116   }
1117
1118   return result;
1119 }
1120
1121 void HandleJoystick()
1122 {
1123   int joystick  = HandleJoystickForAllPlayers();
1124   int keyboard  = key_joystick_mapping;
1125   int joy       = (joystick | keyboard);
1126   int left      = joy & JOY_LEFT;
1127   int right     = joy & JOY_RIGHT;
1128   int up        = joy & JOY_UP;
1129   int down      = joy & JOY_DOWN;
1130   int button    = joy & JOY_BUTTON;
1131   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1132   int dx        = (left ? -1    : right ? 1     : 0);
1133   int dy        = (up   ? -1    : down  ? 1     : 0);
1134
1135   switch (game_status)
1136   {
1137     case GAME_MODE_TITLE:
1138     case GAME_MODE_MAIN:
1139     case GAME_MODE_LEVELS:
1140     case GAME_MODE_LEVELNR:
1141     case GAME_MODE_SETUP:
1142     case GAME_MODE_INFO:
1143     {
1144       static unsigned int joystickmove_delay = 0;
1145
1146       if (joystick && !button &&
1147           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1148         newbutton = dx = dy = 0;
1149
1150       if (game_status == GAME_MODE_TITLE)
1151         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1152       else if (game_status == GAME_MODE_MAIN)
1153         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1154       else if (game_status == GAME_MODE_LEVELS)
1155         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1156       else if (game_status == GAME_MODE_LEVELNR)
1157         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1158       else if (game_status == GAME_MODE_SETUP)
1159         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1160       else if (game_status == GAME_MODE_INFO)
1161         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1162       break;
1163     }
1164
1165     case GAME_MODE_SCORES:
1166       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1167       break;
1168
1169     case GAME_MODE_EDITOR:
1170       HandleLevelEditorIdle();
1171       break;
1172
1173     case GAME_MODE_PLAYING:
1174       if (tape.playing || keyboard)
1175         newbutton = ((joy & JOY_BUTTON) != 0);
1176
1177 #if 0
1178       if (local_player->LevelSolved_GameEnd && newbutton)
1179 #else
1180       if (AllPlayersGone && newbutton)
1181 #endif
1182       {
1183         GameEnd();
1184
1185         return;
1186       }
1187
1188       break;
1189
1190     default:
1191       break;
1192   }
1193 }