rnd-20140114-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 SDL_WINDOWEVENT:
166             HandleWindowEvent((WindowEvent *) &event);
167             break;
168
169           case EVENT_FINGERPRESS:
170           case EVENT_FINGERRELEASE:
171           case EVENT_FINGERMOTION:
172             HandleFingerEvent((FingerEvent *) &event);
173             break;
174
175           case EVENT_TEXTINPUT:
176             HandleTextEvent((TextEvent *) &event);
177             break;
178 #endif
179
180           case EVENT_KEYPRESS:
181           case EVENT_KEYRELEASE:
182             HandleKeyEvent((KeyEvent *) &event);
183             break;
184
185           default:
186             HandleOtherEvents(&event);
187             break;
188         }
189       }
190     }
191
192     // !!! CHECK THIS:
193     // !!! this may result in "HandleNoEvent()" never being called
194     // !!! (especially due to continuously processed tocuh events)
195     // !!! and therefore toon animations being stopped while events
196     // !!! are being processed (even if they are all thrown away)
197
198     else
199     {
200       /* when playing, display a special mouse pointer inside the playfield */
201       if (game_status == GAME_MODE_PLAYING && !tape.pausing)
202       {
203         if (!playfield_cursor_set && cursor_inside_playfield &&
204             DelayReached(&playfield_cursor_delay, 1000))
205         {
206           SetMouseCursor(CURSOR_PLAYFIELD);
207           playfield_cursor_set = TRUE;
208         }
209       }
210       else if (playfield_cursor_set)
211       {
212         SetMouseCursor(CURSOR_DEFAULT);
213         playfield_cursor_set = FALSE;
214       }
215
216       HandleNoEvent();
217     }
218
219     /* don't use all CPU time when idle; the main loop while playing
220        has its own synchronization and is CPU friendly, too */
221
222     if (game_status == GAME_MODE_PLAYING)
223     {
224       HandleGameActions();
225     }
226     else
227     {
228       SyncDisplay();
229       if (!PendingEvent())      /* delay only if no pending events */
230         Delay(10);
231     }
232
233     /* refresh window contents from drawing buffer, if needed */
234     BackToFront();
235
236     if (game_status == GAME_MODE_QUIT)
237       return;
238   }
239 }
240
241 void HandleOtherEvents(Event *event)
242 {
243   switch (event->type)
244   {
245     case EVENT_EXPOSE:
246       HandleExposeEvent((ExposeEvent *) event);
247       break;
248
249     case EVENT_UNMAPNOTIFY:
250 #if 0
251       /* This causes the game to stop not only when iconified, but also
252          when on another virtual desktop, which might be not desired. */
253       SleepWhileUnmapped();
254 #endif
255       break;
256
257     case EVENT_FOCUSIN:
258     case EVENT_FOCUSOUT:
259       HandleFocusEvent((FocusChangeEvent *) event);
260       break;
261
262     case EVENT_CLIENTMESSAGE:
263       HandleClientMessageEvent((ClientMessageEvent *) event);
264       break;
265
266 #if defined(TARGET_SDL)
267     case SDL_JOYAXISMOTION:
268     case SDL_JOYBUTTONDOWN:
269     case SDL_JOYBUTTONUP:
270       HandleJoystickEvent(event);
271       break;
272
273     case SDL_SYSWMEVENT:
274       HandleWindowManagerEvent(event);
275       break;
276 #endif
277
278     default:
279       break;
280   }
281 }
282
283 void ClearEventQueue()
284 {
285   while (PendingEvent())
286   {
287     Event event;
288
289     NextEvent(&event);
290
291     switch (event.type)
292     {
293       case EVENT_BUTTONRELEASE:
294         button_status = MB_RELEASED;
295         break;
296
297       case EVENT_KEYRELEASE:
298 #if 1
299         ClearPlayerAction();
300 #else
301         key_joystick_mapping = 0;
302 #endif
303         break;
304
305       default:
306         HandleOtherEvents(&event);
307         break;
308     }
309   }
310 }
311
312 void ClearPlayerAction()
313 {
314   int i;
315
316   /* simulate key release events for still pressed keys */
317   key_joystick_mapping = 0;
318   for (i = 0; i < MAX_PLAYERS; i++)
319     stored_player[i].action = 0;
320 }
321
322 void SleepWhileUnmapped()
323 {
324   boolean window_unmapped = TRUE;
325
326   KeyboardAutoRepeatOn();
327
328   while (window_unmapped)
329   {
330     Event event;
331
332     NextEvent(&event);
333
334     switch (event.type)
335     {
336       case EVENT_BUTTONRELEASE:
337         button_status = MB_RELEASED;
338         break;
339
340       case EVENT_KEYRELEASE:
341         key_joystick_mapping = 0;
342         break;
343
344       case EVENT_MAPNOTIFY:
345         window_unmapped = FALSE;
346         break;
347
348       case EVENT_UNMAPNOTIFY:
349         /* this is only to surely prevent the 'should not happen' case
350          * of recursively looping between 'SleepWhileUnmapped()' and
351          * 'HandleOtherEvents()' which usually calls this funtion.
352          */
353         break;
354
355       default:
356         HandleOtherEvents(&event);
357         break;
358     }
359   }
360
361   if (game_status == GAME_MODE_PLAYING)
362     KeyboardAutoRepeatOffUnlessAutoplay();
363 }
364
365 void HandleExposeEvent(ExposeEvent *event)
366 {
367 #if !defined(TARGET_SDL)
368   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
369   FlushDisplay();
370 #endif
371 }
372
373 void HandleButtonEvent(ButtonEvent *event)
374 {
375 #if DEBUG_EVENTS
376   Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
377         event->button,
378         event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
379         event->x, event->y);
380 #endif
381
382   motion_status = FALSE;
383
384   if (event->type == EVENT_BUTTONPRESS)
385     button_status = event->button;
386   else
387     button_status = MB_RELEASED;
388
389   HandleButton(event->x, event->y, button_status, event->button);
390 }
391
392 void HandleMotionEvent(MotionEvent *event)
393 {
394   if (!PointerInWindow(window))
395     return;     /* window and pointer are on different screens */
396
397   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
398     return;
399
400   motion_status = TRUE;
401
402 #if DEBUG_EVENTS
403   Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
404         button_status, event->x, event->y);
405 #endif
406
407   HandleButton(event->x, event->y, button_status, button_status);
408 }
409
410 #if defined(TARGET_SDL2)
411 void HandleWindowEvent(WindowEvent *event)
412 {
413 #if DEBUG_EVENTS
414   int subtype = event->event;
415
416   char *event_name =
417     (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
418      subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
419      subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
420      subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
421      subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
422      subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
423      subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
424      subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
425      subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
426      subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
427      subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
428      subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
429      subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
430      subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
431      "(UNKNOWN)");
432
433   Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
434         event_name, event->data1, event->data2);
435 #endif
436
437   if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
438       event->event == SDL_WINDOWEVENT_RESIZED ||
439       event->event == SDL_WINDOWEVENT_EXPOSED)
440     SDLRedrawWindow();
441
442 #if 0
443   if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
444   {
445     // if game started in fullscreen mode, window will also get fullscreen size
446     if (!video.fullscreen_enabled && video.fullscreen_initial)
447     {
448       SDLSetWindowScaling(setup.window_scaling_percent);
449
450       // only do this correction once
451       video.fullscreen_initial = FALSE;
452     }
453   }
454 #endif
455
456   if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
457   {
458 #if 1
459     int new_window_width  = event->data1;
460     int new_window_height = event->data2;
461
462     printf("::: RESIZED from %d, %d to %d, %d\n",
463            video.window_width, video.window_height,
464            new_window_width, new_window_height);
465
466     // if window size has changed after resizing, calculate new scaling factor
467     if (new_window_width  != video.window_width ||
468         new_window_height != video.window_height)
469     {
470       int new_xpercent = (100 * new_window_width  / video.width);
471       int new_ypercent = (100 * new_window_height / video.height);
472
473       setup.window_scaling_percent = video.window_scaling_percent =
474         MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
475             MAX_WINDOW_SCALING_PERCENT);
476
477       video.window_width  = new_window_width;
478       video.window_height = new_window_height;
479
480       printf("::: setup.window_scaling_percent set to %d\n",
481              setup.window_scaling_percent);
482
483       if (game_status == GAME_MODE_SETUP)
484         RedrawSetupScreenAfterFullscreenToggle();
485     }
486 #else
487     // prevent slightly wrong scaling factor due to rounding differences
488     float scaling_factor = (float)setup.window_scaling_percent / 100;
489     int old_xsize = (int)(scaling_factor * video.width);
490     int old_ysize = (int)(scaling_factor * video.height);
491     int new_xsize = event->data1;
492     int new_ysize = event->data2;
493
494     // window size is unchanged when going from fullscreen to window mode,
495     // but reverse calculation of scaling factor might result in a scaling
496     // factor that is slightly different due to rounding differences;
497     // therefore compare old/new window size and not old/new scaling factor
498     if (old_xsize != new_xsize ||
499         old_ysize != new_ysize)
500     {
501       int new_xpercent = (100 * new_xsize / video.width);
502       int new_ypercent = (100 * new_ysize / video.height);
503
504       setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
505
506       if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
507         setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
508       else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
509         setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
510
511       printf("::: setup.window_scaling_percent set to %d\n",
512              setup.window_scaling_percent);
513     }
514 #endif
515   }
516 }
517
518 void HandleFingerEvent(FingerEvent *event)
519 {
520 #if 0
521   static int num_events = 0;
522   int max_events = 10;
523 #endif
524
525 #if 0
526 #if DEBUG_EVENTS
527   Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
528         event->type == EVENT_FINGERPRESS ? "pressed" :
529         event->type == EVENT_FINGERRELEASE ? "released" : "moved",
530         event->touchId,
531         event->fingerId,
532         event->x, event->y,
533         event->dx, event->dy,
534         event->pressure);
535 #endif
536 #endif
537
538 #if 0
539   int x = (int)(event->x * video.width);
540   int y = (int)(event->y * video.height);
541   int button = MB_LEFTBUTTON;
542
543   Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
544 #endif
545
546 #if 0
547   if (++num_events >= max_events)
548     CloseAllAndExit(0);
549 #endif
550
551 #if 1
552 #if 0
553   if (event->type == EVENT_FINGERPRESS ||
554       event->type == EVENT_FINGERMOTION)
555     button_status = button;
556   else
557     button_status = MB_RELEASED;
558
559   int max_x = SX + SXSIZE;
560   int max_y = SY + SYSIZE;
561 #endif
562
563 #if 1
564   if (game_status == GAME_MODE_PLAYING)
565 #else
566   if (game_status == GAME_MODE_PLAYING &&
567       x < max_x)
568 #endif
569   {
570     int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
571                       KEY_PRESSED);
572 #if 1
573     Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
574                event->y > 2.0 / 3.0 ? setup.input[0].key.down :
575                event->x < 1.0 / 3.0 ? setup.input[0].key.left :
576                event->x > 2.0 / 3.0 ? setup.input[0].key.right :
577                setup.input[0].key.drop);
578 #else
579     Key key = (y <     max_y / 3 ? setup.input[0].key.up :
580                y > 2 * max_y / 3 ? setup.input[0].key.down :
581                x <     max_x / 3 ? setup.input[0].key.left :
582                x > 2 * max_x / 3 ? setup.input[0].key.right :
583                setup.input[0].key.drop);
584 #endif
585
586     Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
587
588     HandleKey(key, key_status);
589   }
590   else
591   {
592 #if 0
593     Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
594           button_status, button);
595
596     HandleButton(x, y, button_status, button);
597 #endif
598   }
599 #endif
600 }
601
602 static boolean checkTextInputKeyModState()
603 {
604   // when playing, only handle raw key events and ignore text input
605   if (game_status == GAME_MODE_PLAYING)
606     return FALSE;
607
608   return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
609 }
610
611 void HandleTextEvent(TextEvent *event)
612 {
613   char *text = event->text;
614   Key key = getKeyFromKeyName(text);
615
616 #if DEBUG_EVENTS
617   Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
618         text,
619         strlen(text),
620         text[0], (int)(text[0]),
621         key,
622         getKeyNameFromKey(key),
623         GetKeyModState());
624 #endif
625
626   // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
627   /*
628   if (game_status != GAME_MODE_PLAYING &&
629       (GetKeyModState() & KMOD_TextInput) != KMOD_None)
630   */
631   if (checkTextInputKeyModState())
632   {
633     HandleKey(key, KEY_PRESSED);
634     HandleKey(key, KEY_RELEASED);
635   }
636 }
637 #endif
638
639 void HandleKeyEvent(KeyEvent *event)
640 {
641   int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
642   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
643   Key key = GetEventKey(event, with_modifiers);
644   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
645
646 #if DEBUG_EVENTS
647   Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
648         event->type == EVENT_KEYPRESS ? "pressed" : "released",
649         event->keysym.scancode,
650         event->keysym.sym,
651         keymod,
652         GetKeyModState(),
653         key,
654         getKeyNameFromKey(key));
655 #endif
656
657 #if 0
658   if (key == KSYM_Menu)
659     Error(ERR_DEBUG, "menu key pressed");
660   else if (key == KSYM_Back)
661     Error(ERR_DEBUG, "back key pressed");
662 #endif
663
664 #if defined(PLATFORM_ANDROID)
665   // always map the "back" button to the "escape" key on Android devices
666   if (key == KSYM_Back)
667     key = KSYM_Escape;
668 #endif
669
670   HandleKeyModState(keymod, key_status);
671
672 #if defined(TARGET_SDL2)
673
674   // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
675   /*
676   if (game_status == GAME_MODE_PLAYING ||
677       (GetKeyModState() & KMOD_TextInput) == KMOD_None)
678   */
679   if (!checkTextInputKeyModState())
680     HandleKey(key, key_status);
681 #else
682   HandleKey(key, key_status);
683 #endif
684 }
685
686 void HandleFocusEvent(FocusChangeEvent *event)
687 {
688   static int old_joystick_status = -1;
689
690   if (event->type == EVENT_FOCUSOUT)
691   {
692     KeyboardAutoRepeatOn();
693     old_joystick_status = joystick.status;
694     joystick.status = JOYSTICK_NOT_AVAILABLE;
695
696     ClearPlayerAction();
697   }
698   else if (event->type == EVENT_FOCUSIN)
699   {
700     /* When there are two Rocks'n'Diamonds windows which overlap and
701        the player moves the pointer from one game window to the other,
702        a 'FocusOut' event is generated for the window the pointer is
703        leaving and a 'FocusIn' event is generated for the window the
704        pointer is entering. In some cases, it can happen that the
705        'FocusIn' event is handled by the one game process before the
706        'FocusOut' event by the other game process. In this case the
707        X11 environment would end up with activated keyboard auto repeat,
708        because unfortunately this is a global setting and not (which
709        would be far better) set for each X11 window individually.
710        The effect would be keyboard auto repeat while playing the game
711        (game_status == GAME_MODE_PLAYING), which is not desired.
712        To avoid this special case, we just wait 1/10 second before
713        processing the 'FocusIn' event.
714     */
715
716     if (game_status == GAME_MODE_PLAYING)
717     {
718       Delay(100);
719       KeyboardAutoRepeatOffUnlessAutoplay();
720     }
721
722     if (old_joystick_status != -1)
723       joystick.status = old_joystick_status;
724   }
725 }
726
727 void HandleClientMessageEvent(ClientMessageEvent *event)
728 {
729   if (CheckCloseWindowEvent(event))
730     CloseAllAndExit(0);
731 }
732
733 void HandleWindowManagerEvent(Event *event)
734 {
735 #if defined(TARGET_SDL)
736   SDLHandleWindowManagerEvent(event);
737 #endif
738 }
739
740 void HandleButton(int mx, int my, int button, int button_nr)
741 {
742   static int old_mx = 0, old_my = 0;
743
744   if (button < 0)
745   {
746     mx = old_mx;
747     my = old_my;
748     button = -button;
749   }
750   else
751   {
752     old_mx = mx;
753     old_my = my;
754   }
755
756   if (HandleGadgets(mx, my, button))
757   {
758     /* do not handle this button event anymore */
759     mx = my = -32;      /* force mouse event to be outside screen tiles */
760   }
761
762   /* do not use scroll wheel button events for anything other than gadgets */
763   if (IS_WHEEL_BUTTON(button_nr))
764     return;
765
766 #if 0
767   Error(ERR_DEBUG, "::: game_status == %d", game_status);
768 #endif
769
770   switch (game_status)
771   {
772     case GAME_MODE_TITLE:
773       HandleTitleScreen(mx, my, 0, 0, button);
774       break;
775
776     case GAME_MODE_MAIN:
777       HandleMainMenu(mx, my, 0, 0, button);
778       break;
779
780     case GAME_MODE_PSEUDO_TYPENAME:
781       HandleTypeName(0, KSYM_Return);
782       break;
783
784     case GAME_MODE_LEVELS:
785       HandleChooseLevelSet(mx, my, 0, 0, button);
786       break;
787
788     case GAME_MODE_LEVELNR:
789       HandleChooseLevelNr(mx, my, 0, 0, button);
790       break;
791
792     case GAME_MODE_SCORES:
793       HandleHallOfFame(0, 0, 0, 0, button);
794       break;
795
796     case GAME_MODE_EDITOR:
797       HandleLevelEditorIdle();
798       break;
799
800     case GAME_MODE_INFO:
801       HandleInfoScreen(mx, my, 0, 0, button);
802       break;
803
804     case GAME_MODE_SETUP:
805       HandleSetupScreen(mx, my, 0, 0, button);
806       break;
807
808     case GAME_MODE_PLAYING:
809 #ifdef DEBUG
810       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
811         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
812 #endif
813       break;
814
815     default:
816       break;
817   }
818 }
819
820 static boolean is_string_suffix(char *string, char *suffix)
821 {
822   int string_len = strlen(string);
823   int suffix_len = strlen(suffix);
824
825   if (suffix_len > string_len)
826     return FALSE;
827
828   return (strEqual(&string[string_len - suffix_len], suffix));
829 }
830
831 #define MAX_CHEAT_INPUT_LEN     32
832
833 static void HandleKeysSpecial(Key key)
834 {
835   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
836   char letter = getCharFromKey(key);
837   int cheat_input_len = strlen(cheat_input);
838   int i;
839
840   if (letter == 0)
841     return;
842
843   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
844   {
845     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
846       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
847
848     cheat_input_len = MAX_CHEAT_INPUT_LEN;
849   }
850
851   cheat_input[cheat_input_len++] = letter;
852   cheat_input[cheat_input_len] = '\0';
853
854 #if DEBUG_EVENTS
855   Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
856 #endif
857
858   if (game_status == GAME_MODE_MAIN)
859   {
860     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
861         is_string_suffix(cheat_input, ":ist"))
862     {
863       InsertSolutionTape();
864     }
865     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
866              is_string_suffix(cheat_input, ":rg"))
867     {
868       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
869       DrawMainMenu();
870     }
871     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
872              is_string_suffix(cheat_input, ":rs"))
873     {
874       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
875       DrawMainMenu();
876     }
877     else if (is_string_suffix(cheat_input, ":reload-music") ||
878              is_string_suffix(cheat_input, ":rm"))
879     {
880       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
881       DrawMainMenu();
882     }
883     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
884              is_string_suffix(cheat_input, ":ra"))
885     {
886       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
887                           1 << ARTWORK_TYPE_SOUNDS |
888                           1 << ARTWORK_TYPE_MUSIC);
889       DrawMainMenu();
890     }
891     else if (is_string_suffix(cheat_input, ":dump-level") ||
892              is_string_suffix(cheat_input, ":dl"))
893     {
894       DumpLevel(&level);
895     }
896     else if (is_string_suffix(cheat_input, ":dump-tape") ||
897              is_string_suffix(cheat_input, ":dt"))
898     {
899       DumpTape(&tape);
900     }
901     else if (is_string_suffix(cheat_input, ":save-native-level") ||
902              is_string_suffix(cheat_input, ":snl"))
903     {
904       SaveNativeLevel(&level);
905     }
906   }
907   else if (game_status == GAME_MODE_PLAYING)
908   {
909 #ifdef DEBUG
910     if (is_string_suffix(cheat_input, ".q"))
911       DEBUG_SetMaximumDynamite();
912 #endif
913   }
914   else if (game_status == GAME_MODE_EDITOR)
915   {
916     if (is_string_suffix(cheat_input, ":dump-brush") ||
917         is_string_suffix(cheat_input, ":DB"))
918     {
919       DumpBrush();
920     }
921     else if (is_string_suffix(cheat_input, ":DDB"))
922     {
923       DumpBrush_Small();
924     }
925   }
926 }
927
928 void HandleKey(Key key, int key_status)
929 {
930   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
931   static struct SetupKeyboardInfo ski;
932   static struct SetupShortcutInfo ssi;
933   static struct
934   {
935     Key *key_custom;
936     Key *key_snap;
937     Key key_default;
938     byte action;
939   } key_info[] =
940   {
941     { &ski.left,  &ssi.snap_left,  DEFAULT_KEY_LEFT,  JOY_LEFT        },
942     { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT       },
943     { &ski.up,    &ssi.snap_up,    DEFAULT_KEY_UP,    JOY_UP          },
944     { &ski.down,  &ssi.snap_down,  DEFAULT_KEY_DOWN,  JOY_DOWN        },
945     { &ski.snap,  NULL,            DEFAULT_KEY_SNAP,  JOY_BUTTON_SNAP },
946     { &ski.drop,  NULL,            DEFAULT_KEY_DROP,  JOY_BUTTON_DROP }
947   };
948   int joy = 0;
949   int i;
950
951   if (game_status == GAME_MODE_PLAYING)
952   {
953     /* only needed for single-step tape recording mode */
954     static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
955     static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
956     static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
957     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
958     int pnr;
959
960     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
961     {
962       byte key_action = 0;
963
964       if (setup.input[pnr].use_joystick)
965         continue;
966
967       ski = setup.input[pnr].key;
968
969       for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
970         if (key == *key_info[i].key_custom)
971           key_action |= key_info[i].action;
972
973       /* use combined snap+direction keys for the first player only */
974       if (pnr == 0)
975       {
976         ssi = setup.shortcut;
977
978         for (i = 0; i < NUM_DIRECTIONS; i++)
979           if (key == *key_info[i].key_snap)
980             key_action |= key_info[i].action | JOY_BUTTON_SNAP;
981       }
982
983       /* clear delayed snap and drop actions in single step mode (see below) */
984       if (tape.single_step)
985       {
986         if (clear_snap_button[pnr])
987         {
988           stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
989           clear_snap_button[pnr] = FALSE;
990         }
991
992         if (clear_drop_button[pnr])
993         {
994           stored_player[pnr].action &= ~KEY_BUTTON_DROP;
995           clear_drop_button[pnr] = FALSE;
996         }
997       }
998
999       if (key_status == KEY_PRESSED)
1000         stored_player[pnr].action |= key_action;
1001       else
1002         stored_player[pnr].action &= ~key_action;
1003
1004       if (tape.single_step && tape.recording && tape.pausing)
1005       {
1006         if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1007         {
1008           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1009
1010           /* if snap key already pressed, don't snap when releasing (below) */
1011           if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1012             element_snapped[pnr] = TRUE;
1013
1014           /* if drop key already pressed, don't drop when releasing (below) */
1015           if (stored_player[pnr].action & KEY_BUTTON_DROP)
1016             element_dropped[pnr] = TRUE;
1017         }
1018 #if 1
1019         else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1020         {
1021           if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1022               level.game_engine_type == GAME_ENGINE_TYPE_SP)
1023           {
1024 #if 0
1025             printf("::: drop key pressed\n");
1026 #endif
1027
1028             if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1029                 getRedDiskReleaseFlag_SP() == 0)
1030               stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1031
1032             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1033           }
1034         }
1035 #endif
1036         else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1037         {
1038           if (key_action & KEY_BUTTON_SNAP)
1039           {
1040             /* if snap key was released without moving (see above), snap now */
1041             if (!element_snapped[pnr])
1042             {
1043               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1044
1045               stored_player[pnr].action |= KEY_BUTTON_SNAP;
1046
1047               /* clear delayed snap button on next event */
1048               clear_snap_button[pnr] = TRUE;
1049             }
1050
1051             element_snapped[pnr] = FALSE;
1052           }
1053
1054 #if 1
1055           if (key_action & KEY_BUTTON_DROP &&
1056               level.game_engine_type == GAME_ENGINE_TYPE_RND)
1057           {
1058             /* if drop key was released without moving (see above), drop now */
1059             if (!element_dropped[pnr])
1060             {
1061               TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1062
1063               if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1064                   getRedDiskReleaseFlag_SP() != 0)
1065                 stored_player[pnr].action |= KEY_BUTTON_DROP;
1066
1067               /* clear delayed drop button on next event */
1068               clear_drop_button[pnr] = TRUE;
1069             }
1070
1071             element_dropped[pnr] = FALSE;
1072           }
1073 #endif
1074         }
1075       }
1076       else if (tape.recording && tape.pausing)
1077       {
1078         /* prevent key release events from un-pausing a paused game */
1079         if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1080           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1081       }
1082     }
1083   }
1084   else
1085   {
1086     for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1087       if (key == key_info[i].key_default)
1088         joy |= key_info[i].action;
1089   }
1090
1091   if (joy)
1092   {
1093     if (key_status == KEY_PRESSED)
1094       key_joystick_mapping |= joy;
1095     else
1096       key_joystick_mapping &= ~joy;
1097
1098     HandleJoystick();
1099   }
1100
1101   if (game_status != GAME_MODE_PLAYING)
1102     key_joystick_mapping = 0;
1103
1104   if (key_status == KEY_RELEASED)
1105     return;
1106
1107   if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1108       (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1109   {
1110     setup.fullscreen = !setup.fullscreen;
1111
1112 #if 0
1113     printf("::: %d\n", setup.window_scaling_percent);
1114 #endif
1115
1116     ToggleFullscreenIfNeeded();
1117
1118     if (game_status == GAME_MODE_SETUP)
1119       RedrawSetupScreenAfterFullscreenToggle();
1120
1121     return;
1122   }
1123
1124   if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1125       (GetKeyModState() & KMOD_Control) && video.window_scaling_available &&
1126       !video.fullscreen_enabled)
1127   {
1128     if (key == KSYM_0)
1129       setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1130     else
1131       setup.window_scaling_percent +=
1132         (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1133
1134     if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1135       setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1136     else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1137       setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1138
1139     ToggleFullscreenIfNeeded();
1140
1141     if (game_status == GAME_MODE_SETUP)
1142       RedrawSetupScreenAfterFullscreenToggle();
1143
1144     return;
1145   }
1146
1147 #if 0
1148   if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1149       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1150 #else
1151   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1152       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1153 #endif
1154   {
1155     GameEnd();
1156
1157     return;
1158   }
1159
1160   if (game_status == GAME_MODE_MAIN &&
1161       (key == setup.shortcut.toggle_pause || key == KSYM_space))
1162   {
1163     StartGameActions(options.network, setup.autorecord, level.random_seed);
1164
1165     return;
1166   }
1167
1168   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1169   {
1170     if (key == setup.shortcut.save_game)
1171       TapeQuickSave();
1172     else if (key == setup.shortcut.load_game)
1173       TapeQuickLoad();
1174     else if (key == setup.shortcut.toggle_pause)
1175       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1176
1177     HandleTapeButtonKeys(key);
1178     HandleSoundButtonKeys(key);
1179   }
1180
1181   if (game_status == GAME_MODE_PLAYING && !network_playing)
1182   {
1183     int centered_player_nr_next = -999;
1184
1185     if (key == setup.shortcut.focus_player_all)
1186       centered_player_nr_next = -1;
1187     else
1188       for (i = 0; i < MAX_PLAYERS; i++)
1189         if (key == setup.shortcut.focus_player[i])
1190           centered_player_nr_next = i;
1191
1192     if (centered_player_nr_next != -999)
1193     {
1194       game.centered_player_nr_next = centered_player_nr_next;
1195       game.set_centered_player = TRUE;
1196
1197       if (tape.recording)
1198       {
1199         tape.centered_player_nr_next = game.centered_player_nr_next;
1200         tape.set_centered_player = TRUE;
1201       }
1202     }
1203   }
1204
1205   HandleKeysSpecial(key);
1206
1207   if (HandleGadgetsKeyInput(key))
1208   {
1209     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
1210       key = KSYM_UNDEFINED;
1211   }
1212
1213   switch (game_status)
1214   {
1215     case GAME_MODE_PSEUDO_TYPENAME:
1216       HandleTypeName(0, key);
1217       break;
1218
1219     case GAME_MODE_TITLE:
1220     case GAME_MODE_MAIN:
1221     case GAME_MODE_LEVELS:
1222     case GAME_MODE_LEVELNR:
1223     case GAME_MODE_SETUP:
1224     case GAME_MODE_INFO:
1225     case GAME_MODE_SCORES:
1226       switch (key)
1227       {
1228         case KSYM_space:
1229         case KSYM_Return:
1230           if (game_status == GAME_MODE_TITLE)
1231             HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1232           else if (game_status == GAME_MODE_MAIN)
1233             HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1234           else if (game_status == GAME_MODE_LEVELS)
1235             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1236           else if (game_status == GAME_MODE_LEVELNR)
1237             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1238           else if (game_status == GAME_MODE_SETUP)
1239             HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1240           else if (game_status == GAME_MODE_INFO)
1241             HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1242           else if (game_status == GAME_MODE_SCORES)
1243             HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1244           break;
1245
1246         case KSYM_Escape:
1247           if (game_status != GAME_MODE_MAIN)
1248             FadeSkipNextFadeIn();
1249
1250           if (game_status == GAME_MODE_TITLE)
1251             HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1252           else if (game_status == GAME_MODE_LEVELS)
1253             HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1254           else if (game_status == GAME_MODE_LEVELNR)
1255             HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1256           else if (game_status == GAME_MODE_SETUP)
1257             HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1258           else if (game_status == GAME_MODE_INFO)
1259             HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1260           else if (game_status == GAME_MODE_SCORES)
1261             HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1262           break;
1263
1264         case KSYM_Page_Up:
1265           if (game_status == GAME_MODE_LEVELS)
1266             HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1267           else if (game_status == GAME_MODE_LEVELNR)
1268             HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1269           else if (game_status == GAME_MODE_SETUP)
1270             HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1271           else if (game_status == GAME_MODE_INFO)
1272             HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1273           else if (game_status == GAME_MODE_SCORES)
1274             HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1275           break;
1276
1277         case KSYM_Page_Down:
1278           if (game_status == GAME_MODE_LEVELS)
1279             HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1280           else if (game_status == GAME_MODE_LEVELNR)
1281             HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1282           else if (game_status == GAME_MODE_SETUP)
1283             HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1284           else if (game_status == GAME_MODE_INFO)
1285             HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1286           else if (game_status == GAME_MODE_SCORES)
1287             HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1288           break;
1289
1290 #ifdef DEBUG
1291         case KSYM_0:
1292           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1293           break;
1294
1295         case KSYM_b:
1296           setup.sp_show_border_elements = !setup.sp_show_border_elements;
1297           printf("Supaplex border elements %s\n",
1298                  setup.sp_show_border_elements ? "enabled" : "disabled");
1299           break;
1300 #endif
1301
1302         default:
1303           break;
1304       }
1305       break;
1306
1307     case GAME_MODE_EDITOR:
1308       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1309         HandleLevelEditorKeyInput(key);
1310       break;
1311
1312     case GAME_MODE_PLAYING:
1313     {
1314       switch (key)
1315       {
1316         case KSYM_Escape:
1317           RequestQuitGame(setup.ask_on_escape);
1318           break;
1319
1320 #ifdef DEBUG
1321         case KSYM_0:
1322 #if 0
1323         case KSYM_1:
1324         case KSYM_2:
1325         case KSYM_3:
1326         case KSYM_4:
1327         case KSYM_5:
1328         case KSYM_6:
1329         case KSYM_7:
1330         case KSYM_8:
1331         case KSYM_9:
1332 #endif
1333           if (key == KSYM_0)
1334           {
1335             if (GameFrameDelay == 500)
1336               GameFrameDelay = GAME_FRAME_DELAY;
1337             else
1338               GameFrameDelay = 500;
1339           }
1340           else
1341             GameFrameDelay = (key - KSYM_0) * 10;
1342           printf("Game speed == %d%% (%d ms delay between two frames)\n",
1343                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1344           break;
1345
1346         case KSYM_d:
1347           if (options.debug)
1348           {
1349             options.debug = FALSE;
1350             printf("debug mode disabled\n");
1351           }
1352           else
1353           {
1354             options.debug = TRUE;
1355             printf("debug mode enabled\n");
1356           }
1357           break;
1358
1359         case KSYM_s:
1360           if (!global.fps_slowdown)
1361           {
1362             global.fps_slowdown = TRUE;
1363             global.fps_slowdown_factor = 2;
1364             printf("fps slowdown enabled -- display only every 2nd frame\n");
1365           }
1366           else if (global.fps_slowdown_factor == 2)
1367           {
1368             global.fps_slowdown_factor = 4;
1369             printf("fps slowdown enabled -- display only every 4th frame\n");
1370           }
1371           else
1372           {
1373             global.fps_slowdown = FALSE;
1374             global.fps_slowdown_factor = 1;
1375             printf("fps slowdown disabled\n");
1376           }
1377           break;
1378
1379         case KSYM_f:
1380           ScrollStepSize = TILEX / 8;
1381           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1382           break;
1383
1384         case KSYM_g:
1385           ScrollStepSize = TILEX / 4;
1386           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1387           break;
1388
1389         case KSYM_h:
1390           ScrollStepSize = TILEX / 2;
1391           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1392           break;
1393
1394         case KSYM_l:
1395           ScrollStepSize = TILEX;
1396           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1397           break;
1398
1399         case KSYM_v:
1400           printf("::: currently using game engine version %d\n",
1401                  game.engine_version);
1402           break;
1403 #endif
1404
1405         default:
1406           break;
1407       }
1408       break;
1409     }
1410
1411     default:
1412       if (key == KSYM_Escape)
1413       {
1414         game_status = GAME_MODE_MAIN;
1415         DrawMainMenu();
1416
1417         return;
1418       }
1419   }
1420 }
1421
1422 void HandleNoEvent()
1423 {
1424   if (button_status && game_status != GAME_MODE_PLAYING)
1425   {
1426     HandleButton(0, 0, -button_status, button_status);
1427
1428     return;
1429   }
1430
1431 #if defined(NETWORK_AVALIABLE)
1432   if (options.network)
1433     HandleNetworking();
1434 #endif
1435
1436   HandleJoystick();
1437 }
1438
1439 static int HandleJoystickForAllPlayers()
1440 {
1441   int i;
1442   int result = 0;
1443
1444   for (i = 0; i < MAX_PLAYERS; i++)
1445   {
1446     byte joy_action = 0;
1447
1448     /*
1449     if (!setup.input[i].use_joystick)
1450       continue;
1451       */
1452
1453     joy_action = Joystick(i);
1454     result |= joy_action;
1455
1456     if (!setup.input[i].use_joystick)
1457       continue;
1458
1459     stored_player[i].action = joy_action;
1460   }
1461
1462   return result;
1463 }
1464
1465 void HandleJoystick()
1466 {
1467   int joystick  = HandleJoystickForAllPlayers();
1468   int keyboard  = key_joystick_mapping;
1469   int joy       = (joystick | keyboard);
1470   int left      = joy & JOY_LEFT;
1471   int right     = joy & JOY_RIGHT;
1472   int up        = joy & JOY_UP;
1473   int down      = joy & JOY_DOWN;
1474   int button    = joy & JOY_BUTTON;
1475   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1476   int dx        = (left ? -1    : right ? 1     : 0);
1477   int dy        = (up   ? -1    : down  ? 1     : 0);
1478
1479   switch (game_status)
1480   {
1481     case GAME_MODE_TITLE:
1482     case GAME_MODE_MAIN:
1483     case GAME_MODE_LEVELS:
1484     case GAME_MODE_LEVELNR:
1485     case GAME_MODE_SETUP:
1486     case GAME_MODE_INFO:
1487     {
1488       static unsigned int joystickmove_delay = 0;
1489
1490       if (joystick && !button &&
1491           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1492         newbutton = dx = dy = 0;
1493
1494       if (game_status == GAME_MODE_TITLE)
1495         HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1496       else if (game_status == GAME_MODE_MAIN)
1497         HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1498       else if (game_status == GAME_MODE_LEVELS)
1499         HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1500       else if (game_status == GAME_MODE_LEVELNR)
1501         HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1502       else if (game_status == GAME_MODE_SETUP)
1503         HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1504       else if (game_status == GAME_MODE_INFO)
1505         HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1506       break;
1507     }
1508
1509     case GAME_MODE_SCORES:
1510       HandleHallOfFame(0, 0, dx, dy, !newbutton);
1511       break;
1512
1513     case GAME_MODE_EDITOR:
1514       HandleLevelEditorIdle();
1515       break;
1516
1517     case GAME_MODE_PLAYING:
1518       if (tape.playing || keyboard)
1519         newbutton = ((joy & JOY_BUTTON) != 0);
1520
1521 #if 0
1522       if (local_player->LevelSolved_GameEnd && newbutton)
1523 #else
1524       if (AllPlayersGone && newbutton)
1525 #endif
1526       {
1527         GameEnd();
1528
1529         return;
1530       }
1531
1532       break;
1533
1534     default:
1535       break;
1536   }
1537 }