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