rnd-20031228-1-src
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 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 static boolean cursor_inside_playfield = FALSE;
28 static boolean playfield_cursor_set = FALSE;
29 static unsigned long playfield_cursor_delay = 0;
30
31
32 /* event filter especially needed for SDL event filtering due to
33    delay problems with lots of mouse motion events when mouse button
34    not pressed (X11 can handle this with 'PointerMotionHintMask') */
35
36 int FilterMouseMotionEvents(const Event *event)
37 {
38   MotionEvent *motion;
39
40   /* non-motion events are directly passed to event handler functions */
41   if (event->type != EVENT_MOTIONNOTIFY)
42     return 1;
43
44   motion = (MotionEvent *)event;
45   cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
46                              motion->y >= SY && motion->y < SY + SYSIZE);
47
48   if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
49   {
50     SetMouseCursor(CURSOR_DEFAULT);
51     playfield_cursor_set = FALSE;
52     DelayReached(&playfield_cursor_delay, 0);
53   }
54
55   /* skip mouse motion events without pressed button outside level editor */
56   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR &&
57       game_status != GAME_MODE_PLAYING)
58     return 0;
59   else
60     return 1;
61 }
62
63 /* this is only really needed for non-SDL targets to filter unwanted events;
64    when using SDL with properly installed event filter, this function can be
65    replaced with a simple "NextEvent()" call, but it doesn't hurt either */
66
67 static boolean NextValidEvent(Event *event)
68 {
69   while (PendingEvent())
70   {
71     NextEvent(event);
72
73     if (FilterMouseMotionEvents(event))
74       return TRUE;
75   }
76
77   return FALSE;
78 }
79
80 void EventLoop(void)
81 {
82   while (1)
83   {
84     if (PendingEvent())         /* got event */
85     {
86       Event event;
87
88       if (NextValidEvent(&event))
89       {
90         switch(event.type)
91         {
92           case EVENT_BUTTONPRESS:
93           case EVENT_BUTTONRELEASE:
94             HandleButtonEvent((ButtonEvent *) &event);
95             break;
96   
97           case EVENT_MOTIONNOTIFY:
98             HandleMotionEvent((MotionEvent *) &event);
99             break;
100   
101           case EVENT_KEYPRESS:
102           case EVENT_KEYRELEASE:
103             HandleKeyEvent((KeyEvent *) &event);
104             break;
105   
106           default:
107             HandleOtherEvents(&event);
108             break;
109         }
110       }
111     }
112     else
113     {
114       /* when playing, display a special mouse pointer inside the playfield */
115       if (game_status == GAME_MODE_PLAYING)
116       {
117         if (!playfield_cursor_set && cursor_inside_playfield &&
118             DelayReached(&playfield_cursor_delay, 1000))
119         {
120           SetMouseCursor(CURSOR_PLAYFIELD);
121           playfield_cursor_set = TRUE;
122         }
123       }
124       else if (playfield_cursor_set)
125       {
126         SetMouseCursor(CURSOR_DEFAULT);
127         playfield_cursor_set = FALSE;
128       }
129
130       HandleNoEvent();
131     }
132
133     /* don't use all CPU time when idle; the main loop while playing
134        has its own synchronization and is CPU friendly, too */
135
136     if (game_status == GAME_MODE_PLAYING)
137       HandleGameActions();
138     else
139     {
140       SyncDisplay();
141       if (!PendingEvent())      /* delay only if no pending events */
142         Delay(10);
143     }
144
145     /* refresh window contents from drawing buffer, if needed */
146     BackToFront();
147
148     if (game_status == GAME_MODE_QUIT)
149       return;
150   }
151 }
152
153 void HandleOtherEvents(Event *event)
154 {
155   switch(event->type)
156   {
157     case EVENT_EXPOSE:
158       HandleExposeEvent((ExposeEvent *) event);
159       break;
160
161     case EVENT_UNMAPNOTIFY:
162 #if 0
163       /* This causes the game to stop not only when iconified, but also
164          when on another virtual desktop, which might be not desired. */
165       SleepWhileUnmapped();
166 #endif
167       break;
168
169     case EVENT_FOCUSIN:
170     case EVENT_FOCUSOUT:
171       HandleFocusEvent((FocusChangeEvent *) event);
172       break;
173
174     case EVENT_CLIENTMESSAGE:
175       HandleClientMessageEvent((ClientMessageEvent *) event);
176       break;
177
178 #if defined(TARGET_SDL)
179     case SDL_JOYAXISMOTION:
180     case SDL_JOYBUTTONDOWN:
181     case SDL_JOYBUTTONUP:
182       HandleJoystickEvent(event);
183       break;
184 #endif
185
186     default:
187       break;
188   }
189 }
190
191 void ClearEventQueue()
192 {
193   while (PendingEvent())
194   {
195     Event event;
196
197     NextEvent(&event);
198
199     switch(event.type)
200     {
201       case EVENT_BUTTONRELEASE:
202         button_status = MB_RELEASED;
203         break;
204
205       case EVENT_KEYRELEASE:
206         key_joystick_mapping = 0;
207         break;
208
209       default:
210         HandleOtherEvents(&event);
211         break;
212     }
213   }
214 }
215
216 void ClearPlayerAction()
217 {
218   int i;
219
220   /* simulate key release events for still pressed keys */
221   key_joystick_mapping = 0;
222   for (i = 0; i < MAX_PLAYERS; i++)
223     stored_player[i].action = 0;
224 }
225
226 void SleepWhileUnmapped()
227 {
228   boolean window_unmapped = TRUE;
229
230   KeyboardAutoRepeatOn();
231
232   while (window_unmapped)
233   {
234     Event event;
235
236     NextEvent(&event);
237
238     switch(event.type)
239     {
240       case EVENT_BUTTONRELEASE:
241         button_status = MB_RELEASED;
242         break;
243
244       case EVENT_KEYRELEASE:
245         key_joystick_mapping = 0;
246         break;
247
248       case EVENT_MAPNOTIFY:
249         window_unmapped = FALSE;
250         break;
251
252       case EVENT_UNMAPNOTIFY:
253         /* this is only to surely prevent the 'should not happen' case
254          * of recursively looping between 'SleepWhileUnmapped()' and
255          * 'HandleOtherEvents()' which usually calls this funtion.
256          */
257         break;
258
259       default:
260         HandleOtherEvents(&event);
261         break;
262     }
263   }
264
265   if (game_status == GAME_MODE_PLAYING)
266     KeyboardAutoRepeatOffUnlessAutoplay();
267 }
268
269 void HandleExposeEvent(ExposeEvent *event)
270 {
271 #ifndef TARGET_SDL
272   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
273   FlushDisplay();
274 #endif
275 }
276
277 void HandleButtonEvent(ButtonEvent *event)
278 {
279   motion_status = FALSE;
280
281   if (event->type == EVENT_BUTTONPRESS)
282     button_status = event->button;
283   else
284     button_status = MB_RELEASED;
285
286   HandleButton(event->x, event->y, button_status);
287 }
288
289 void HandleMotionEvent(MotionEvent *event)
290 {
291   if (!PointerInWindow(window))
292     return;     /* window and pointer are on different screens */
293
294 #if 1
295   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
296     return;
297 #endif
298
299   motion_status = TRUE;
300
301   HandleButton(event->x, event->y, button_status);
302 }
303
304 void HandleKeyEvent(KeyEvent *event)
305 {
306   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
307   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
308   Key key = GetEventKey(event, with_modifiers);
309   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
310
311   HandleKeyModState(keymod, key_status);
312   HandleKey(key, key_status);
313 }
314
315 void HandleFocusEvent(FocusChangeEvent *event)
316 {
317   static int old_joystick_status = -1;
318
319   if (event->type == EVENT_FOCUSOUT)
320   {
321     KeyboardAutoRepeatOn();
322     old_joystick_status = joystick.status;
323     joystick.status = JOYSTICK_NOT_AVAILABLE;
324
325     ClearPlayerAction();
326   }
327   else if (event->type == EVENT_FOCUSIN)
328   {
329     /* When there are two Rocks'n'Diamonds windows which overlap and
330        the player moves the pointer from one game window to the other,
331        a 'FocusOut' event is generated for the window the pointer is
332        leaving and a 'FocusIn' event is generated for the window the
333        pointer is entering. In some cases, it can happen that the
334        'FocusIn' event is handled by the one game process before the
335        'FocusOut' event by the other game process. In this case the
336        X11 environment would end up with activated keyboard auto repeat,
337        because unfortunately this is a global setting and not (which
338        would be far better) set for each X11 window individually.
339        The effect would be keyboard auto repeat while playing the game
340        (game_status == GAME_MODE_PLAYING), which is not desired.
341        To avoid this special case, we just wait 1/10 second before
342        processing the 'FocusIn' event.
343     */
344
345     if (game_status == GAME_MODE_PLAYING)
346     {
347       Delay(100);
348       KeyboardAutoRepeatOffUnlessAutoplay();
349     }
350     if (old_joystick_status != -1)
351       joystick.status = old_joystick_status;
352   }
353 }
354
355 void HandleClientMessageEvent(ClientMessageEvent *event)
356 {
357   if (CheckCloseWindowEvent(event))
358     CloseAllAndExit(0);
359 }
360
361 void HandleButton(int mx, int my, int button)
362 {
363   static int old_mx = 0, old_my = 0;
364
365   if (button < 0)
366   {
367     mx = old_mx;
368     my = old_my;
369     button = -button;
370   }
371   else
372   {
373     old_mx = mx;
374     old_my = my;
375   }
376
377   if (HandleGadgets(mx, my, button))
378   {
379     /* do not handle this button event anymore */
380     mx = my = -32;      /* force mouse event to be outside screen tiles */
381   }
382
383   switch(game_status)
384   {
385     case GAME_MODE_MAIN:
386       HandleMainMenu(mx,my, 0,0, button);
387       break;
388
389     case GAME_MODE_PSEUDO_TYPENAME:
390       HandleTypeName(0, KSYM_Return);
391       break;
392
393     case GAME_MODE_LEVELS:
394       HandleChooseLevel(mx,my, 0,0, button);
395       break;
396
397     case GAME_MODE_SCORES:
398       HandleHallOfFame(0,0, 0,0, button);
399       break;
400
401     case GAME_MODE_EDITOR:
402       break;
403
404     case GAME_MODE_INFO:
405       HandleInfoScreen(mx,my, 0,0, button);
406       break;
407
408     case GAME_MODE_SETUP:
409       HandleSetupScreen(mx,my, 0,0, button);
410       break;
411
412     case GAME_MODE_PLAYING:
413 #ifdef DEBUG
414       if (button == MB_RELEASED)
415       {
416         if (IN_GFX_SCREEN(mx, my))
417         {
418           int sx = (mx - SX) / TILEX;
419           int sy = (my - SY) / TILEY;
420           int x = LEVELX(sx);
421           int y = LEVELY(sy);
422
423           printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
424
425           if (!IN_LEV_FIELD(x, y))
426             break;
427
428           printf("      Feld[%d][%d] == %d ('%s')\n", x,y, Feld[x][y],
429                  element_info[Feld[x][y]].token_name);
430           printf("      Back[%d][%d] == %d\n", x,y, Back[x][y]);
431           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
432           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
433           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
434           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
435           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
436           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
437           printf("      ChangeDelay[%d][%d] == %d\n", x,y, ChangeDelay[x][y]);
438           printf("      GfxElement[%d][%d] == %d\n", x,y, GfxElement[x][y]);
439           printf("      GfxAction[%d][%d] == %d\n", x,y, GfxAction[x][y]);
440           printf("      GfxFrame[%d][%d] == %d\n", x,y, GfxFrame[x][y]);
441           printf("\n");
442         }
443       }
444 #endif
445       break;
446
447     default:
448       break;
449   }
450 }
451
452 static boolean is_string_suffix(char *string, char *suffix)
453 {
454   int string_len = strlen(string);
455   int suffix_len = strlen(suffix);
456
457   if (suffix_len > string_len)
458     return FALSE;
459
460   return (strcmp(&string[string_len - suffix_len], suffix) == 0);
461 }
462
463 #define MAX_CHEAT_INPUT_LEN     32
464
465 static void HandleKeysCheating(Key key)
466 {
467   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
468   char letter = getCharFromKey(key);
469   int cheat_input_len = strlen(cheat_input);
470   int i;
471
472   if (letter == 0)
473     return;
474
475   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
476   {
477     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
478       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
479
480     cheat_input_len = MAX_CHEAT_INPUT_LEN;
481   }
482
483   cheat_input[cheat_input_len++] = letter;
484   cheat_input[cheat_input_len] = '\0';
485
486 #if 0
487   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
488 #endif
489
490 #if 1
491   if (is_string_suffix(cheat_input, ":insert solution tape"))
492     InsertSolutionTape();
493 #else
494   if (is_string_suffix(cheat_input, ":ist"))
495     InsertSolutionTape();
496 #endif
497
498 #ifdef DEBUG
499   else if (is_string_suffix(cheat_input, ":dump tape"))
500     DumpTape(&tape);
501   else if (is_string_suffix(cheat_input, ".q"))
502     for (i = 0; i < MAX_INVENTORY_SIZE; i++)
503       if (local_player->inventory_size < MAX_INVENTORY_SIZE)
504         local_player->inventory_element[local_player->inventory_size++] =
505           EL_DYNAMITE;
506 #endif
507 }
508
509 void HandleKey(Key key, int key_status)
510 {
511   int joy = 0;
512   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
513   static struct SetupKeyboardInfo custom_key;
514   static struct
515   {
516     Key *key_custom;
517     Key key_default;
518     byte action;
519   } key_info[] =
520   {
521     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
522     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
523     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
524     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
525     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
526     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
527   };
528
529   if (game_status == GAME_MODE_PLAYING)
530   {
531     /* only needed for single-step tape recording mode */
532     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
533     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
534     int pnr;
535
536     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
537     {
538       int i;
539       byte key_action = 0;
540
541       if (setup.input[pnr].use_joystick)
542         continue;
543
544       custom_key = setup.input[pnr].key;
545
546       for (i = 0; i < 6; i++)
547         if (key == *key_info[i].key_custom)
548           key_action |= key_info[i].action;
549
550       if (tape.single_step && clear_button_2[pnr])
551       {
552         stored_player[pnr].action &= ~KEY_BUTTON_2;
553         clear_button_2[pnr] = FALSE;
554       }
555
556       if (key_status == KEY_PRESSED)
557         stored_player[pnr].action |= key_action;
558       else
559         stored_player[pnr].action &= ~key_action;
560
561       if (tape.single_step && tape.recording && tape.pausing)
562       {
563         if (key_status == KEY_PRESSED &&
564             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
565         {
566           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
567
568           if (key_action & KEY_MOTION)
569           {
570             if (stored_player[pnr].action & KEY_BUTTON_2)
571               element_dropped[pnr] = TRUE;
572           }
573         }
574         else if (key_status == KEY_RELEASED &&
575                  (key_action & KEY_BUTTON_2))
576         {
577           if (!element_dropped[pnr])
578           {
579             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
580
581             stored_player[pnr].action |= KEY_BUTTON_2;
582             clear_button_2[pnr] = TRUE;
583           }
584
585           element_dropped[pnr] = FALSE;
586         }
587       }
588       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
589         TapeTogglePause(TAPE_TOGGLE_MANUAL);
590     }
591   }
592   else
593   {
594     int i;
595
596     for (i = 0; i < 6; i++)
597       if (key == key_info[i].key_default)
598         joy |= key_info[i].action;
599   }
600
601   if (joy)
602   {
603     if (key_status == KEY_PRESSED)
604       key_joystick_mapping |= joy;
605     else
606       key_joystick_mapping &= ~joy;
607
608     HandleJoystick();
609   }
610
611   if (game_status != GAME_MODE_PLAYING)
612     key_joystick_mapping = 0;
613
614   if (key_status == KEY_RELEASED)
615     return;
616
617   if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
618       game_status == GAME_MODE_PLAYING && AllPlayersGone)
619   {
620     CloseDoor(DOOR_CLOSE_1);
621     game_status = GAME_MODE_MAIN;
622     DrawMainMenu();
623     return;
624   }
625
626   /* special key shortcuts */
627   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
628   {
629     if (key == setup.shortcut.save_game)
630       TapeQuickSave();
631     else if (key == setup.shortcut.load_game)
632       TapeQuickLoad();
633     else if (key == setup.shortcut.toggle_pause)
634       TapeTogglePause(TAPE_TOGGLE_MANUAL);
635
636     HandleKeysCheating(key);
637   }
638
639   if (HandleGadgetsKeyInput(key))
640   {
641     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
642       key = KSYM_UNDEFINED;
643   }
644
645   switch(game_status)
646   {
647     case GAME_MODE_PSEUDO_TYPENAME:
648       HandleTypeName(0, key);
649       break;
650
651     case GAME_MODE_MAIN:
652     case GAME_MODE_LEVELS:
653     case GAME_MODE_SETUP:
654     case GAME_MODE_INFO:
655       switch(key)
656       {
657         case KSYM_Return:
658           if (game_status == GAME_MODE_MAIN)
659             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
660           else if (game_status == GAME_MODE_LEVELS)
661             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
662           else if (game_status == GAME_MODE_SETUP)
663             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
664           else if (game_status == GAME_MODE_INFO)
665             HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
666           break;
667
668         case KSYM_Escape:
669           if (game_status == GAME_MODE_LEVELS)
670             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
671           else if (game_status == GAME_MODE_SETUP)
672             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
673           else if (game_status == GAME_MODE_INFO)
674             HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
675           break;
676
677         case KSYM_Page_Up:
678           if (game_status == GAME_MODE_LEVELS)
679             HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
680           else if (game_status == GAME_MODE_SETUP)
681             HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
682           else if (game_status == GAME_MODE_INFO)
683             HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
684           break;
685
686         case KSYM_Page_Down:
687           if (game_status == GAME_MODE_LEVELS)
688             HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
689           else if (game_status == GAME_MODE_SETUP)
690             HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
691           else if (game_status == GAME_MODE_INFO)
692             HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
693           break;
694
695         default:
696           break;
697       }
698       break;
699
700     case GAME_MODE_SCORES:
701       switch(key)
702       {
703         case KSYM_Return:
704         case KSYM_Escape:
705           game_status = GAME_MODE_MAIN;
706           DrawMainMenu();
707           break;
708
709         case KSYM_Page_Up:
710           HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
711           break;
712
713         case KSYM_Page_Down:
714           HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
715           break;
716
717         default:
718           break;
719       }
720       break;
721
722     case GAME_MODE_EDITOR:
723       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
724         HandleLevelEditorKeyInput(key);
725       break;
726
727     case GAME_MODE_PLAYING:
728     {
729       switch(key)
730       {
731         case KSYM_Escape:
732           RequestQuitGame(setup.ask_on_escape);
733           break;
734
735 #ifdef DEBUG
736         case KSYM_0:
737         case KSYM_1:
738         case KSYM_2:
739         case KSYM_3:
740         case KSYM_4:
741         case KSYM_5:
742         case KSYM_6:
743         case KSYM_7:
744         case KSYM_8:
745         case KSYM_9:
746           if (key == KSYM_0)
747           {
748             if (GameFrameDelay == 500)
749               GameFrameDelay = GAME_FRAME_DELAY;
750             else
751               GameFrameDelay = 500;
752           }
753           else
754             GameFrameDelay = (key - KSYM_0) * 10;
755           printf("Game speed == %d%% (%d ms delay between two frames)\n",
756                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
757           break;
758
759         case KSYM_d:
760           if (options.debug)
761           {
762             options.debug = FALSE;
763             printf("debug mode disabled\n");
764           }
765           else
766           {
767             options.debug = TRUE;
768             printf("debug mode enabled\n");
769           }
770           break;
771
772         case KSYM_S:
773           if (!global.fps_slowdown)
774           {
775             global.fps_slowdown = TRUE;
776             global.fps_slowdown_factor = 2;
777             printf("fps slowdown enabled -- display only every 2nd frame\n");
778           }
779           else if (global.fps_slowdown_factor == 2)
780           {
781             global.fps_slowdown_factor = 4;
782             printf("fps slowdown enabled -- display only every 4th frame\n");
783           }
784           else
785           {
786             global.fps_slowdown = FALSE;
787             global.fps_slowdown_factor = 1;
788             printf("fps slowdown disabled\n");
789           }
790           break;
791
792 #if 0
793         case KSYM_a:
794           if (ScrollStepSize == TILEX/8)
795             ScrollStepSize = TILEX/4;
796           else
797             ScrollStepSize = TILEX/8;
798           printf("ScrollStepSize == %d\n", ScrollStepSize);
799           break;
800 #endif
801
802 #if 0
803         case KSYM_m:
804           if (MoveSpeed == 8)
805           {
806             MoveSpeed = 4;
807             ScrollStepSize = TILEX/4;
808           }
809           else
810           {
811             MoveSpeed = 8;
812             ScrollStepSize = TILEX/8;
813           }
814           printf("MoveSpeed == %d\n", MoveSpeed);
815           break;
816 #endif
817
818         case KSYM_f:
819           ScrollStepSize = TILEX/8;
820           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
821           break;
822
823         case KSYM_g:
824           ScrollStepSize = TILEX/4;
825           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
826           break;
827
828         case KSYM_h:
829           ScrollStepSize = TILEX/2;
830           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
831           break;
832
833         case KSYM_l:
834           ScrollStepSize = TILEX;
835           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
836           break;
837
838 #if 0
839
840         case KSYM_z:
841           {
842             int i;
843
844             for (i = 0; i < MAX_PLAYERS; i++)
845             {
846               printf("Player %d:\n", i);
847               printf("  jx == %d, jy == %d\n",
848                      stored_player[i].jx, stored_player[i].jy);
849               printf("  last_jx == %d, last_jy == %d\n",
850                      stored_player[i].last_jx, stored_player[i].last_jy);
851             }
852             printf("\n");
853           }
854
855           break;
856 #endif
857 #endif
858
859         default:
860           break;
861       }
862       break;
863     }
864
865     default:
866       if (key == KSYM_Escape)
867       {
868         game_status = GAME_MODE_MAIN;
869         DrawMainMenu();
870
871         return;
872       }
873   }
874 }
875
876 void HandleNoEvent()
877 {
878   if (button_status && game_status != GAME_MODE_PLAYING)
879   {
880     HandleButton(0, 0, -button_status);
881     return;
882   }
883
884 #if defined(PLATFORM_UNIX)
885   if (options.network)
886     HandleNetworking();
887 #endif
888
889   HandleJoystick();
890 }
891
892 static int HandleJoystickForAllPlayers()
893 {
894   int i;
895   int result = 0;
896
897   for (i = 0; i < MAX_PLAYERS; i++)
898   {
899     byte joy_action = 0;
900
901     /*
902     if (!setup.input[i].use_joystick)
903       continue;
904       */
905
906     joy_action = Joystick(i);
907     result |= joy_action;
908
909     if (!setup.input[i].use_joystick)
910       continue;
911
912     stored_player[i].action = joy_action;
913   }
914
915   return result;
916 }
917
918 void HandleJoystick()
919 {
920   int joystick  = HandleJoystickForAllPlayers();
921   int keyboard  = key_joystick_mapping;
922   int joy       = (joystick | keyboard);
923   int left      = joy & JOY_LEFT;
924   int right     = joy & JOY_RIGHT;
925   int up        = joy & JOY_UP;
926   int down      = joy & JOY_DOWN;
927   int button    = joy & JOY_BUTTON;
928   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
929   int dx        = (left ? -1    : right ? 1     : 0);
930   int dy        = (up   ? -1    : down  ? 1     : 0);
931
932   switch(game_status)
933   {
934     case GAME_MODE_MAIN:
935     case GAME_MODE_LEVELS:
936     case GAME_MODE_SETUP:
937     case GAME_MODE_INFO:
938     {
939       static unsigned long joystickmove_delay = 0;
940
941       if (joystick && !button &&
942           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
943         newbutton = dx = dy = 0;
944
945       if (game_status == GAME_MODE_MAIN)
946         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
947       else if (game_status == GAME_MODE_LEVELS)
948         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
949       else if (game_status == GAME_MODE_SETUP)
950         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
951       else if (game_status == GAME_MODE_INFO)
952         HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
953       break;
954     }
955
956     case GAME_MODE_SCORES:
957       HandleHallOfFame(0,0, dx,dy, !newbutton);
958       break;
959
960     case GAME_MODE_EDITOR:
961       HandleLevelEditorIdle();
962       break;
963
964     case GAME_MODE_PLAYING:
965       if (tape.playing || keyboard)
966         newbutton = ((joy & JOY_BUTTON) != 0);
967
968       if (AllPlayersGone && newbutton)
969       {
970         CloseDoor(DOOR_CLOSE_1);
971         game_status = GAME_MODE_MAIN;
972         DrawMainMenu();
973         return;
974       }
975
976       break;
977
978     default:
979       break;
980   }
981 }