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