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